Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
Max Brunsfeld
9ec697390d Move Participant and ParticipantLocation down into client crate 2024-01-05 17:09:18 -08:00
Max Brunsfeld
be56f775d4 Move live_kit re-exports to main call module 2024-01-05 17:01:40 -08:00
Max Brunsfeld
a0049f8d16 Move audio + video tracks off of participant struct
This way, that struct can be used in the project crate.
2024-01-05 16:57:52 -08:00
10 changed files with 118 additions and 118 deletions

View file

@ -1,5 +1,4 @@
pub mod call_settings;
pub mod participant;
pub mod room;
use anyhow::{anyhow, Result};
@ -18,7 +17,7 @@ use room::Event;
use settings::Settings;
use std::sync::Arc;
pub use participant::ParticipantLocation;
pub use live_kit_client::{Frame, RemoteAudioTrack, RemoteVideoTrack};
pub use room::Room;
pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {

View file

@ -1,54 +0,0 @@
use anyhow::{anyhow, Result};
use client::ParticipantIndex;
use client::{proto, User};
use collections::HashMap;
use gpui::WeakModel;
pub use live_kit_client::Frame;
pub use live_kit_client::{RemoteAudioTrack, RemoteVideoTrack};
use project::Project;
use std::sync::Arc;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ParticipantLocation {
SharedProject { project_id: u64 },
UnsharedProject,
External,
}
impl ParticipantLocation {
pub fn from_proto(location: Option<proto::ParticipantLocation>) -> Result<Self> {
match location.and_then(|l| l.variant) {
Some(proto::participant_location::Variant::SharedProject(project)) => {
Ok(Self::SharedProject {
project_id: project.id,
})
}
Some(proto::participant_location::Variant::UnsharedProject(_)) => {
Ok(Self::UnsharedProject)
}
Some(proto::participant_location::Variant::External(_)) => Ok(Self::External),
None => Err(anyhow!("participant location was not provided")),
}
}
}
#[derive(Clone, Default)]
pub struct LocalParticipant {
pub projects: Vec<proto::ParticipantProject>,
pub active_project: Option<WeakModel<Project>>,
pub role: proto::ChannelRole,
}
#[derive(Clone, Debug)]
pub struct RemoteParticipant {
pub user: Arc<User>,
pub peer_id: proto::PeerId,
pub role: proto::ChannelRole,
pub projects: Vec<proto::ParticipantProject>,
pub location: ParticipantLocation,
pub participant_index: ParticipantIndex,
pub muted: bool,
pub speaking: bool,
pub video_tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>,
pub audio_tracks: HashMap<live_kit_client::Sid, Arc<RemoteAudioTrack>>,
}

View file

@ -1,12 +1,9 @@
use crate::{
call_settings::CallSettings,
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant},
};
use crate::call_settings::CallSettings;
use anyhow::{anyhow, Result};
use audio::{Audio, Sound};
use client::{
proto::{self, PeerId},
Client, ParticipantIndex, TypedEnvelope, User, UserStore,
Client, Participant, ParticipantIndex, ParticipantLocation, TypedEnvelope, User, UserStore,
};
use collections::{BTreeMap, HashMap, HashSet};
use fs::Fs;
@ -16,8 +13,8 @@ use gpui::{
};
use language::LanguageRegistry;
use live_kit_client::{
LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteAudioTrackUpdate,
RemoteVideoTrackUpdate,
LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteAudioTrack,
RemoteAudioTrackUpdate, RemoteVideoTrack, RemoteVideoTrackUpdate,
};
use postage::{sink::Sink, stream::Stream, watch};
use project::Project;
@ -63,7 +60,9 @@ pub struct Room {
shared_projects: HashSet<WeakModel<Project>>,
joined_projects: HashSet<WeakModel<Project>>,
local_participant: LocalParticipant,
remote_participants: BTreeMap<u64, RemoteParticipant>,
remote_participants: BTreeMap<u64, Participant>,
remote_audio_tracks: BTreeMap<u64, HashMap<String, Arc<RemoteAudioTrack>>>,
remote_video_tracks: BTreeMap<u64, HashMap<String, Arc<RemoteVideoTrack>>>,
pending_participants: Vec<Arc<User>>,
participant_user_ids: HashSet<u64>,
pending_call_count: usize,
@ -79,6 +78,13 @@ pub struct Room {
maintain_connection: Option<Task<Option<()>>>,
}
#[derive(Clone, Default)]
pub struct LocalParticipant {
pub projects: Vec<proto::ParticipantProject>,
pub active_project: Option<WeakModel<Project>>,
pub role: proto::ChannelRole,
}
impl EventEmitter<Event> for Room {}
impl Room {
@ -227,6 +233,8 @@ impl Room {
],
leave_when_empty: false,
pending_room_update: None,
remote_audio_tracks: Default::default(),
remote_video_tracks: Default::default(),
client,
user_store,
follows_by_leader_id_project_id: Default::default(),
@ -600,16 +608,22 @@ impl Room {
&self.local_participant
}
pub fn remote_participants(&self) -> &BTreeMap<u64, RemoteParticipant> {
pub fn remote_participants(&self) -> &BTreeMap<u64, Participant> {
&self.remote_participants
}
pub fn remote_participant_for_peer_id(&self, peer_id: PeerId) -> Option<&RemoteParticipant> {
pub fn remote_participant_for_peer_id(&self, peer_id: PeerId) -> Option<&Participant> {
self.remote_participants
.values()
.find(|p| p.peer_id == peer_id)
}
pub fn video_track_for_participant(&self, user_id: u64) -> Option<Arc<RemoteVideoTrack>> {
self.remote_video_tracks
.get(&user_id)
.and_then(|tracks| tracks.values().next().cloned())
}
pub fn role_for_user(&self, user_id: u64) -> Option<proto::ChannelRole> {
self.remote_participants
.get(&user_id)
@ -814,7 +828,7 @@ impl Room {
} else {
this.remote_participants.insert(
participant.user_id,
RemoteParticipant {
Participant {
user: user.clone(),
participant_index,
peer_id,
@ -823,8 +837,6 @@ impl Room {
role,
muted: true,
speaking: false,
video_tracks: Default::default(),
audio_tracks: Default::default(),
},
);
@ -875,6 +887,10 @@ impl Room {
false
}
});
this.remote_audio_tracks
.retain(|user_id, _| this.participant_user_ids.contains(user_id));
this.remote_video_tracks
.retain(|user_id, _| this.participant_user_ids.contains(user_id));
}
if let Some(pending_participants) = pending_participants.log_err() {
@ -953,9 +969,12 @@ impl Room {
let track_id = track.sid().to_string();
let participant = self
.remote_participants
.get_mut(&user_id)
.get(&user_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
participant.video_tracks.insert(track_id.clone(), track);
self.remote_video_tracks
.entry(user_id)
.or_default()
.insert(track_id, track);
cx.emit(Event::RemoteVideoTracksChanged {
participant_id: participant.peer_id,
});
@ -967,9 +986,11 @@ impl Room {
let user_id = publisher_id.parse()?;
let participant = self
.remote_participants
.get_mut(&user_id)
.get(&user_id)
.ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?;
participant.video_tracks.remove(&track_id);
if let Some(tracks) = self.remote_video_tracks.get_mut(&user_id) {
tracks.remove(&track_id);
}
cx.emit(Event::RemoteVideoTracksChanged {
participant_id: participant.peer_id,
});
@ -1012,11 +1033,13 @@ impl Room {
}
RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => {
let mut found = false;
for participant in &mut self.remote_participants.values_mut() {
for track in participant.audio_tracks.values() {
if track.sid() == track_id {
found = true;
break;
for (user_id, participant) in self.remote_participants.iter_mut() {
if let Some(tracks) = &self.remote_audio_tracks.get(user_id) {
for track in tracks.values() {
if track.sid() == track_id {
found = true;
break;
}
}
}
if found {
@ -1034,7 +1057,10 @@ impl Room {
.remote_participants
.get_mut(&user_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
participant.audio_tracks.insert(track_id.clone(), track);
self.remote_audio_tracks
.entry(user_id)
.or_default()
.insert(track_id, track);
participant.muted = publication.is_muted();
cx.emit(Event::RemoteAudioTracksChanged {
@ -1050,7 +1076,9 @@ impl Room {
.remote_participants
.get_mut(&user_id)
.ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?;
participant.audio_tracks.remove(&track_id);
if let Some(tracks) = self.remote_audio_tracks.get_mut(&user_id) {
tracks.remove(&track_id);
}
cx.emit(Event::RemoteAudioTracksChanged {
participant_id: participant.peer_id,
});

View file

@ -29,6 +29,25 @@ pub struct Collaborator {
pub user_id: UserId,
}
#[derive(Clone, Debug)]
pub struct Participant {
pub user: Arc<User>,
pub peer_id: proto::PeerId,
pub role: proto::ChannelRole,
pub projects: Vec<proto::ParticipantProject>,
pub location: ParticipantLocation,
pub participant_index: ParticipantIndex,
pub muted: bool,
pub speaking: bool,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ParticipantLocation {
SharedProject { project_id: u64 },
UnsharedProject,
External,
}
impl PartialOrd for User {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
@ -692,3 +711,20 @@ impl Collaborator {
})
}
}
impl ParticipantLocation {
pub fn from_proto(location: Option<proto::ParticipantLocation>) -> Result<Self> {
match location.and_then(|l| l.variant) {
Some(proto::participant_location::Variant::SharedProject(project)) => {
Ok(Self::SharedProject {
project_id: project.id,
})
}
Some(proto::participant_location::Variant::UnsharedProject(_)) => {
Ok(Self::UnsharedProject)
}
Some(proto::participant_location::Variant::External(_)) => Ok(Self::External),
None => Err(anyhow!("participant location was not provided")),
}
}
}

View file

@ -2,8 +2,8 @@ use crate::{
rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
tests::{channel_id, room_participants, RoomParticipants, TestClient, TestServer},
};
use call::{room, ActiveCall, ParticipantLocation, Room};
use client::{User, RECEIVE_TIMEOUT};
use call::{room, ActiveCall, Room};
use client::{ParticipantLocation, User, RECEIVE_TIMEOUT};
use collections::{HashMap, HashSet};
use fs::{repository::GitFileStatus, FakeFs, Fs as _, RemoveOptions};
use futures::StreamExt as _;
@ -248,12 +248,9 @@ async fn test_basic_calls(
assert_eq!(participant_id, client_a.peer_id().unwrap());
room_b.read_with(cx_b, |room, _| {
assert_eq!(
room.remote_participants()[&client_a.user_id().unwrap()]
.video_tracks
.len(),
1
);
assert!(room
.video_track_for_participant(client_a.user_id().unwrap())
.is_some());
});
} else {
panic!("unexpected event")
@ -266,12 +263,9 @@ async fn test_basic_calls(
assert_eq!(participant_id, client_a.peer_id().unwrap());
room_c.read_with(cx_c, |room, _| {
assert_eq!(
room.remote_participants()[&client_a.user_id().unwrap()]
.video_tracks
.len(),
1
);
assert!(room
.video_track_for_participant(client_a.user_id().unwrap())
.is_some());
});
} else {
panic!("unexpected event")
@ -5710,15 +5704,9 @@ async fn test_join_call_after_screen_was_shared(
);
// Ensure User B sees User A's screenshare.
room_b.read_with(cx_b, |room, _| {
assert_eq!(
room.remote_participants()
.get(&client_a.user_id().unwrap())
.unwrap()
.video_tracks
.len(),
1
);
assert!(room
.video_track_for_participant(client_a.user_id().unwrap())
.is_some());
});
}

View file

@ -466,6 +466,9 @@ impl CollabPanel {
for mat in matches {
let user_id = mat.candidate_id as u64;
let participant = &room.remote_participants()[&user_id];
let has_shared_screen = room
.video_track_for_participant(participant.user.id)
.is_some();
self.entries.push(ListEntry::CallParticipant {
user: participant.user.clone(),
peer_id: Some(participant.peer_id),
@ -477,11 +480,10 @@ impl CollabPanel {
project_id: project.id,
worktree_root_names: project.worktree_root_names.clone(),
host_user_id: participant.user.id,
is_last: projects.peek().is_none()
&& participant.video_tracks.is_empty(),
is_last: projects.peek().is_none() && !has_shared_screen,
});
}
if !participant.video_tracks.is_empty() {
if has_shared_screen {
self.entries.push(ListEntry::ParticipantScreen {
peer_id: Some(participant.peer_id),
is_last: true,

View file

@ -1,7 +1,7 @@
use crate::face_pile::FacePile;
use auto_update::AutoUpdateStatus;
use call::{ActiveCall, ParticipantLocation, Room};
use client::{proto::PeerId, Client, ParticipantIndex, User, UserStore};
use call::{ActiveCall, Room};
use client::{proto::PeerId, Client, ParticipantIndex, ParticipantLocation, User, UserStore};
use gpui::{
actions, canvas, div, point, px, rems, Action, AnyElement, AppContext, Element, Hsla,
InteractiveElement, IntoElement, Model, ParentElement, Path, Render,

View file

@ -1,6 +1,7 @@
use crate::{pane_group::element::pane_axis, AppState, FollowerState, Pane, Workspace};
use anyhow::{anyhow, Result};
use call::{ActiveCall, ParticipantLocation};
use call::ActiveCall;
use client::ParticipantLocation;
use collections::HashMap;
use gpui::{
point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View,

View file

@ -3,7 +3,7 @@ use crate::{
ItemNavHistory, WorkspaceId,
};
use anyhow::Result;
use call::participant::{Frame, RemoteVideoTrack};
use call::{Frame, RemoteVideoTrack};
use client::{proto::PeerId, User};
use futures::StreamExt;
use gpui::{

View file

@ -15,7 +15,7 @@ use anyhow::{anyhow, Context as _, Result};
use call::ActiveCall;
use client::{
proto::{self, PeerId},
Client, Status, TypedEnvelope, UserStore,
Client, ParticipantLocation, Status, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
@ -2398,9 +2398,9 @@ impl Workspace {
let project = self.project.read(cx);
let other_project_id = match remote_participant.location {
call::ParticipantLocation::External => None,
call::ParticipantLocation::UnsharedProject => None,
call::ParticipantLocation::SharedProject { project_id } => {
ParticipantLocation::External => None,
ParticipantLocation::UnsharedProject => None,
ParticipantLocation::SharedProject { project_id } => {
if Some(project_id) == project.remote_id() {
None
} else {
@ -2789,15 +2789,15 @@ impl Workspace {
let leader_in_this_app;
let leader_in_this_project;
match participant.location {
call::ParticipantLocation::SharedProject { project_id } => {
ParticipantLocation::SharedProject { project_id } => {
leader_in_this_app = true;
leader_in_this_project = Some(project_id) == self.project.read(cx).remote_id();
}
call::ParticipantLocation::UnsharedProject => {
ParticipantLocation::UnsharedProject => {
leader_in_this_app = true;
leader_in_this_project = false;
}
call::ParticipantLocation::External => {
ParticipantLocation::External => {
leader_in_this_app = false;
leader_in_this_project = false;
}
@ -2848,7 +2848,7 @@ impl Workspace {
let call = self.active_call()?;
let room = call.read(cx).room()?.read(cx);
let participant = room.remote_participant_for_peer_id(peer_id)?;
let track = participant.video_tracks.values().next()?.clone();
let track = room.video_track_for_participant(participant.user.id)?;
let user = participant.user.clone();
for item in pane.read(cx).items_of_type::<SharedScreen>() {