@@ -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();
@@ -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<AnyObject>.fromOpaque(ptr).takeRetainedValue();
+ let _ = Unmanaged<AnyObject>.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<LKRoomDelegate>.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<Room>.fromOpaque(room).takeUnretainedValue();
+ let room = Unmanaged<Room>.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<Room>.fromOpaque(room).takeUnretainedValue();
- let track = Unmanaged<LocalVideoTrack>.fromOpaque(track).takeUnretainedValue();
+ let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
+ let track = Unmanaged<LocalVideoTrack>.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()
}
@@ -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<Self> {
+ 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<Output = Result<()>> {
@@ -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<Output = Result<()>> {
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<Room>) -> 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,