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:
commit
491b3d5515
13 changed files with 157 additions and 51 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1052,6 +1052,10 @@ dependencies = [
|
||||||
"media",
|
"media",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
27
crates/call/src/call_settings.rs
Normal file
27
crates/call/src/call_settings.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue