diff --git a/crates/capture/src/main.rs b/crates/capture/src/main.rs index 967fd6689c..f89bc4bf95 100644 --- a/crates/capture/src/main.rs +++ b/crates/capture/src/main.rs @@ -1,5 +1,7 @@ mod live_kit_token; +use std::time::Duration; + use gpui::{ actions, elements::{Canvas, *}, @@ -34,25 +36,38 @@ fn main() { let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap(); let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap(); - let token = live_kit_token::create_token( - &live_kit_key, - &live_kit_secret, - "test-room", - "test-participant", - ) - .unwrap(); - - let room = Room::new(); + let background = cx.background().clone(); cx.foreground() .spawn(async move { println!("connecting..."); - room.connect(&live_kit_url, &token).await.unwrap(); + let user1_token = live_kit_token::create_token( + &live_kit_key, + &live_kit_secret, + "test-room", + "test-participant-1", + ) + .unwrap(); + let room1 = Room::new("user-1 room"); + room1.connect(&live_kit_url, &user1_token).await.unwrap(); + + let user2_token = live_kit_token::create_token( + &live_kit_key, + &live_kit_secret, + "test-room", + "test-participant-2", + ) + .unwrap(); + let room2 = Room::new("user-2 room"); + room2.connect(&live_kit_url, &user2_token).await.unwrap(); + let windows = live_kit::list_windows(); println!("connected! {:?}", windows); let window_id = windows.iter().next().unwrap().id; let track = LocalVideoTrack::screen_share_for_window(window_id); - room.publish_video_track(&track).await.unwrap(); + room1.publish_video_track(&track).await.unwrap(); + + background.timer(Duration::from_secs(120)).await; }) .detach(); diff --git a/crates/live_kit/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index a33aeeb6de..1802fd91fb 100644 --- a/crates/live_kit/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -1,40 +1,61 @@ import Foundation import LiveKit +class LKRoomDelegate: RoomDelegate { + var data: UnsafeRawPointer + var onDidSubscribeToRemoteTrack: @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> Void + + init(data: UnsafeRawPointer, onDidSubscribeToRemoteTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> Void) { + self.data = data + self.onDidSubscribeToRemoteTrack = onDidSubscribeToRemoteTrack + } + + func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) { + self.onDidSubscribeToRemoteTrack(self.data, Unmanaged.passRetained(track).toOpaque()) + } +} + @_cdecl("LKRelease") public func LKRelease(ptr: UnsafeRawPointer) { - let _ = Unmanaged.fromOpaque(ptr).takeRetainedValue(); + let _ = Unmanaged.fromOpaque(ptr).takeRetainedValue() +} + +@_cdecl("LKRoomDelegateCreate") +public func LKRoomDelegateCreate(data: UnsafeRawPointer, onDidSubscribeToRemoteTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer { + let delegate = LKRoomDelegate(data: data, onDidSubscribeToRemoteTrack: onDidSubscribeToRemoteTrack) + return Unmanaged.passRetained(delegate).toOpaque() } @_cdecl("LKRoomCreate") -public func LKRoomCreate() -> UnsafeMutableRawPointer { - Unmanaged.passRetained(Room()).toOpaque() +public func LKRoomCreate(delegate: UnsafeRawPointer) -> UnsafeMutableRawPointer { + let delegate = Unmanaged.fromOpaque(delegate).takeUnretainedValue() + return Unmanaged.passRetained(Room(delegate: delegate)).toOpaque() } @_cdecl("LKRoomConnect") public func LKRoomConnect(room: UnsafeRawPointer, url: CFString, token: CFString, callback: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, callback_data: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue(); + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() room.connect(url as String, token as String).then { _ in - callback(callback_data, UnsafeRawPointer(nil) as! CFString?); + callback(callback_data, UnsafeRawPointer(nil) as! CFString?) }.catch { error in - callback(callback_data, error.localizedDescription as CFString); - }; + callback(callback_data, error.localizedDescription as CFString) + } } @_cdecl("LKRoomPublishVideoTrack") public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, callback_data: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue(); - let track = Unmanaged.fromOpaque(track).takeUnretainedValue(); + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + let track = Unmanaged.fromOpaque(track).takeUnretainedValue() room.localParticipant?.publishVideoTrack(track: track).then { _ in - callback(callback_data, UnsafeRawPointer(nil) as! CFString?); + callback(callback_data, UnsafeRawPointer(nil) as! CFString?) }.catch { error in - callback(callback_data, error.localizedDescription as CFString); - }; + callback(callback_data, error.localizedDescription as CFString) + } } @_cdecl("LKCreateScreenShareTrackForWindow") public func LKCreateScreenShareTrackForWindow(windowId: uint32) -> UnsafeMutableRawPointer { - let track = LocalVideoTrack.createMacOSScreenShareTrack(source: .window(id: windowId)); + let track = LocalVideoTrack.createMacOSScreenShareTrack(source: .window(id: windowId)) return Unmanaged.passRetained(track).toOpaque() } diff --git a/crates/live_kit/src/live_kit.rs b/crates/live_kit/src/live_kit.rs index 58a01d7f79..0c254f99b3 100644 --- a/crates/live_kit/src/live_kit.rs +++ b/crates/live_kit/src/live_kit.rs @@ -11,34 +11,56 @@ use core_graphics::window::{ kCGWindowNumber, kCGWindowOwnerName, kCGWindowOwnerPID, CGWindowListCopyWindowInfo, }; use futures::{channel::oneshot, Future}; -use std::ffi::c_void; +use std::{ + ffi::c_void, + sync::{Arc, Weak}, +}; extern "C" { fn LKRelease(object: *const c_void); - fn LKRoomCreate() -> *const c_void; + fn LKRoomDelegateCreate( + callback_data: *const c_void, + on_did_subscribe_to_remote_track: extern "C" fn( + callback_data: *mut c_void, + remote_track: *const c_void, + ), + ) -> *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: extern "C" fn(*mut c_void, CFStringRef), callback_data: *mut c_void, ); fn LKRoomPublishVideoTrack( room: *const c_void, track: *const c_void, - callback: extern "C" fn(*mut c_void, CFStringRef) -> (), + callback: extern "C" fn(*mut c_void, CFStringRef), callback_data: *mut c_void, ); fn LKCreateScreenShareTrackForWindow(windowId: u32) -> *const c_void; } -pub struct Room(*const c_void); +pub struct Room { + debug_name: &'static str, + native_room: *const c_void, + _delegate: RoomDelegate, +} impl Room { - pub fn new() -> Self { - Self(unsafe { LKRoomCreate() }) + pub fn new(debug_name: &'static str) -> Arc { + Arc::new_cyclic(|weak_room| { + let delegate = RoomDelegate::new(weak_room.clone()); + Self { + debug_name, + native_room: unsafe { LKRoomCreate(delegate.native_delegate) }, + _delegate: delegate, + } + }) } pub fn connect(&self, url: &str, token: &str) -> impl Future> { @@ -47,7 +69,7 @@ impl Room { let (did_connect, tx, rx) = Self::build_done_callback(); unsafe { LKRoomConnect( - self.0, + self.native_room, url.as_concrete_TypeRef(), token.as_concrete_TypeRef(), did_connect, @@ -61,11 +83,15 @@ impl Room { pub fn publish_video_track(&self, track: &LocalVideoTrack) -> impl Future> { let (did_publish, tx, rx) = Self::build_done_callback(); unsafe { - LKRoomPublishVideoTrack(self.0, track.0, did_publish, tx); + LKRoomPublishVideoTrack(self.native_room, track.0, did_publish, tx); } async { rx.await.unwrap().context("error publishing video track") } } + fn did_subscribe_to_remote_track(&self, track: RemoteVideoTrack) { + println!("{}: !!!!!!!!!!!!!!!!!!", self.debug_name); + } + fn build_done_callback() -> ( extern "C" fn(*mut c_void, CFStringRef), *mut c_void, @@ -91,7 +117,46 @@ impl Room { impl Drop for Room { fn drop(&mut self) { - unsafe { LKRelease(self.0) } + unsafe { LKRelease(self.native_room) } + } +} + +struct RoomDelegate { + native_delegate: *const c_void, + weak_room: *const Room, +} + +impl RoomDelegate { + fn new(weak_room: Weak) -> Self { + let weak_room = Weak::into_raw(weak_room); + let native_delegate = unsafe { + LKRoomDelegateCreate( + weak_room as *const c_void, + Self::on_did_subscribe_to_remote_track, + ) + }; + Self { + native_delegate, + weak_room, + } + } + + extern "C" fn on_did_subscribe_to_remote_track(room: *mut c_void, track: *const c_void) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let track = unsafe { RemoteVideoTrack(track) }; + if let Some(room) = room.upgrade() { + room.did_subscribe_to_remote_track(track); + } + let _ = Weak::into_raw(room); + } +} + +impl Drop for RoomDelegate { + fn drop(&mut self) { + unsafe { + LKRelease(self.native_delegate); + let _ = Weak::from_raw(self.weak_room); + } } } @@ -109,6 +174,14 @@ impl Drop for LocalVideoTrack { } } +pub struct RemoteVideoTrack(*const c_void); + +impl Drop for RemoteVideoTrack { + fn drop(&mut self) { + unsafe { LKRelease(self.0) } + } +} + #[derive(Debug)] pub struct WindowInfo { pub id: u32,