From 499b8f5f5536e76d292e0746da37f26f02fd697d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 17 Oct 2022 18:00:54 +0200 Subject: [PATCH] WIP --- Cargo.lock | 26 +-- crates/call/Cargo.toml | 3 +- crates/call/src/call.rs | 2 + crates/call/src/participant.rs | 20 ++- crates/call/src/room.rs | 111 +++++++++++-- crates/capture/Cargo.toml | 29 ---- crates/capture/build.rs | 7 - crates/capture/src/main.rs | 150 ------------------ crates/collab/src/rpc.rs | 16 +- .../Sources/LiveKitBridge/LiveKitBridge.swift | 25 ++- crates/live_kit_client/src/live_kit_client.rs | 85 ++++++++-- crates/live_kit_server/src/api.rs | 2 +- crates/workspace/src/pane_group.rs | 32 ++-- 13 files changed, 245 insertions(+), 263 deletions(-) delete mode 100644 crates/capture/Cargo.toml delete mode 100644 crates/capture/build.rs delete mode 100644 crates/capture/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 67160b4d0e..c896fdf7ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,6 +732,7 @@ dependencies = [ "futures 0.3.24", "gpui", "live_kit_client", + "media", "postage", "project", "util", @@ -803,31 +804,6 @@ dependencies = [ "winx", ] -[[package]] -name = "capture" -version = "0.1.0" -dependencies = [ - "anyhow", - "bindgen", - "block", - "byteorder", - "bytes 1.2.1", - "cocoa", - "core-foundation", - "core-graphics", - "foreign-types", - "futures 0.3.24", - "gpui", - "live_kit_client", - "live_kit_server", - "log", - "media", - "objc", - "parking_lot 0.11.2", - "postage", - "simplelog", -] - [[package]] name = "castaway" version = "0.1.2" diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index 663b561f29..bd592e1de1 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -19,8 +19,9 @@ test-support = [ [dependencies] client = { path = "../client" } collections = { path = "../collections" } -live_kit_client = { path = "../live_kit_client" } gpui = { path = "../gpui" } +live_kit_client = { path = "../live_kit_client" } +media = { path = "../media" } project = { path = "../project" } util = { path = "../util" } diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 6b06d04375..056c6f1260 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -132,6 +132,8 @@ impl ActiveCall { Room::create(recipient_user_id, initial_project, client, user_store, cx) }) .await?; + room.update(&mut cx, |room, cx| room.share_screen(cx)) + .await?; this.update(&mut cx, |this, cx| this.set_room(Some(room), cx)); }; diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index a5be5b4af2..09174df793 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -1,6 +1,8 @@ use anyhow::{anyhow, Result}; use client::{proto, User}; -use gpui::WeakModelHandle; +use collections::HashMap; +use gpui::{Task, WeakModelHandle}; +use media::core_video::CVImageBuffer; use project::Project; use std::sync::Arc; @@ -34,9 +36,23 @@ pub struct LocalParticipant { pub active_project: Option>, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct RemoteParticipant { pub user: Arc, pub projects: Vec, pub location: ParticipantLocation, + pub tracks: HashMap, +} + +#[derive(Clone)] +pub struct RemoteVideoTrack { + pub(crate) frame: Option, + pub(crate) _live_kit_track: Arc, + pub(crate) _maintain_frame: Arc>, +} + +impl RemoteVideoTrack { + pub fn frame(&self) -> Option<&CVImageBuffer> { + self.frame.as_ref() + } } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index e10f55f13e..dd1e4598db 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1,5 +1,5 @@ use crate::{ - participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}, + participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack}, IncomingCall, }; use anyhow::{anyhow, Result}; @@ -7,7 +7,8 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use collections::{BTreeMap, HashSet}; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; -use live_kit_client::LocalVideoTrack; +use live_kit_client::{LocalVideoTrack, RemoteVideoTrackChange}; +use postage::watch; use project::Project; use std::sync::Arc; use util::ResultExt; @@ -27,7 +28,7 @@ pub enum Event { pub struct Room { id: u64, - live_kit_room: Option>, + live_kit_room: Option<(Arc, Task<()>)>, status: RoomStatus, local_participant: LocalParticipant, remote_participants: BTreeMap, @@ -75,17 +76,23 @@ impl Room { let live_kit_room = if let Some(connection_info) = live_kit_connection_info { let room = live_kit_client::Room::new(); let mut tracks = room.remote_video_tracks(); - cx.foreground() - .spawn(async move { - while let Some(track) = tracks.next().await { - dbg!("received track"); - } - }) - .detach(); + let maintain_room = cx.spawn_weak(|this, mut cx| async move { + while let Some(track_change) = tracks.next().await { + let this = if let Some(this) = this.upgrade(&cx) { + this + } else { + break; + }; + + this.update(&mut cx, |this, cx| { + this.remote_video_track_changed(track_change, cx).log_err() + }); + } + }); cx.foreground() .spawn(room.connect(&connection_info.server_url, &connection_info.token)) .detach_and_log_err(cx); - Some(room) + Some((room, maintain_room)) } else { None }; @@ -318,8 +325,20 @@ impl Room { projects: participant.projects, location: ParticipantLocation::from_proto(participant.location) .unwrap_or(ParticipantLocation::External), + tracks: Default::default(), }, ); + + if let Some((room, _)) = this.live_kit_room.as_ref() { + for track in + room.video_tracks_for_remote_participant(peer_id.0.to_string()) + { + this.remote_video_track_changed( + RemoteVideoTrackChange::Subscribed(track), + cx, + ); + } + } } this.remote_participants.retain(|_, participant| { @@ -357,6 +376,74 @@ impl Room { Ok(()) } + fn remote_video_track_changed( + &mut self, + change: RemoteVideoTrackChange, + cx: &mut ModelContext, + ) -> Result<()> { + match change { + RemoteVideoTrackChange::Subscribed(track) => { + let peer_id = PeerId(track.publisher_id().parse()?); + let track_id = track.id().to_string(); + let participant = self + .remote_participants + .get_mut(&peer_id) + .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; + let (mut tx, mut rx) = watch::channel(); + track.add_renderer(move |frame| *tx.borrow_mut() = Some(frame)); + participant.tracks.insert( + track_id.clone(), + RemoteVideoTrack { + frame: None, + _live_kit_track: track, + _maintain_frame: Arc::new(cx.spawn_weak(|this, mut cx| async move { + while let Some(frame) = rx.next().await { + let this = if let Some(this) = this.upgrade(&cx) { + this + } else { + break; + }; + + let done = this.update(&mut cx, |this, cx| { + // TODO: replace this with an emit. + cx.notify(); + if let Some(track) = + this.remote_participants.get_mut(&peer_id).and_then( + |participant| participant.tracks.get_mut(&track_id), + ) + { + track.frame = frame; + false + } else { + true + } + }); + + if done { + break; + } + } + })), + }, + ); + } + RemoteVideoTrackChange::Unsubscribed { + publisher_id, + track_id, + } => { + let peer_id = PeerId(publisher_id.parse()?); + let participant = self + .remote_participants + .get_mut(&peer_id) + .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; + participant.tracks.remove(&track_id); + } + } + + cx.notify(); + Ok(()) + } + fn check_invariants(&self) { #[cfg(any(test, feature = "test-support"))] { @@ -502,7 +589,7 @@ impl Room { return Task::ready(Err(anyhow!("room is offline"))); } - let room = if let Some(room) = self.live_kit_room.as_ref() { + let room = if let Some((room, _)) = self.live_kit_room.as_ref() { room.clone() } else { return Task::ready(Err(anyhow!("not connected to LiveKit"))); diff --git a/crates/capture/Cargo.toml b/crates/capture/Cargo.toml deleted file mode 100644 index a5ec131ff1..0000000000 --- a/crates/capture/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "capture" -version = "0.1.0" -edition = "2021" -description = "An example of screen capture" - -[dependencies] -gpui = { path = "../gpui" } -live_kit_client = { path = "../live_kit_client" } -live_kit_server = { path = "../live_kit_server" } -media = { path = "../media" } - -anyhow = "1.0.38" -block = "0.1" -bytes = "1.2" -byteorder = "1.4" -cocoa = "0.24" -core-foundation = "0.9.3" -core-graphics = "0.22.3" -foreign-types = "0.3" -futures = "0.3" -log = { version = "0.4.16", features = ["kv_unstable_serde"] } -objc = "0.2" -parking_lot = "0.11.1" -postage = { version = "0.4.1", features = ["futures-traits"] } -simplelog = "0.9" - -[build-dependencies] -bindgen = "0.59.2" diff --git a/crates/capture/build.rs b/crates/capture/build.rs deleted file mode 100644 index 41f60ff486..0000000000 --- a/crates/capture/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); - - // Register exported Objective-C selectors, protocols, etc - println!("cargo:rustc-link-arg=-Wl,-ObjC"); -} diff --git a/crates/capture/src/main.rs b/crates/capture/src/main.rs deleted file mode 100644 index 79cb0df922..0000000000 --- a/crates/capture/src/main.rs +++ /dev/null @@ -1,150 +0,0 @@ -use futures::StreamExt; -use gpui::{ - actions, - elements::{Canvas, *}, - keymap::Binding, - platform::current::Surface, - Menu, MenuItem, ViewContext, -}; -use live_kit_client::{LocalVideoTrack, Room}; -use log::LevelFilter; -use media::core_video::CVImageBuffer; -use postage::watch; -use simplelog::SimpleLogger; -use std::sync::Arc; - -actions!(capture, [Quit]); - -fn main() { - SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - - gpui::App::new(()).unwrap().run(|cx| { - cx.platform().activate(true); - cx.add_global_action(quit); - - cx.add_bindings([Binding::new("cmd-q", Quit, None)]); - cx.set_menus(vec![Menu { - name: "Zed", - items: vec![MenuItem::Action { - name: "Quit", - action: Box::new(Quit), - }], - }]); - - let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap(); - let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap(); - let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap(); - - cx.spawn(|mut cx| async move { - let user1_token = live_kit_server::token::create( - &live_kit_key, - &live_kit_secret, - Some("test-participant-1"), - live_kit_server::token::VideoGrant { - room: Some("test-room"), - room_join: Some(true), - can_publish: Some(true), - can_subscribe: Some(true), - ..Default::default() - }, - ) - .unwrap(); - let room1 = Room::new(); - room1.connect(&live_kit_url, &user1_token).await.unwrap(); - - let user2_token = live_kit_server::token::create( - &live_kit_key, - &live_kit_secret, - Some("test-participant-2"), - live_kit_server::token::VideoGrant { - room: Some("test-room"), - room_join: Some(true), - can_publish: Some(true), - can_subscribe: Some(true), - ..Default::default() - }, - ) - .unwrap(); - - let room2 = Room::new(); - room2.connect(&live_kit_url, &user2_token).await.unwrap(); - cx.add_window(Default::default(), |cx| ScreenCaptureView::new(room2, cx)); - - let display_sources = live_kit_client::display_sources().await.unwrap(); - let track = LocalVideoTrack::screen_share_for_display(display_sources.first().unwrap()); - room1.publish_video_track(&track).await.unwrap(); - }) - .detach(); - }); -} - -struct ScreenCaptureView { - image_buffer: Option, - _room: Arc, -} - -impl gpui::Entity for ScreenCaptureView { - type Event = (); -} - -impl ScreenCaptureView { - pub fn new(room: Arc, cx: &mut ViewContext) -> Self { - let mut remote_video_tracks = room.remote_video_tracks(); - cx.spawn_weak(|this, mut cx| async move { - if let Some(video_track) = remote_video_tracks.next().await { - let (mut frames_tx, mut frames_rx) = watch::channel_with(None); - video_track.add_renderer(move |frame| *frames_tx.borrow_mut() = Some(frame)); - - while let Some(frame) = frames_rx.next().await { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.image_buffer = frame; - cx.notify(); - }); - } else { - break; - } - } - } - }) - .detach(); - - Self { - image_buffer: None, - _room: room, - } - } -} - -impl gpui::View for ScreenCaptureView { - fn ui_name() -> &'static str { - "View" - } - - fn render(&mut self, _: &mut gpui::RenderContext) -> gpui::ElementBox { - let image_buffer = self.image_buffer.clone(); - let canvas = Canvas::new(move |bounds, _, cx| { - if let Some(image_buffer) = image_buffer.clone() { - cx.scene.push_surface(Surface { - bounds, - image_buffer, - }); - } - }); - - if let Some(image_buffer) = self.image_buffer.as_ref() { - canvas - .constrained() - .with_width(image_buffer.width() as f32) - .with_height(image_buffer.height() as f32) - .aligned() - .boxed() - } else { - canvas.boxed() - } - } -} - -fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) { - cx.platform().quit(); -} diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 6ba056d03a..70e47a755e 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -504,7 +504,7 @@ impl Server { if let Some(room) = removed_connection.room { self.room_updated(&room); - room_left = Some(self.room_left(&room, removed_connection.user_id)); + room_left = Some(self.room_left(&room, connection_id)); } contacts_to_update.insert(removed_connection.user_id); @@ -613,7 +613,7 @@ impl Server { .trace_err() { if let Some(token) = live_kit - .room_token_for_user(&room.live_kit_room, &user_id.to_string()) + .room_token(&room.live_kit_room, &request.sender_id.to_string()) .trace_err() { Some(proto::LiveKitConnectionInfo { @@ -658,7 +658,7 @@ impl Server { let live_kit_connection_info = if let Some(live_kit) = self.app_state.live_kit_client.as_ref() { if let Some(token) = live_kit - .room_token_for_user(&room.live_kit_room, &user_id.to_string()) + .room_token(&room.live_kit_room, &request.sender_id.to_string()) .trace_err() { Some(proto::LiveKitConnectionInfo { @@ -724,7 +724,7 @@ impl Server { } self.room_updated(&left_room.room); - room_left = self.room_left(&left_room.room, user_id); + room_left = self.room_left(&left_room.room, message.sender_id); for connection_id in left_room.canceled_call_connection_ids { self.peer @@ -883,13 +883,17 @@ impl Server { } } - fn room_left(&self, room: &proto::Room, user_id: UserId) -> impl Future> { + fn room_left( + &self, + room: &proto::Room, + connection_id: ConnectionId, + ) -> impl Future> { let client = self.app_state.live_kit_client.clone(); let room_name = room.live_kit_room.clone(); async move { if let Some(client) = client { client - .remove_participant(room_name, user_id.to_string()) + .remove_participant(room_name, connection_id.to_string()) .await?; } diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index 9a7b25114e..b39f359737 100644 --- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -4,16 +4,24 @@ import WebRTC class LKRoomDelegate: RoomDelegate { var data: UnsafeRawPointer - var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> Void + var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void + var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void - init(data: UnsafeRawPointer, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> Void) { + init(data: UnsafeRawPointer, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void) { self.data = data self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack + self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack } func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) { if track.kind == .video { - self.onDidSubscribeToRemoteVideoTrack(self.data, Unmanaged.passRetained(track).toOpaque()) + self.onDidSubscribeToRemoteVideoTrack(self.data, participant.sid as CFString, track.id as CFString, Unmanaged.passRetained(track).toOpaque()) + } + } + + func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) { + if track.kind == .video { + self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.sid as CFString, track.id as CFString) } } } @@ -53,8 +61,8 @@ public func LKRelease(ptr: UnsafeRawPointer) { } @_cdecl("LKRoomDelegateCreate") -public func LKRoomDelegateCreate(data: UnsafeRawPointer, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer { - let delegate = LKRoomDelegate(data: data, onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack) +public func LKRoomDelegateCreate(data: UnsafeRawPointer, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void) -> UnsafeMutableRawPointer { + let delegate = LKRoomDelegate(data: data, onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack, onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack) return Unmanaged.passRetained(delegate).toOpaque() } @@ -86,6 +94,13 @@ public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPoin } } +@_cdecl("LKRoomVideoTracksForRemoteParticipant") +public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + let tracks = room.remoteParticipants[participantId as Sid]?.videoTracks.compactMap { $0.track as? RemoteVideoTrack } + return tracks as CFArray? +} + @_cdecl("LKCreateScreenShareTrackForDisplay") public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { let display = Unmanaged.fromOpaque(display).takeUnretainedValue() diff --git a/crates/live_kit_client/src/live_kit_client.rs b/crates/live_kit_client/src/live_kit_client.rs index f8a94f6ae5..6bd5ce47e6 100644 --- a/crates/live_kit_client/src/live_kit_client.rs +++ b/crates/live_kit_client/src/live_kit_client.rs @@ -22,8 +22,15 @@ extern "C" { 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; @@ -62,7 +69,7 @@ extern "C" { pub struct Room { native_room: *const c_void, - remote_video_track_subscribers: Mutex>>>, + remote_video_track_subscribers: Mutex>>, _delegate: RoomDelegate, } @@ -103,7 +110,7 @@ impl Room { async { rx.await.unwrap().context("error publishing video track") } } - pub fn remote_video_tracks(&self) -> mpsc::UnboundedReceiver> { + pub fn remote_video_tracks(&self) -> mpsc::UnboundedReceiver { let (tx, rx) = mpsc::unbounded(); self.remote_video_track_subscribers.lock().push(tx); rx @@ -111,9 +118,20 @@ impl Room { 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(track.clone()).is_ok()); + self.remote_video_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteVideoTrackChange::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(RemoteVideoTrackChange::Unsubscribed { + publisher_id: publisher_id.clone(), + track_id: track_id.clone(), + }) + .is_ok() + }); } fn build_done_callback() -> ( @@ -157,6 +175,7 @@ impl RoomDelegate { LKRoomDelegateCreate( weak_room as *mut c_void, Self::on_did_subscribe_to_remote_video_track, + Self::on_did_unsubscribe_from_remote_video_track, ) }; Self { @@ -165,14 +184,39 @@ impl RoomDelegate { } } - extern "C" fn on_did_subscribe_to_remote_video_track(room: *mut c_void, track: *const c_void) { + 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 track = RemoteVideoTrack(track); + 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 { + id: track_id, + native_track: track, + 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 { @@ -198,9 +242,22 @@ impl Drop for LocalVideoTrack { } } -pub struct RemoteVideoTrack(*const c_void); +#[derive(Debug)] +pub struct RemoteVideoTrack { + id: String, + native_track: *const c_void, + publisher_id: String, +} impl RemoteVideoTrack { + pub fn id(&self) -> &str { + &self.id + } + + pub fn publisher_id(&self) -> &str { + &self.publisher_id + } + pub fn add_renderer(&self, callback: F) where F: 'static + FnMut(CVImageBuffer), @@ -226,17 +283,25 @@ impl RemoteVideoTrack { unsafe { let renderer = LKVideoRendererCreate(callback_data as *mut c_void, on_frame::, on_drop::); - LKVideoTrackAddRenderer(self.0, renderer); + LKVideoTrackAddRenderer(self.native_track, renderer); } } } impl Drop for RemoteVideoTrack { fn drop(&mut self) { - unsafe { LKRelease(self.0) } + unsafe { LKRelease(self.native_track) } } } +pub enum RemoteVideoTrackChange { + Subscribed(Arc), + Unsubscribed { + publisher_id: String, + track_id: String, + }, +} + pub struct MacOSDisplay(*const c_void); impl Drop for MacOSDisplay { diff --git a/crates/live_kit_server/src/api.rs b/crates/live_kit_server/src/api.rs index c21f586434..bd98d27069 100644 --- a/crates/live_kit_server/src/api.rs +++ b/crates/live_kit_server/src/api.rs @@ -78,7 +78,7 @@ impl Client { } } - pub fn room_token_for_user(&self, room: &str, identity: &str) -> Result { + pub fn room_token(&self, room: &str, identity: &str) -> Result { token::create( &self.key, &self.secret, diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 10fac09fff..c1c4aef096 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -201,21 +201,23 @@ impl Member { .right() .boxed(), ), - call::ParticipantLocation::External => Some( - Label::new( - format!( - "{} is viewing a window outside of Zed", - leader.user.github_login - ), - theme.workspace.external_location_message.text.clone(), - ) - .contained() - .with_style(theme.workspace.external_location_message.container) - .aligned() - .bottom() - .right() - .boxed(), - ), + call::ParticipantLocation::External => { + let frame = leader + .tracks + .values() + .next() + .and_then(|track| track.frame()) + .cloned(); + return Canvas::new(move |bounds, _, cx| { + if let Some(frame) = frame.clone() { + cx.scene.push_surface(gpui::mac::Surface { + bounds, + image_buffer: frame, + }); + } + }) + .boxed(); + } } } else { None