Detailed changes
@@ -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"
@@ -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" }
@@ -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));
};
@@ -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<WeakModelHandle<Project>>,
}
-#[derive(Clone, Debug)]
+#[derive(Clone)]
pub struct RemoteParticipant {
pub user: Arc<User>,
pub projects: Vec<proto::ParticipantProject>,
pub location: ParticipantLocation,
+ pub tracks: HashMap<String, RemoteVideoTrack>,
+}
+
+#[derive(Clone)]
+pub struct RemoteVideoTrack {
+ pub(crate) frame: Option<CVImageBuffer>,
+ pub(crate) _live_kit_track: Arc<live_kit_client::RemoteVideoTrack>,
+ pub(crate) _maintain_frame: Arc<Task<()>>,
+}
+
+impl RemoteVideoTrack {
+ pub fn frame(&self) -> Option<&CVImageBuffer> {
+ self.frame.as_ref()
+ }
}
@@ -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<Arc<live_kit_client::Room>>,
+ live_kit_room: Option<(Arc<live_kit_client::Room>, Task<()>)>,
status: RoomStatus,
local_participant: LocalParticipant,
remote_participants: BTreeMap<PeerId, RemoteParticipant>,
@@ -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<Self>,
+ ) -> 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")));
@@ -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"
@@ -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");
-}
@@ -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<CVImageBuffer>,
- _room: Arc<Room>,
-}
-
-impl gpui::Entity for ScreenCaptureView {
- type Event = ();
-}
-
-impl ScreenCaptureView {
- pub fn new(room: Arc<Room>, cx: &mut ViewContext<Self>) -> 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<Self>) -> 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();
-}
@@ -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<Output = Result<()>> {
+ fn room_left(
+ &self,
+ room: &proto::Room,
+ connection_id: ConnectionId,
+ ) -> impl Future<Output = Result<()>> {
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?;
}
@@ -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<Room>.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<MacOSDisplay>.fromOpaque(display).takeUnretainedValue()
@@ -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<Vec<mpsc::UnboundedSender<Arc<RemoteVideoTrack>>>>,
+ remote_video_track_subscribers: Mutex<Vec<mpsc::UnboundedSender<RemoteVideoTrackChange>>>,
_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<Arc<RemoteVideoTrack>> {
+ pub fn remote_video_tracks(&self) -> mpsc::UnboundedReceiver<RemoteVideoTrackChange> {
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<F>(&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::<F>, on_drop::<F>);
- 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<RemoteVideoTrack>),
+ Unsubscribed {
+ publisher_id: String,
+ track_id: String,
+ },
+}
+
pub struct MacOSDisplay(*const c_void);
impl Drop for MacOSDisplay {
@@ -78,7 +78,7 @@ impl Client {
}
}
- pub fn room_token_for_user(&self, room: &str, identity: &str) -> Result<String> {
+ pub fn room_token(&self, room: &str, identity: &str) -> Result<String> {
token::create(
&self.key,
&self.secret,
@@ -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