WIP: Start on a fake implementation of live-kit

Antonio Scandurra created

Change summary

crates/call/Cargo.toml                        |   2 
crates/live_kit_client/Cargo.toml             |   3 
crates/live_kit_client/build.rs               |  14 
crates/live_kit_client/src/live_kit_client.rs | 432 --------------------
crates/live_kit_client/src/prod.rs            | 422 ++++++++++++++++++++
crates/live_kit_client/src/test.rs            |  77 +++
6 files changed, 518 insertions(+), 432 deletions(-)

Detailed changes

crates/call/Cargo.toml 🔗

@@ -12,6 +12,7 @@ test-support = [
     "client/test-support",
     "collections/test-support",
     "gpui/test-support",
+    "live_kit_client/test-support",
     "project/test-support",
     "util/test-support"
 ]
@@ -33,5 +34,6 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
 client = { path = "../client", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
 gpui = { path = "../gpui", features = ["test-support"] }
+live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/live_kit_client/build.rs 🔗

@@ -35,14 +35,16 @@ pub struct SwiftTarget {
 const MACOS_TARGET_VERSION: &str = "10.15";
 
 fn main() {
-    let swift_target = get_swift_target();
+    if cfg!(not(any(test, feature = "test-support"))) {
+        let swift_target = get_swift_target();
 
-    build_bridge(&swift_target);
-    link_swift_stdlib(&swift_target);
-    link_webrtc_framework(&swift_target);
+        build_bridge(&swift_target);
+        link_swift_stdlib(&swift_target);
+        link_webrtc_framework(&swift_target);
 
-    // Register exported Objective-C selectors, protocols, etc when building example binaries.
-    println!("cargo:rustc-link-arg=-Wl,-ObjC");
+        // Register exported Objective-C selectors, protocols, etc when building example binaries.
+        println!("cargo:rustc-link-arg=-Wl,-ObjC");
+    }
 }
 
 fn build_bridge(swift_target: &SwiftTarget) {

crates/live_kit_client/src/live_kit_client.rs 🔗

@@ -1,428 +1,8 @@
-use anyhow::{anyhow, Context, Result};
-use core_foundation::{
-    array::{CFArray, CFArrayRef},
-    base::{CFRelease, CFRetain, TCFType},
-    string::{CFString, CFStringRef},
-};
-use futures::{
-    channel::{mpsc, oneshot},
-    Future,
-};
-use media::core_video::{CVImageBuffer, CVImageBufferRef};
-use parking_lot::Mutex;
-use std::{
-    ffi::c_void,
-    sync::{Arc, Weak},
-};
+pub mod prod;
+pub mod test;
 
-pub type Sid = String;
+#[cfg(not(any(test, feature = "test-support")))]
+pub use prod::*;
 
-extern "C" {
-    fn LKRoomDelegateCreate(
-        callback_data: *mut c_void,
-        on_did_subscribe_to_remote_video_track: extern "C" fn(
-            callback_data: *mut c_void,
-            publisher_id: CFStringRef,
-            track_id: CFStringRef,
-            remote_track: *const c_void,
-        ),
-        on_did_unsubscribe_from_remote_video_track: extern "C" fn(
-            callback_data: *mut c_void,
-            publisher_id: CFStringRef,
-            track_id: CFStringRef,
-        ),
-    ) -> *const c_void;
-
-    fn LKRoomCreate(delegate: *const c_void) -> *const c_void;
-    fn LKRoomConnect(
-        room: *const c_void,
-        url: CFStringRef,
-        token: CFStringRef,
-        callback: extern "C" fn(*mut c_void, CFStringRef),
-        callback_data: *mut c_void,
-    );
-    fn LKRoomDisconnect(room: *const c_void);
-    fn LKRoomPublishVideoTrack(
-        room: *const c_void,
-        track: *const c_void,
-        callback: extern "C" fn(*mut c_void, *mut c_void, CFStringRef),
-        callback_data: *mut c_void,
-    );
-    fn LKRoomUnpublishTrack(room: *const c_void, publication: *const c_void);
-    fn LKRoomVideoTracksForRemoteParticipant(
-        room: *const c_void,
-        participant_id: CFStringRef,
-    ) -> CFArrayRef;
-
-    fn LKVideoRendererCreate(
-        callback_data: *mut c_void,
-        on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef),
-        on_drop: extern "C" fn(callback_data: *mut c_void),
-    ) -> *const c_void;
-
-    fn LKVideoTrackAddRenderer(track: *const c_void, renderer: *const c_void);
-    fn LKRemoteVideoTrackGetSid(track: *const c_void) -> CFStringRef;
-
-    fn LKDisplaySources(
-        callback_data: *mut c_void,
-        callback: extern "C" fn(
-            callback_data: *mut c_void,
-            sources: CFArrayRef,
-            error: CFStringRef,
-        ),
-    );
-    fn LKCreateScreenShareTrackForDisplay(display: *const c_void) -> *const c_void;
-}
-
-pub struct Room {
-    native_room: *const c_void,
-    remote_video_track_subscribers: Mutex<Vec<mpsc::UnboundedSender<RemoteVideoTrackUpdate>>>,
-    _delegate: RoomDelegate,
-}
-
-impl Room {
-    pub fn new() -> Arc<Self> {
-        Arc::new_cyclic(|weak_room| {
-            let delegate = RoomDelegate::new(weak_room.clone());
-            Self {
-                native_room: unsafe { LKRoomCreate(delegate.native_delegate) },
-                remote_video_track_subscribers: Default::default(),
-                _delegate: delegate,
-            }
-        })
-    }
-
-    pub fn connect(&self, url: &str, token: &str) -> impl Future<Output = Result<()>> {
-        let url = CFString::new(url);
-        let token = CFString::new(token);
-        let (did_connect, tx, rx) = Self::build_done_callback();
-        unsafe {
-            LKRoomConnect(
-                self.native_room,
-                url.as_concrete_TypeRef(),
-                token.as_concrete_TypeRef(),
-                did_connect,
-                tx,
-            )
-        }
-
-        async { rx.await.unwrap().context("error connecting to room") }
-    }
-
-    pub fn publish_video_track(
-        &self,
-        track: &LocalVideoTrack,
-    ) -> impl Future<Output = Result<LocalTrackPublication>> {
-        let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>();
-        extern "C" fn callback(tx: *mut c_void, publication: *mut c_void, error: CFStringRef) {
-            let tx =
-                unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) };
-            if error.is_null() {
-                let _ = tx.send(Ok(LocalTrackPublication(publication)));
-            } else {
-                let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
-                let _ = tx.send(Err(anyhow!(error)));
-            }
-        }
-        unsafe {
-            LKRoomPublishVideoTrack(
-                self.native_room,
-                track.0,
-                callback,
-                Box::into_raw(Box::new(tx)) as *mut c_void,
-            );
-        }
-        async { rx.await.unwrap().context("error publishing video track") }
-    }
-
-    pub fn unpublish_track(&self, publication: LocalTrackPublication) {
-        unsafe {
-            LKRoomUnpublishTrack(self.native_room, publication.0);
-        }
-    }
-
-    pub fn remote_video_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteVideoTrack>> {
-        unsafe {
-            let tracks = LKRoomVideoTracksForRemoteParticipant(
-                self.native_room,
-                CFString::new(participant_id).as_concrete_TypeRef(),
-            );
-
-            if tracks.is_null() {
-                Vec::new()
-            } else {
-                let tracks = CFArray::wrap_under_get_rule(tracks);
-                tracks
-                    .into_iter()
-                    .map(|native_track| {
-                        let native_track = *native_track;
-                        let id =
-                            CFString::wrap_under_get_rule(LKRemoteVideoTrackGetSid(native_track))
-                                .to_string();
-                        Arc::new(RemoteVideoTrack::new(
-                            native_track,
-                            id,
-                            participant_id.into(),
-                        ))
-                    })
-                    .collect()
-            }
-        }
-    }
-
-    pub fn remote_video_track_updates(&self) -> mpsc::UnboundedReceiver<RemoteVideoTrackUpdate> {
-        let (tx, rx) = mpsc::unbounded();
-        self.remote_video_track_subscribers.lock().push(tx);
-        rx
-    }
-
-    fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) {
-        let track = Arc::new(track);
-        self.remote_video_track_subscribers.lock().retain(|tx| {
-            tx.unbounded_send(RemoteVideoTrackUpdate::Subscribed(track.clone()))
-                .is_ok()
-        });
-    }
-
-    fn did_unsubscribe_from_remote_video_track(&self, publisher_id: String, track_id: String) {
-        self.remote_video_track_subscribers.lock().retain(|tx| {
-            tx.unbounded_send(RemoteVideoTrackUpdate::Unsubscribed {
-                publisher_id: publisher_id.clone(),
-                track_id: track_id.clone(),
-            })
-            .is_ok()
-        });
-    }
-
-    fn build_done_callback() -> (
-        extern "C" fn(*mut c_void, CFStringRef),
-        *mut c_void,
-        oneshot::Receiver<Result<()>>,
-    ) {
-        let (tx, rx) = oneshot::channel();
-        extern "C" fn done_callback(tx: *mut c_void, error: CFStringRef) {
-            let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<()>>) };
-            if error.is_null() {
-                let _ = tx.send(Ok(()));
-            } else {
-                let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
-                let _ = tx.send(Err(anyhow!(error)));
-            }
-        }
-        (
-            done_callback,
-            Box::into_raw(Box::new(tx)) as *mut c_void,
-            rx,
-        )
-    }
-}
-
-impl Drop for Room {
-    fn drop(&mut self) {
-        unsafe {
-            LKRoomDisconnect(self.native_room);
-            CFRelease(self.native_room);
-        }
-    }
-}
-
-struct RoomDelegate {
-    native_delegate: *const c_void,
-    weak_room: *const Room,
-}
-
-impl RoomDelegate {
-    fn new(weak_room: Weak<Room>) -> Self {
-        let weak_room = Weak::into_raw(weak_room);
-        let native_delegate = unsafe {
-            LKRoomDelegateCreate(
-                weak_room as *mut c_void,
-                Self::on_did_subscribe_to_remote_video_track,
-                Self::on_did_unsubscribe_from_remote_video_track,
-            )
-        };
-        Self {
-            native_delegate,
-            weak_room,
-        }
-    }
-
-    extern "C" fn on_did_subscribe_to_remote_video_track(
-        room: *mut c_void,
-        publisher_id: CFStringRef,
-        track_id: CFStringRef,
-        track: *const c_void,
-    ) {
-        let room = unsafe { Weak::from_raw(room as *mut Room) };
-        let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
-        let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
-        let track = RemoteVideoTrack::new(track, track_id, publisher_id);
-        if let Some(room) = room.upgrade() {
-            room.did_subscribe_to_remote_video_track(track);
-        }
-        let _ = Weak::into_raw(room);
-    }
-
-    extern "C" fn on_did_unsubscribe_from_remote_video_track(
-        room: *mut c_void,
-        publisher_id: CFStringRef,
-        track_id: CFStringRef,
-    ) {
-        let room = unsafe { Weak::from_raw(room as *mut Room) };
-        let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
-        let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
-        if let Some(room) = room.upgrade() {
-            room.did_unsubscribe_from_remote_video_track(publisher_id, track_id);
-        }
-        let _ = Weak::into_raw(room);
-    }
-}
-
-impl Drop for RoomDelegate {
-    fn drop(&mut self) {
-        unsafe {
-            CFRelease(self.native_delegate);
-            let _ = Weak::from_raw(self.weak_room);
-        }
-    }
-}
-
-pub struct LocalVideoTrack(*const c_void);
-
-impl LocalVideoTrack {
-    pub fn screen_share_for_display(display: &MacOSDisplay) -> Self {
-        Self(unsafe { LKCreateScreenShareTrackForDisplay(display.0) })
-    }
-}
-
-impl Drop for LocalVideoTrack {
-    fn drop(&mut self) {
-        unsafe { CFRelease(self.0) }
-    }
-}
-
-pub struct LocalTrackPublication(*const c_void);
-
-impl Drop for LocalTrackPublication {
-    fn drop(&mut self) {
-        unsafe { CFRelease(self.0) }
-    }
-}
-
-#[derive(Debug)]
-pub struct RemoteVideoTrack {
-    native_track: *const c_void,
-    sid: Sid,
-    publisher_id: String,
-}
-
-impl RemoteVideoTrack {
-    fn new(native_track: *const c_void, sid: Sid, publisher_id: String) -> Self {
-        unsafe {
-            CFRetain(native_track);
-        }
-        Self {
-            native_track,
-            sid,
-            publisher_id,
-        }
-    }
-
-    pub fn sid(&self) -> &str {
-        &self.sid
-    }
-
-    pub fn publisher_id(&self) -> &str {
-        &self.publisher_id
-    }
-
-    pub fn add_renderer<F>(&self, callback: F)
-    where
-        F: 'static + FnMut(CVImageBuffer),
-    {
-        extern "C" fn on_frame<F>(callback_data: *mut c_void, frame: CVImageBufferRef)
-        where
-            F: FnMut(CVImageBuffer),
-        {
-            unsafe {
-                let buffer = CVImageBuffer::wrap_under_get_rule(frame);
-                let callback = &mut *(callback_data as *mut F);
-                callback(buffer);
-            }
-        }
-
-        extern "C" fn on_drop<F>(callback_data: *mut c_void) {
-            unsafe {
-                let _ = Box::from_raw(callback_data as *mut F);
-            }
-        }
-
-        let callback_data = Box::into_raw(Box::new(callback));
-        unsafe {
-            let renderer =
-                LKVideoRendererCreate(callback_data as *mut c_void, on_frame::<F>, on_drop::<F>);
-            LKVideoTrackAddRenderer(self.native_track, renderer);
-        }
-    }
-}
-
-impl Drop for RemoteVideoTrack {
-    fn drop(&mut self) {
-        unsafe { CFRelease(self.native_track) }
-    }
-}
-
-pub enum RemoteVideoTrackUpdate {
-    Subscribed(Arc<RemoteVideoTrack>),
-    Unsubscribed { publisher_id: Sid, track_id: Sid },
-}
-
-pub struct MacOSDisplay(*const c_void);
-
-impl MacOSDisplay {
-    fn new(ptr: *const c_void) -> Self {
-        unsafe {
-            CFRetain(ptr);
-        }
-        Self(ptr)
-    }
-}
-
-impl Drop for MacOSDisplay {
-    fn drop(&mut self) {
-        unsafe { CFRelease(self.0) }
-    }
-}
-
-pub fn display_sources() -> impl Future<Output = Result<Vec<MacOSDisplay>>> {
-    extern "C" fn callback(tx: *mut c_void, sources: CFArrayRef, error: CFStringRef) {
-        unsafe {
-            let tx = Box::from_raw(tx as *mut oneshot::Sender<Result<Vec<MacOSDisplay>>>);
-
-            if sources.is_null() {
-                let _ = tx.send(Err(anyhow!("{}", CFString::wrap_under_get_rule(error))));
-            } else {
-                let sources = CFArray::wrap_under_get_rule(sources)
-                    .into_iter()
-                    .map(|source| MacOSDisplay::new(*source))
-                    .collect();
-
-                let _ = tx.send(Ok(sources));
-            }
-        }
-    }
-
-    let (tx, rx) = oneshot::channel();
-
-    unsafe {
-        LKDisplaySources(Box::into_raw(Box::new(tx)) as *mut _, callback);
-    }
-
-    async move { rx.await.unwrap() }
-}
-
-#[cfg(test)]
-mod tests {
-    #[test]
-    fn test_client() {}
-}
+#[cfg(any(test, feature = "test-support"))]
+pub use test::*;

crates/live_kit_client/src/prod.rs 🔗

@@ -0,0 +1,422 @@
+use anyhow::{anyhow, Context, Result};
+use core_foundation::{
+    array::{CFArray, CFArrayRef},
+    base::{CFRelease, CFRetain, TCFType},
+    string::{CFString, CFStringRef},
+};
+use futures::{
+    channel::{mpsc, oneshot},
+    Future,
+};
+use media::core_video::{CVImageBuffer, CVImageBufferRef};
+use parking_lot::Mutex;
+use std::{
+    ffi::c_void,
+    sync::{Arc, Weak},
+};
+
+extern "C" {
+    fn LKRoomDelegateCreate(
+        callback_data: *mut c_void,
+        on_did_subscribe_to_remote_video_track: extern "C" fn(
+            callback_data: *mut c_void,
+            publisher_id: CFStringRef,
+            track_id: CFStringRef,
+            remote_track: *const c_void,
+        ),
+        on_did_unsubscribe_from_remote_video_track: extern "C" fn(
+            callback_data: *mut c_void,
+            publisher_id: CFStringRef,
+            track_id: CFStringRef,
+        ),
+    ) -> *const c_void;
+
+    fn LKRoomCreate(delegate: *const c_void) -> *const c_void;
+    fn LKRoomConnect(
+        room: *const c_void,
+        url: CFStringRef,
+        token: CFStringRef,
+        callback: extern "C" fn(*mut c_void, CFStringRef),
+        callback_data: *mut c_void,
+    );
+    fn LKRoomDisconnect(room: *const c_void);
+    fn LKRoomPublishVideoTrack(
+        room: *const c_void,
+        track: *const c_void,
+        callback: extern "C" fn(*mut c_void, *mut c_void, CFStringRef),
+        callback_data: *mut c_void,
+    );
+    fn LKRoomUnpublishTrack(room: *const c_void, publication: *const c_void);
+    fn LKRoomVideoTracksForRemoteParticipant(
+        room: *const c_void,
+        participant_id: CFStringRef,
+    ) -> CFArrayRef;
+
+    fn LKVideoRendererCreate(
+        callback_data: *mut c_void,
+        on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef),
+        on_drop: extern "C" fn(callback_data: *mut c_void),
+    ) -> *const c_void;
+
+    fn LKVideoTrackAddRenderer(track: *const c_void, renderer: *const c_void);
+    fn LKRemoteVideoTrackGetSid(track: *const c_void) -> CFStringRef;
+
+    fn LKDisplaySources(
+        callback_data: *mut c_void,
+        callback: extern "C" fn(
+            callback_data: *mut c_void,
+            sources: CFArrayRef,
+            error: CFStringRef,
+        ),
+    );
+    fn LKCreateScreenShareTrackForDisplay(display: *const c_void) -> *const c_void;
+}
+
+pub type Sid = String;
+
+pub struct Room {
+    native_room: *const c_void,
+    remote_video_track_subscribers: Mutex<Vec<mpsc::UnboundedSender<RemoteVideoTrackUpdate>>>,
+    _delegate: RoomDelegate,
+}
+
+impl Room {
+    pub fn new() -> Arc<Self> {
+        Arc::new_cyclic(|weak_room| {
+            let delegate = RoomDelegate::new(weak_room.clone());
+            Self {
+                native_room: unsafe { LKRoomCreate(delegate.native_delegate) },
+                remote_video_track_subscribers: Default::default(),
+                _delegate: delegate,
+            }
+        })
+    }
+
+    pub fn connect(&self, url: &str, token: &str) -> impl Future<Output = Result<()>> {
+        let url = CFString::new(url);
+        let token = CFString::new(token);
+        let (did_connect, tx, rx) = Self::build_done_callback();
+        unsafe {
+            LKRoomConnect(
+                self.native_room,
+                url.as_concrete_TypeRef(),
+                token.as_concrete_TypeRef(),
+                did_connect,
+                tx,
+            )
+        }
+
+        async { rx.await.unwrap().context("error connecting to room") }
+    }
+
+    pub fn publish_video_track(
+        &self,
+        track: &LocalVideoTrack,
+    ) -> impl Future<Output = Result<LocalTrackPublication>> {
+        let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>();
+        extern "C" fn callback(tx: *mut c_void, publication: *mut c_void, error: CFStringRef) {
+            let tx =
+                unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) };
+            if error.is_null() {
+                let _ = tx.send(Ok(LocalTrackPublication(publication)));
+            } else {
+                let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
+                let _ = tx.send(Err(anyhow!(error)));
+            }
+        }
+        unsafe {
+            LKRoomPublishVideoTrack(
+                self.native_room,
+                track.0,
+                callback,
+                Box::into_raw(Box::new(tx)) as *mut c_void,
+            );
+        }
+        async { rx.await.unwrap().context("error publishing video track") }
+    }
+
+    pub fn unpublish_track(&self, publication: LocalTrackPublication) {
+        unsafe {
+            LKRoomUnpublishTrack(self.native_room, publication.0);
+        }
+    }
+
+    pub fn remote_video_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteVideoTrack>> {
+        unsafe {
+            let tracks = LKRoomVideoTracksForRemoteParticipant(
+                self.native_room,
+                CFString::new(participant_id).as_concrete_TypeRef(),
+            );
+
+            if tracks.is_null() {
+                Vec::new()
+            } else {
+                let tracks = CFArray::wrap_under_get_rule(tracks);
+                tracks
+                    .into_iter()
+                    .map(|native_track| {
+                        let native_track = *native_track;
+                        let id =
+                            CFString::wrap_under_get_rule(LKRemoteVideoTrackGetSid(native_track))
+                                .to_string();
+                        Arc::new(RemoteVideoTrack::new(
+                            native_track,
+                            id,
+                            participant_id.into(),
+                        ))
+                    })
+                    .collect()
+            }
+        }
+    }
+
+    pub fn remote_video_track_updates(&self) -> mpsc::UnboundedReceiver<RemoteVideoTrackUpdate> {
+        let (tx, rx) = mpsc::unbounded();
+        self.remote_video_track_subscribers.lock().push(tx);
+        rx
+    }
+
+    fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) {
+        let track = Arc::new(track);
+        self.remote_video_track_subscribers.lock().retain(|tx| {
+            tx.unbounded_send(RemoteVideoTrackUpdate::Subscribed(track.clone()))
+                .is_ok()
+        });
+    }
+
+    fn did_unsubscribe_from_remote_video_track(&self, publisher_id: String, track_id: String) {
+        self.remote_video_track_subscribers.lock().retain(|tx| {
+            tx.unbounded_send(RemoteVideoTrackUpdate::Unsubscribed {
+                publisher_id: publisher_id.clone(),
+                track_id: track_id.clone(),
+            })
+            .is_ok()
+        });
+    }
+
+    fn build_done_callback() -> (
+        extern "C" fn(*mut c_void, CFStringRef),
+        *mut c_void,
+        oneshot::Receiver<Result<()>>,
+    ) {
+        let (tx, rx) = oneshot::channel();
+        extern "C" fn done_callback(tx: *mut c_void, error: CFStringRef) {
+            let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<()>>) };
+            if error.is_null() {
+                let _ = tx.send(Ok(()));
+            } else {
+                let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
+                let _ = tx.send(Err(anyhow!(error)));
+            }
+        }
+        (
+            done_callback,
+            Box::into_raw(Box::new(tx)) as *mut c_void,
+            rx,
+        )
+    }
+}
+
+impl Drop for Room {
+    fn drop(&mut self) {
+        unsafe {
+            LKRoomDisconnect(self.native_room);
+            CFRelease(self.native_room);
+        }
+    }
+}
+
+struct RoomDelegate {
+    native_delegate: *const c_void,
+    weak_room: *const Room,
+}
+
+impl RoomDelegate {
+    fn new(weak_room: Weak<Room>) -> Self {
+        let weak_room = Weak::into_raw(weak_room);
+        let native_delegate = unsafe {
+            LKRoomDelegateCreate(
+                weak_room as *mut c_void,
+                Self::on_did_subscribe_to_remote_video_track,
+                Self::on_did_unsubscribe_from_remote_video_track,
+            )
+        };
+        Self {
+            native_delegate,
+            weak_room,
+        }
+    }
+
+    extern "C" fn on_did_subscribe_to_remote_video_track(
+        room: *mut c_void,
+        publisher_id: CFStringRef,
+        track_id: CFStringRef,
+        track: *const c_void,
+    ) {
+        let room = unsafe { Weak::from_raw(room as *mut Room) };
+        let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
+        let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
+        let track = RemoteVideoTrack::new(track, track_id, publisher_id);
+        if let Some(room) = room.upgrade() {
+            room.did_subscribe_to_remote_video_track(track);
+        }
+        let _ = Weak::into_raw(room);
+    }
+
+    extern "C" fn on_did_unsubscribe_from_remote_video_track(
+        room: *mut c_void,
+        publisher_id: CFStringRef,
+        track_id: CFStringRef,
+    ) {
+        let room = unsafe { Weak::from_raw(room as *mut Room) };
+        let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
+        let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
+        if let Some(room) = room.upgrade() {
+            room.did_unsubscribe_from_remote_video_track(publisher_id, track_id);
+        }
+        let _ = Weak::into_raw(room);
+    }
+}
+
+impl Drop for RoomDelegate {
+    fn drop(&mut self) {
+        unsafe {
+            CFRelease(self.native_delegate);
+            let _ = Weak::from_raw(self.weak_room);
+        }
+    }
+}
+
+pub struct LocalVideoTrack(*const c_void);
+
+impl LocalVideoTrack {
+    pub fn screen_share_for_display(display: &MacOSDisplay) -> Self {
+        Self(unsafe { LKCreateScreenShareTrackForDisplay(display.0) })
+    }
+}
+
+impl Drop for LocalVideoTrack {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0) }
+    }
+}
+
+pub struct LocalTrackPublication(*const c_void);
+
+impl Drop for LocalTrackPublication {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0) }
+    }
+}
+
+#[derive(Debug)]
+pub struct RemoteVideoTrack {
+    native_track: *const c_void,
+    sid: Sid,
+    publisher_id: String,
+}
+
+impl RemoteVideoTrack {
+    fn new(native_track: *const c_void, sid: Sid, publisher_id: String) -> Self {
+        unsafe {
+            CFRetain(native_track);
+        }
+        Self {
+            native_track,
+            sid,
+            publisher_id,
+        }
+    }
+
+    pub fn sid(&self) -> &str {
+        &self.sid
+    }
+
+    pub fn publisher_id(&self) -> &str {
+        &self.publisher_id
+    }
+
+    pub fn add_renderer<F>(&self, callback: F)
+    where
+        F: 'static + FnMut(CVImageBuffer),
+    {
+        extern "C" fn on_frame<F>(callback_data: *mut c_void, frame: CVImageBufferRef)
+        where
+            F: FnMut(CVImageBuffer),
+        {
+            unsafe {
+                let buffer = CVImageBuffer::wrap_under_get_rule(frame);
+                let callback = &mut *(callback_data as *mut F);
+                callback(buffer);
+            }
+        }
+
+        extern "C" fn on_drop<F>(callback_data: *mut c_void) {
+            unsafe {
+                let _ = Box::from_raw(callback_data as *mut F);
+            }
+        }
+
+        let callback_data = Box::into_raw(Box::new(callback));
+        unsafe {
+            let renderer =
+                LKVideoRendererCreate(callback_data as *mut c_void, on_frame::<F>, on_drop::<F>);
+            LKVideoTrackAddRenderer(self.native_track, renderer);
+        }
+    }
+}
+
+impl Drop for RemoteVideoTrack {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.native_track) }
+    }
+}
+
+pub enum RemoteVideoTrackUpdate {
+    Subscribed(Arc<RemoteVideoTrack>),
+    Unsubscribed { publisher_id: Sid, track_id: Sid },
+}
+
+pub struct MacOSDisplay(*const c_void);
+
+impl MacOSDisplay {
+    fn new(ptr: *const c_void) -> Self {
+        unsafe {
+            CFRetain(ptr);
+        }
+        Self(ptr)
+    }
+}
+
+impl Drop for MacOSDisplay {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0) }
+    }
+}
+
+pub fn display_sources() -> impl Future<Output = Result<Vec<MacOSDisplay>>> {
+    extern "C" fn callback(tx: *mut c_void, sources: CFArrayRef, error: CFStringRef) {
+        unsafe {
+            let tx = Box::from_raw(tx as *mut oneshot::Sender<Result<Vec<MacOSDisplay>>>);
+
+            if sources.is_null() {
+                let _ = tx.send(Err(anyhow!("{}", CFString::wrap_under_get_rule(error))));
+            } else {
+                let sources = CFArray::wrap_under_get_rule(sources)
+                    .into_iter()
+                    .map(|source| MacOSDisplay::new(*source))
+                    .collect();
+
+                let _ = tx.send(Ok(sources));
+            }
+        }
+    }
+
+    let (tx, rx) = oneshot::channel();
+
+    unsafe {
+        LKDisplaySources(Box::into_raw(Box::new(tx)) as *mut _, callback);
+    }
+
+    async move { rx.await.unwrap() }
+}

crates/live_kit_client/src/test.rs 🔗

@@ -0,0 +1,77 @@
+use anyhow::Result;
+use futures::{channel::mpsc, future};
+use media::core_video::CVImageBuffer;
+use std::{future::Future, sync::Arc};
+
+pub type Sid = String;
+
+pub struct Room;
+
+impl Room {
+    pub fn new() -> Arc<Self> {
+        Arc::new(Self)
+    }
+
+    pub fn connect(&self, url: &str, token: &str) -> impl Future<Output = Result<()>> {
+        future::pending()
+    }
+
+    pub fn publish_video_track(
+        &self,
+        track: &LocalVideoTrack,
+    ) -> impl Future<Output = Result<LocalTrackPublication>> {
+        future::pending()
+    }
+
+    pub fn unpublish_track(&self, publication: LocalTrackPublication) {}
+
+    pub fn remote_video_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteVideoTrack>> {
+        Default::default()
+    }
+
+    pub fn remote_video_track_updates(&self) -> mpsc::UnboundedReceiver<RemoteVideoTrackUpdate> {
+        mpsc::unbounded().1
+    }
+}
+
+pub struct LocalTrackPublication;
+
+pub struct LocalVideoTrack;
+
+impl LocalVideoTrack {
+    pub fn screen_share_for_display(display: &MacOSDisplay) -> Self {
+        Self
+    }
+}
+
+pub struct RemoteVideoTrack {
+    sid: Sid,
+    publisher_id: Sid,
+}
+
+impl RemoteVideoTrack {
+    pub fn sid(&self) -> &str {
+        &self.sid
+    }
+
+    pub fn publisher_id(&self) -> &str {
+        &self.publisher_id
+    }
+
+    pub fn add_renderer<F>(&self, callback: F)
+    where
+        F: 'static + FnMut(CVImageBuffer),
+    {
+    }
+}
+
+pub enum RemoteVideoTrackUpdate {
+    Subscribed(Arc<RemoteVideoTrack>),
+    Unsubscribed { publisher_id: Sid, track_id: Sid },
+}
+
+pub struct MacOSDisplay;
+
+pub fn display_sources() -> impl Future<Output = Result<Vec<MacOSDisplay>>> {
+    future::pending()
+}