Mute mics by default (#2754)

This adds a setting to mute mics by default.

fixes https://github.com/zed-industries/community/issues/1769

Release notes:

- Fixed a bug with gutter spacing on files that end on a new significant
digit
- Added a setting for muting on join, and set it to true by default.
This commit is contained in:
Mikayla Maki 2023-07-19 12:42:30 -07:00 committed by GitHub
commit 491b3d5515
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 157 additions and 51 deletions

4
Cargo.lock generated
View file

@ -1052,6 +1052,10 @@ dependencies = [
"media", "media",
"postage", "postage",
"project", "project",
"schemars",
"serde",
"serde_derive",
"serde_json",
"settings", "settings",
"util", "util",
] ]

View file

@ -66,6 +66,11 @@
// 3. Draw all invisible symbols: // 3. Draw all invisible symbols:
// "all" // "all"
"show_whitespaces": "selection", "show_whitespaces": "selection",
// Settings related to calls in Zed
"calls": {
// Join calls with the microphone muted by default
"mute_on_join": true
},
// Scrollbar related settings // Scrollbar related settings
"scrollbar": { "scrollbar": {
// When to show the scrollbar in the editor. // When to show the scrollbar in the editor.

View file

@ -36,6 +36,10 @@ anyhow.workspace = true
async-broadcast = "0.4" async-broadcast = "0.4"
futures.workspace = true futures.workspace = true
postage.workspace = true postage.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
[dev-dependencies] [dev-dependencies]
client = { path = "../client", features = ["test-support"] } client = { path = "../client", features = ["test-support"] }

View file

@ -1,9 +1,11 @@
pub mod call_settings;
pub mod participant; pub mod participant;
pub mod room; pub mod room;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use call_settings::CallSettings;
use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore}; use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore};
use collections::HashSet; use collections::HashSet;
use futures::{future::Shared, FutureExt}; use futures::{future::Shared, FutureExt};
@ -19,6 +21,8 @@ pub use participant::ParticipantLocation;
pub use room::Room; pub use room::Room;
pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut AppContext) { pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut AppContext) {
settings::register::<CallSettings>(cx);
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx)); let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call); cx.set_global(active_call);
} }

View file

@ -0,0 +1,27 @@
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use settings::Setting;
#[derive(Deserialize, Debug)]
pub struct CallSettings {
pub mute_on_join: bool,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct CallSettingsContent {
pub mute_on_join: Option<bool>,
}
impl Setting for CallSettings {
const KEY: Option<&'static str> = Some("calls");
type FileContent = CallSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}

View file

@ -1,4 +1,5 @@
use crate::{ use crate::{
call_settings::CallSettings,
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack}, participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack},
IncomingCall, IncomingCall,
}; };
@ -19,7 +20,7 @@ use live_kit_client::{
}; };
use postage::stream::Stream; use postage::stream::Stream;
use project::Project; use project::Project;
use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration}; use std::{future::Future, mem, panic::Location, pin::Pin, sync::Arc, time::Duration};
use util::{post_inc, ResultExt, TryFutureExt}; use util::{post_inc, ResultExt, TryFutureExt};
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
@ -153,8 +154,10 @@ impl Room {
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
connect.await?; connect.await?;
this.update(&mut cx, |this, cx| this.share_microphone(cx)) if !cx.read(|cx| settings::get::<CallSettings>(cx).mute_on_join) {
.await?; this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
}
anyhow::Ok(()) anyhow::Ok(())
}) })
@ -656,7 +659,7 @@ impl Room {
peer_id, peer_id,
projects: participant.projects, projects: participant.projects,
location, location,
muted: false, muted: true,
speaking: false, speaking: false,
video_tracks: Default::default(), video_tracks: Default::default(),
audio_tracks: Default::default(), audio_tracks: Default::default(),
@ -670,6 +673,10 @@ impl Room {
live_kit.room.remote_video_tracks(&user.id.to_string()); live_kit.room.remote_video_tracks(&user.id.to_string());
let audio_tracks = let audio_tracks =
live_kit.room.remote_audio_tracks(&user.id.to_string()); live_kit.room.remote_audio_tracks(&user.id.to_string());
let publications = live_kit
.room
.remote_audio_track_publications(&user.id.to_string());
for track in video_tracks { for track in video_tracks {
this.remote_video_track_updated( this.remote_video_track_updated(
RemoteVideoTrackUpdate::Subscribed(track), RemoteVideoTrackUpdate::Subscribed(track),
@ -677,9 +684,15 @@ impl Room {
) )
.log_err(); .log_err();
} }
for track in audio_tracks {
for (track, publication) in
audio_tracks.iter().zip(publications.iter())
{
this.remote_audio_track_updated( this.remote_audio_track_updated(
RemoteAudioTrackUpdate::Subscribed(track), RemoteAudioTrackUpdate::Subscribed(
track.clone(),
publication.clone(),
),
cx, cx,
) )
.log_err(); .log_err();
@ -819,8 +832,8 @@ impl Room {
cx.notify(); cx.notify();
} }
RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => { RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => {
let mut found = false;
for participant in &mut self.remote_participants.values_mut() { for participant in &mut self.remote_participants.values_mut() {
let mut found = false;
for track in participant.audio_tracks.values() { for track in participant.audio_tracks.values() {
if track.sid() == track_id { if track.sid() == track_id {
found = true; found = true;
@ -832,16 +845,20 @@ impl Room {
break; break;
} }
} }
cx.notify(); cx.notify();
} }
RemoteAudioTrackUpdate::Subscribed(track) => { RemoteAudioTrackUpdate::Subscribed(track, publication) => {
let user_id = track.publisher_id().parse()?; let user_id = track.publisher_id().parse()?;
let track_id = track.sid().to_string(); let track_id = track.sid().to_string();
let participant = self let participant = self
.remote_participants .remote_participants
.get_mut(&user_id) .get_mut(&user_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
participant.audio_tracks.insert(track_id.clone(), track); participant.audio_tracks.insert(track_id.clone(), track);
participant.muted = publication.is_muted();
cx.emit(Event::RemoteAudioTracksChanged { cx.emit(Event::RemoteAudioTracksChanged {
participant_id: participant.peer_id, participant_id: participant.peer_id,
}); });
@ -1053,7 +1070,7 @@ impl Room {
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 {
LocalTrack::None => None, LocalTrack::None => Some(true),
LocalTrack::Pending { muted, .. } => Some(*muted), LocalTrack::Pending { muted, .. } => Some(*muted),
LocalTrack::Published { muted, .. } => Some(*muted), LocalTrack::Published { muted, .. } => Some(*muted),
}) })
@ -1070,7 +1087,9 @@ impl Room {
self.live_kit.as_ref().map(|live_kit| live_kit.deafened) self.live_kit.as_ref().map(|live_kit| live_kit.deafened)
} }
#[track_caller]
pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> { pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
dbg!(Location::caller());
if self.status.is_offline() { if self.status.is_offline() {
return Task::ready(Err(anyhow!("room is offline"))); return Task::ready(Err(anyhow!("room is offline")));
} else if self.is_sharing_mic() { } else if self.is_sharing_mic() {
@ -1244,6 +1263,10 @@ 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(); 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() {
if matches!(live_kit.microphone_track, LocalTrack::None) {
return Ok(self.share_microphone(cx));
}
let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?; let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
live_kit.muted_by_user = should_mute; live_kit.muted_by_user = should_mute;

View file

@ -652,10 +652,10 @@ impl CollabTitlebarItem {
let is_muted = room.read(cx).is_muted(); 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";
} else { } else {
icon = "icons/radix/mic.svg"; icon = "icons/radix/mic.svg";
tooltip = "Mute microphone\nRight click for options"; tooltip = "Mute microphone";
} }
let titlebar = &theme.titlebar; let titlebar = &theme.titlebar;
@ -705,10 +705,10 @@ impl CollabTitlebarItem {
let is_deafened = room.read(cx).is_deafened().unwrap_or(false); let is_deafened = room.read(cx).is_deafened().unwrap_or(false);
if is_deafened { if is_deafened {
icon = "icons/radix/speaker-off.svg"; icon = "icons/radix/speaker-off.svg";
tooltip = "Unmute speakers\nRight click for options"; tooltip = "Unmute speakers";
} else { } else {
icon = "icons/radix/speaker-loud.svg"; icon = "icons/radix/speaker-loud.svg";
tooltip = "Mute speakers\nRight click for options"; tooltip = "Mute speakers";
} }
let titlebar = &theme.titlebar; let titlebar = &theme.titlebar;

View file

@ -18,13 +18,7 @@ use workspace::AppState;
actions!( actions!(
collab, collab,
[ [ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
ToggleScreenSharing,
ToggleMute,
ToggleDeafen,
LeaveCall,
ShareMicrophone
]
); );
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) { pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
@ -40,7 +34,6 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
cx.add_global_action(toggle_screen_sharing); cx.add_global_action(toggle_screen_sharing);
cx.add_global_action(toggle_mute); cx.add_global_action(toggle_mute);
cx.add_global_action(toggle_deafen); cx.add_global_action(toggle_deafen);
cx.add_global_action(share_microphone);
} }
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
@ -85,10 +78,3 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
.log_err(); .log_err();
} }
} }
pub fn share_microphone(_: &ShareMicrophone, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
room.update(cx, Room::share_microphone)
.detach_and_log_err(cx)
}
}

View file

@ -1311,7 +1311,7 @@ impl EditorElement {
} }
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 { fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1; let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
let style = &self.style; let style = &self.style;
cx.text_layout_cache() cx.text_layout_cache()

View file

@ -6,7 +6,7 @@ import ScreenCaptureKit
class LKRoomDelegate: RoomDelegate { class LKRoomDelegate: RoomDelegate {
var data: UnsafeRawPointer var data: UnsafeRawPointer
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, 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 onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void
var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void
@ -16,7 +16,7 @@ class LKRoomDelegate: RoomDelegate {
init( init(
data: UnsafeRawPointer, data: UnsafeRawPointer,
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, 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, onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void, onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void,
@ -43,7 +43,7 @@ class LKRoomDelegate: RoomDelegate {
if track.kind == .video { if track.kind == .video {
self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque()) self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
} else if track.kind == .audio { } else if track.kind == .audio {
self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque()) self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque(), Unmanaged.passUnretained(publication).toOpaque())
} }
} }
@ -52,12 +52,12 @@ class LKRoomDelegate: RoomDelegate {
self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted) self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted)
} }
} }
func room(_ room: Room, didUpdate speakers: [Participant]) { func room(_ room: Room, didUpdate speakers: [Participant]) {
guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return } guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return }
self.onActiveSpeakersChanged(self.data, speaker_ids) 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)
@ -104,7 +104,7 @@ class LKVideoRenderer: NSObject, VideoRenderer {
public func LKRoomDelegateCreate( public func LKRoomDelegateCreate(
data: UnsafeRawPointer, data: UnsafeRawPointer,
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, 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, onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void, onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void,
@ -180,39 +180,39 @@ public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawP
@_cdecl("LKRoomAudioTracksForRemoteParticipant") @_cdecl("LKRoomAudioTracksForRemoteParticipant")
public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue() let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants { for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String { if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray? return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray?
} }
} }
return nil; return nil;
} }
@_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant") @_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant")
public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue() let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants { for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String { if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray? return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray?
} }
} }
return nil; return nil;
} }
@_cdecl("LKRoomVideoTracksForRemoteParticipant") @_cdecl("LKRoomVideoTracksForRemoteParticipant")
public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue() let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants { for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String { if participant.identity == participantId as String {
return participant.videoTracks.compactMap { $0.track as? RemoteVideoTrack } as CFArray? return participant.videoTracks.compactMap { $0.track as? RemoteVideoTrack } as CFArray?
} }
} }
return nil; return nil;
} }
@ -222,7 +222,7 @@ public func LKLocalAudioTrackCreateTrack() -> UnsafeMutableRawPointer {
echoCancellation: true, echoCancellation: true,
noiseSuppression: true noiseSuppression: true
)) ))
return Unmanaged.passRetained(track).toOpaque() return Unmanaged.passRetained(track).toOpaque()
} }
@ -276,7 +276,7 @@ public func LKLocalTrackPublicationSetMute(
callback_data: UnsafeRawPointer callback_data: UnsafeRawPointer
) { ) {
let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue() let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue()
if muted { if muted {
publication.mute().then { publication.mute().then {
on_complete(callback_data, nil) on_complete(callback_data, nil)
@ -307,3 +307,21 @@ public func LKRemoteTrackPublicationSetEnabled(
on_complete(callback_data, error.localizedDescription as CFString) on_complete(callback_data, error.localizedDescription as CFString)
} }
} }
@_cdecl("LKRemoteTrackPublicationIsMuted")
public func LKRemoteTrackPublicationIsMuted(
publication: UnsafeRawPointer
) -> Bool {
let publication = Unmanaged<RemoteTrackPublication>.fromOpaque(publication).takeUnretainedValue()
return publication.muted
}
@_cdecl("LKRemoteTrackPublicationGetSid")
public func LKRemoteTrackPublicationGetSid(
publication: UnsafeRawPointer
) -> CFString {
let publication = Unmanaged<RemoteTrackPublication>.fromOpaque(publication).takeUnretainedValue()
return publication.sid as CFString
}

View file

@ -63,7 +63,7 @@ fn main() {
let audio_track = LocalAudioTrack::create(); let audio_track = LocalAudioTrack::create();
let audio_track_publication = room_a.publish_audio_track(&audio_track).await.unwrap(); let audio_track_publication = room_a.publish_audio_track(&audio_track).await.unwrap();
if let RemoteAudioTrackUpdate::Subscribed(track) = if let RemoteAudioTrackUpdate::Subscribed(track, _) =
audio_track_updates.next().await.unwrap() audio_track_updates.next().await.unwrap()
{ {
let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); let remote_tracks = room_b.remote_audio_tracks("test-participant-1");

View file

@ -26,6 +26,7 @@ extern "C" {
publisher_id: CFStringRef, publisher_id: CFStringRef,
track_id: CFStringRef, track_id: CFStringRef,
remote_track: *const c_void, remote_track: *const c_void,
remote_publication: *const c_void,
), ),
on_did_unsubscribe_from_remote_audio_track: extern "C" fn( on_did_unsubscribe_from_remote_audio_track: extern "C" fn(
callback_data: *mut c_void, callback_data: *mut c_void,
@ -125,6 +126,9 @@ extern "C" {
on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef),
callback_data: *mut c_void, callback_data: *mut c_void,
); );
fn LKRemoteTrackPublicationIsMuted(publication: *const c_void) -> bool;
fn LKRemoteTrackPublicationGetSid(publication: *const c_void) -> CFStringRef;
} }
pub type Sid = String; pub type Sid = String;
@ -372,11 +376,19 @@ impl Room {
rx rx
} }
fn did_subscribe_to_remote_audio_track(&self, track: RemoteAudioTrack) { fn did_subscribe_to_remote_audio_track(
&self,
track: RemoteAudioTrack,
publication: RemoteTrackPublication,
) {
let track = Arc::new(track); let track = Arc::new(track);
let publication = Arc::new(publication);
self.remote_audio_track_subscribers.lock().retain(|tx| { self.remote_audio_track_subscribers.lock().retain(|tx| {
tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(track.clone())) tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(
.is_ok() track.clone(),
publication.clone(),
))
.is_ok()
}); });
} }
@ -501,13 +513,15 @@ impl RoomDelegate {
publisher_id: CFStringRef, publisher_id: CFStringRef,
track_id: CFStringRef, track_id: CFStringRef,
track: *const c_void, track: *const c_void,
publication: *const c_void,
) { ) {
let room = unsafe { Weak::from_raw(room as *mut Room) }; let room = unsafe { Weak::from_raw(room as *mut Room) };
let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; 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_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
let track = RemoteAudioTrack::new(track, track_id, publisher_id); let track = RemoteAudioTrack::new(track, track_id, publisher_id);
let publication = RemoteTrackPublication::new(publication);
if let Some(room) = room.upgrade() { if let Some(room) = room.upgrade() {
room.did_subscribe_to_remote_audio_track(track); room.did_subscribe_to_remote_audio_track(track, publication);
} }
let _ = Weak::into_raw(room); let _ = Weak::into_raw(room);
} }
@ -682,6 +696,14 @@ impl RemoteTrackPublication {
Self(native_track_publication) Self(native_track_publication)
} }
pub fn sid(&self) -> String {
unsafe { CFString::wrap_under_get_rule(LKRemoteTrackPublicationGetSid(self.0)).to_string() }
}
pub fn is_muted(&self) -> bool {
unsafe { LKRemoteTrackPublicationIsMuted(self.0) }
}
pub fn set_enabled(&self, enabled: bool) -> impl Future<Output = Result<()>> { pub fn set_enabled(&self, enabled: bool) -> impl Future<Output = Result<()>> {
let (tx, rx) = futures::channel::oneshot::channel(); let (tx, rx) = futures::channel::oneshot::channel();
@ -832,7 +854,7 @@ pub enum RemoteVideoTrackUpdate {
pub enum RemoteAudioTrackUpdate { pub enum RemoteAudioTrackUpdate {
ActiveSpeakersChanged { speakers: Vec<Sid> }, ActiveSpeakersChanged { speakers: Vec<Sid> },
MuteChanged { track_id: Sid, muted: bool }, MuteChanged { track_id: Sid, muted: bool },
Subscribed(Arc<RemoteAudioTrack>), Subscribed(Arc<RemoteAudioTrack>, Arc<RemoteTrackPublication>),
Unsubscribed { publisher_id: Sid, track_id: Sid }, Unsubscribed { publisher_id: Sid, track_id: Sid },
} }

View file

@ -216,6 +216,8 @@ impl TestServer {
publisher_id: identity.clone(), publisher_id: identity.clone(),
}); });
let publication = Arc::new(RemoteTrackPublication);
room.audio_tracks.push(track.clone()); room.audio_tracks.push(track.clone());
for (id, client_room) in &room.client_rooms { for (id, client_room) in &room.client_rooms {
@ -225,7 +227,10 @@ impl TestServer {
.lock() .lock()
.audio_track_updates .audio_track_updates
.0 .0
.try_broadcast(RemoteAudioTrackUpdate::Subscribed(track.clone())) .try_broadcast(RemoteAudioTrackUpdate::Subscribed(
track.clone(),
publication.clone(),
))
.unwrap(); .unwrap();
} }
} }
@ -501,6 +506,14 @@ impl RemoteTrackPublication {
pub fn set_enabled(&self, _enabled: bool) -> impl Future<Output = Result<()>> { pub fn set_enabled(&self, _enabled: bool) -> impl Future<Output = Result<()>> {
async { Ok(()) } async { Ok(()) }
} }
pub fn is_muted(&self) -> bool {
false
}
pub fn sid(&self) -> String {
"".to_string()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -579,7 +592,7 @@ pub enum RemoteVideoTrackUpdate {
pub enum RemoteAudioTrackUpdate { pub enum RemoteAudioTrackUpdate {
ActiveSpeakersChanged { speakers: Vec<Sid> }, ActiveSpeakersChanged { speakers: Vec<Sid> },
MuteChanged { track_id: Sid, muted: bool }, MuteChanged { track_id: Sid, muted: bool },
Subscribed(Arc<RemoteAudioTrack>), Subscribed(Arc<RemoteAudioTrack>, Arc<RemoteTrackPublication>),
Unsubscribed { publisher_id: Sid, track_id: Sid }, Unsubscribed { publisher_id: Sid, track_id: Sid },
} }