Added muted and currently speaking tracking

This commit is contained in:
Mikayla Maki 2023-06-27 19:19:08 -07:00
parent 9a07696240
commit 5d02b49058
No known key found for this signature in database
8 changed files with 185 additions and 13 deletions

View file

@ -44,6 +44,7 @@ pub struct RemoteParticipant {
pub projects: Vec<proto::ParticipantProject>, pub projects: Vec<proto::ParticipantProject>,
pub location: ParticipantLocation, pub location: ParticipantLocation,
pub muted: bool, pub muted: bool,
pub speaking: bool,
pub video_tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>, pub video_tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>,
pub audio_tracks: HashMap<live_kit_client::Sid, Arc<RemoteAudioTrack>>, pub audio_tracks: HashMap<live_kit_client::Sid, Arc<RemoteAudioTrack>>,
} }

View file

@ -164,6 +164,7 @@ impl Room {
microphone_track: LocalTrack::None, microphone_track: LocalTrack::None,
next_publish_id: 0, next_publish_id: 0,
deafened: false, deafened: false,
speaking: false,
_maintain_room, _maintain_room,
_maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks],
}) })
@ -648,6 +649,7 @@ impl Room {
projects: participant.projects, projects: participant.projects,
location, location,
muted: false, muted: false,
speaking: false,
video_tracks: Default::default(), video_tracks: Default::default(),
audio_tracks: Default::default(), audio_tracks: Default::default(),
}, },
@ -782,6 +784,30 @@ impl Room {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
match change { match change {
RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } => {
let mut speaker_ids = speakers
.into_iter()
.filter_map(|speaker_sid| speaker_sid.parse().ok())
.collect::<Vec<u64>>();
speaker_ids.sort_unstable();
for (sid, participant) in &mut self.remote_participants {
if let Ok(_) = speaker_ids.binary_search(sid) {
participant.speaking = true;
} else {
participant.speaking = false;
}
}
if let Some(id) = self.client.user_id() {
if let Some(room) = &mut self.live_kit {
if let Ok(_) = speaker_ids.binary_search(&id) {
room.speaking = true;
} else {
room.speaking = false;
}
}
}
cx.notify();
}
RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => { RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => {
for participant in &mut self.remote_participants.values_mut() { for participant in &mut self.remote_participants.values_mut() {
let mut found = false; let mut found = false;
@ -796,6 +822,7 @@ impl Room {
break; break;
} }
} }
cx.notify();
} }
RemoteAudioTrackUpdate::Subscribed(track) => { RemoteAudioTrackUpdate::Subscribed(track) => {
let user_id = track.publisher_id().parse()?; let user_id = track.publisher_id().parse()?;
@ -1011,7 +1038,7 @@ impl Room {
}) })
} }
pub fn is_muted(&self) -> Option<bool> { pub fn is_muted(&self) -> bool {
self.live_kit self.live_kit
.as_ref() .as_ref()
.and_then(|live_kit| match &live_kit.microphone_track { .and_then(|live_kit| match &live_kit.microphone_track {
@ -1019,6 +1046,13 @@ impl Room {
LocalTrack::Pending { muted, .. } => Some(*muted), LocalTrack::Pending { muted, .. } => Some(*muted),
LocalTrack::Published { muted, .. } => Some(*muted), LocalTrack::Published { muted, .. } => Some(*muted),
}) })
.unwrap_or(false)
}
pub fn is_speaking(&self) -> bool {
self.live_kit
.as_ref()
.map_or(false, |live_kit| live_kit.speaking)
} }
pub fn is_deafened(&self) -> Option<bool> { pub fn is_deafened(&self) -> Option<bool> {
@ -1215,7 +1249,7 @@ impl Room {
} }
} }
pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> { pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
let should_mute = self.is_muted().unwrap_or(false); let should_mute = self.is_muted();
if let Some(live_kit) = self.live_kit.as_mut() { if let Some(live_kit) = self.live_kit.as_mut() {
Self::set_mute(live_kit, !should_mute, cx) Self::set_mute(live_kit, !should_mute, cx)
} else { } else {
@ -1298,6 +1332,7 @@ struct LiveKitRoom {
screen_track: LocalTrack, screen_track: LocalTrack,
microphone_track: LocalTrack, microphone_track: LocalTrack,
deafened: bool, deafened: bool,
speaking: bool,
next_publish_id: usize, next_publish_id: usize,
_maintain_room: Task<()>, _maintain_room: Task<()>,
_maintain_tracks: [Task<()>; 2], _maintain_tracks: [Task<()>; 2],

View file

@ -86,8 +86,10 @@ impl View for CollabTitlebarItem {
right_container right_container
.add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx)); .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
right_container.add_child(self.render_leave_call(&theme, cx)); right_container.add_child(self.render_leave_call(&theme, cx));
let muted = room.read(cx).is_muted();
let speaking = room.read(cx).is_speaking();
left_container left_container
.add_child(self.render_current_user(&workspace, &theme, &user, peer_id, cx)); .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, muted, speaking, cx));
left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx));
right_container.add_child(self.render_toggle_mute(&theme, &room, cx)); right_container.add_child(self.render_toggle_mute(&theme, &room, cx));
right_container.add_child(self.render_toggle_deafen(&theme, &room, cx)); right_container.add_child(self.render_toggle_deafen(&theme, &room, cx));
@ -449,7 +451,7 @@ impl CollabTitlebarItem {
) -> AnyElement<Self> { ) -> AnyElement<Self> {
let icon; let icon;
let tooltip; let tooltip;
let is_muted = room.read(cx).is_muted().unwrap_or(false); let is_muted = room.read(cx).is_muted();
if is_muted { if is_muted {
icon = "icons/radix/mic-mute.svg"; icon = "icons/radix/mic-mute.svg";
tooltip = "Unmute microphone\nRight click for options"; tooltip = "Unmute microphone\nRight click for options";
@ -766,6 +768,8 @@ impl CollabTitlebarItem {
replica_id, replica_id,
participant.peer_id, participant.peer_id,
Some(participant.location), Some(participant.location),
participant.muted,
participant.speaking,
workspace, workspace,
theme, theme,
cx, cx,
@ -782,14 +786,19 @@ impl CollabTitlebarItem {
theme: &Theme, theme: &Theme,
user: &Arc<User>, user: &Arc<User>,
peer_id: PeerId, peer_id: PeerId,
muted: bool,
speaking: bool,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> AnyElement<Self> { ) -> AnyElement<Self> {
let replica_id = workspace.read(cx).project().read(cx).replica_id(); let replica_id = workspace.read(cx).project().read(cx).replica_id();
Container::new(self.render_face_pile( Container::new(self.render_face_pile(
user, user,
Some(replica_id), Some(replica_id),
peer_id, peer_id,
None, None,
muted,
speaking,
workspace, workspace,
theme, theme,
cx, cx,
@ -804,6 +813,8 @@ impl CollabTitlebarItem {
replica_id: Option<ReplicaId>, replica_id: Option<ReplicaId>,
peer_id: PeerId, peer_id: PeerId,
location: Option<ParticipantLocation>, location: Option<ParticipantLocation>,
muted: bool,
speaking: bool,
workspace: &ViewHandle<Workspace>, workspace: &ViewHandle<Workspace>,
theme: &Theme, theme: &Theme,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
@ -829,11 +840,17 @@ impl CollabTitlebarItem {
let leader_style = theme.titlebar.leader_avatar; let leader_style = theme.titlebar.leader_avatar;
let follower_style = theme.titlebar.follower_avatar; let follower_style = theme.titlebar.follower_avatar;
let mut background_color = theme let mut background_color = if muted {
gpui::color::Color::red()
} else if speaking {
gpui::color::Color::green()
} else {
theme
.titlebar .titlebar
.container .container
.background_color .background_color
.unwrap_or_default(); .unwrap_or_default()
};
if let Some(replica_id) = replica_id { if let Some(replica_id) = replica_id {
if followed_by_self { if followed_by_self {
let selection = theme.editor.replica_selection_style(replica_id).selection; let selection = theme.editor.replica_selection_style(replica_id).selection;

View file

@ -8,6 +8,8 @@ class LKRoomDelegate: RoomDelegate {
var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void
var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void
var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
var onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void
var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void
var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void
var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
@ -16,6 +18,8 @@ class LKRoomDelegate: RoomDelegate {
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void,
onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void) onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void)
{ {
@ -25,6 +29,8 @@ class LKRoomDelegate: RoomDelegate {
self.onDidUnsubscribeFromRemoteAudioTrack = onDidUnsubscribeFromRemoteAudioTrack self.onDidUnsubscribeFromRemoteAudioTrack = onDidUnsubscribeFromRemoteAudioTrack
self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack
self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack
self.onMuteChangedFromRemoteAudioTrack = onMuteChangedFromRemoteAudioTrack
self.onActiveSpeakersChanged = onActiveSpeakersChanged
} }
func room(_ room: Room, didUpdate connectionState: ConnectionState, oldValue: ConnectionState) { func room(_ room: Room, didUpdate connectionState: ConnectionState, oldValue: ConnectionState) {
@ -41,6 +47,17 @@ class LKRoomDelegate: RoomDelegate {
} }
} }
func room(_ room: Room, participant: Participant, didUpdate publication: TrackPublication, muted: Bool) {
if publication.kind == .audio {
self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted)
}
}
func room(_ room: Room, didUpdate speakers: [Participant]) {
guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return }
self.onActiveSpeakersChanged(self.data, speaker_ids)
}
func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) { func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) {
if track.kind == .video { if track.kind == .video {
self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString) self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString)
@ -89,6 +106,8 @@ public func LKRoomDelegateCreate(
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void,
onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
) -> UnsafeMutableRawPointer { ) -> UnsafeMutableRawPointer {
@ -97,6 +116,8 @@ public func LKRoomDelegateCreate(
onDidDisconnect: onDidDisconnect, onDidDisconnect: onDidDisconnect,
onDidSubscribeToRemoteAudioTrack: onDidSubscribeToRemoteAudioTrack, onDidSubscribeToRemoteAudioTrack: onDidSubscribeToRemoteAudioTrack,
onDidUnsubscribeFromRemoteAudioTrack: onDidUnsubscribeFromRemoteAudioTrack, onDidUnsubscribeFromRemoteAudioTrack: onDidUnsubscribeFromRemoteAudioTrack,
onMuteChangedFromRemoteAudioTrack: onMuteChangedFromRemoteAudioTrack,
onActiveSpeakersChanged: onActiveSpeakerChanged,
onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack, onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack,
onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack
) )

View file

@ -74,19 +74,54 @@ fn main() {
panic!("unexpected message"); panic!("unexpected message");
} }
audio_track_publication.set_mute(true).await.unwrap();
println!("waiting for mute changed!");
if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } =
audio_track_updates.next().await.unwrap()
{
let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
assert_eq!(remote_tracks[0].sid(), track_id);
assert_eq!(muted, true);
} else {
panic!("unexpected message");
}
audio_track_publication.set_mute(false).await.unwrap();
if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } =
audio_track_updates.next().await.unwrap()
{
let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
assert_eq!(remote_tracks[0].sid(), track_id);
assert_eq!(muted, false);
} else {
panic!("unexpected message");
}
println!("Pausing for 5 seconds to test audio, make some noise!"); println!("Pausing for 5 seconds to test audio, make some noise!");
let timer = cx.background().timer(Duration::from_secs(5)); let timer = cx.background().timer(Duration::from_secs(5));
timer.await; timer.await;
let remote_audio_track = room_b let remote_audio_track = room_b
.remote_audio_tracks("test-participant-1") .remote_audio_tracks("test-participant-1")
.pop() .pop()
.unwrap(); .unwrap();
room_a.unpublish_track(audio_track_publication); room_a.unpublish_track(audio_track_publication);
// Clear out any active speakers changed messages
let mut next = audio_track_updates.next().await.unwrap();
while let RemoteAudioTrackUpdate::ActiveSpeakersChanged {
speakers
} = next
{
println!("Speakers changed: {:?}", speakers);
next = audio_track_updates.next().await.unwrap();
}
if let RemoteAudioTrackUpdate::Unsubscribed { if let RemoteAudioTrackUpdate::Unsubscribed {
publisher_id, publisher_id,
track_id, track_id,
} = audio_track_updates.next().await.unwrap() } = next
{ {
assert_eq!(publisher_id, "test-participant-1"); assert_eq!(publisher_id, "test-participant-1");
assert_eq!(remote_audio_track.sid(), track_id); assert_eq!(remote_audio_track.sid(), track_id);

View file

@ -32,6 +32,15 @@ extern "C" {
publisher_id: CFStringRef, publisher_id: CFStringRef,
track_id: CFStringRef, track_id: CFStringRef,
), ),
on_mute_changed_from_remote_audio_track: extern "C" fn(
callback_data: *mut c_void,
track_id: CFStringRef,
muted: bool,
),
on_active_speakers_changed: extern "C" fn(
callback_data: *mut c_void,
participants: CFArrayRef,
),
on_did_subscribe_to_remote_video_track: extern "C" fn( on_did_subscribe_to_remote_video_track: extern "C" fn(
callback_data: *mut c_void, callback_data: *mut c_void,
publisher_id: CFStringRef, publisher_id: CFStringRef,
@ -381,6 +390,24 @@ impl Room {
}); });
} }
fn mute_changed_from_remote_audio_track(&self, track_id: String, muted: bool) {
self.remote_audio_track_subscribers.lock().retain(|tx| {
tx.unbounded_send(RemoteAudioTrackUpdate::MuteChanged {
track_id: track_id.clone(),
muted,
})
.is_ok()
});
}
// A vec of publisher IDs
fn active_speakers_changed(&self, speakers: Vec<String>) {
self.remote_audio_track_subscribers.lock().retain(move |tx| {
tx.unbounded_send(RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers: speakers.clone() })
.is_ok()
});
}
fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) {
let track = Arc::new(track); let track = Arc::new(track);
self.remote_video_track_subscribers.lock().retain(|tx| { self.remote_video_track_subscribers.lock().retain(|tx| {
@ -445,6 +472,8 @@ impl RoomDelegate {
Self::on_did_disconnect, Self::on_did_disconnect,
Self::on_did_subscribe_to_remote_audio_track, Self::on_did_subscribe_to_remote_audio_track,
Self::on_did_unsubscribe_from_remote_audio_track, Self::on_did_unsubscribe_from_remote_audio_track,
Self::on_mute_change_from_remote_audio_track,
Self::on_active_speakers_changed,
Self::on_did_subscribe_to_remote_video_track, Self::on_did_subscribe_to_remote_video_track,
Self::on_did_unsubscribe_from_remote_video_track, Self::on_did_unsubscribe_from_remote_video_track,
) )
@ -493,6 +522,38 @@ impl RoomDelegate {
let _ = Weak::into_raw(room); let _ = Weak::into_raw(room);
} }
extern "C" fn on_mute_change_from_remote_audio_track(
room: *mut c_void,
track_id: CFStringRef,
muted: bool,
) {
let room = unsafe { Weak::from_raw(room as *mut Room) };
let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
if let Some(room) = room.upgrade() {
room.mute_changed_from_remote_audio_track(track_id, muted);
}
let _ = Weak::into_raw(room);
}
extern "C" fn on_active_speakers_changed(room: *mut c_void, participants: CFArrayRef) {
if participants.is_null() {
return;
}
let room = unsafe { Weak::from_raw(room as *mut Room) };
let speakers = unsafe {
CFArray::wrap_under_get_rule(participants)
.into_iter()
.map(|speaker: core_foundation::base::ItemRef<'_, *const c_void>| CFString::wrap_under_get_rule(*speaker as CFStringRef).to_string())
.collect()
};
if let Some(room) = room.upgrade() {
room.active_speakers_changed(speakers);
}
let _ = Weak::into_raw(room);
}
extern "C" fn on_did_subscribe_to_remote_video_track( extern "C" fn on_did_subscribe_to_remote_video_track(
room: *mut c_void, room: *mut c_void,
publisher_id: CFStringRef, publisher_id: CFStringRef,
@ -761,7 +822,8 @@ pub enum RemoteVideoTrackUpdate {
} }
pub enum RemoteAudioTrackUpdate { pub enum RemoteAudioTrackUpdate {
MuteChanged { track_id: Sid, muted: bool}, ActiveSpeakersChanged { speakers: Vec<Sid> },
MuteChanged { track_id: Sid, muted: bool },
Subscribed(Arc<RemoteAudioTrack>), Subscribed(Arc<RemoteAudioTrack>),
Unsubscribed { publisher_id: Sid, track_id: Sid }, Unsubscribed { publisher_id: Sid, track_id: Sid },
} }

View file

@ -580,6 +580,7 @@ pub enum RemoteVideoTrackUpdate {
#[derive(Clone)] #[derive(Clone)]
pub enum RemoteAudioTrackUpdate { pub enum RemoteAudioTrackUpdate {
ActiveSpeakersChanged { speakers: Vec<Sid> },
MuteChanged { track_id: Sid, muted: bool}, MuteChanged { track_id: Sid, muted: bool},
Subscribed(Arc<RemoteAudioTrack>), Subscribed(Arc<RemoteAudioTrack>),
Unsubscribed { publisher_id: Sid, track_id: Sid }, Unsubscribed { publisher_id: Sid, track_id: Sid },

View file

@ -54,5 +54,5 @@ sleep 0.5
# Start the two Zed child processes. Open the given paths with the first instance. # Start the two Zed child processes. Open the given paths with the first instance.
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
ZED_IMPERSONATE=${username_1} ZED_WINDOW_POSITION=${position_1} target/debug/Zed $@ & ZED_IMPERSONATE=${username_1} ZED_WINDOW_POSITION=${position_1} target/debug/Zed $@ &
ZED_IMPERSONATE=${username_2} ZED_WINDOW_POSITION=${position_2} target/debug/Zed & SECOND=true ZED_IMPERSONATE=${username_2} ZED_WINDOW_POSITION=${position_2} target/debug/Zed &
wait wait