@@ -154,8 +154,8 @@ impl Room {
Some(LiveKitRoom {
room,
- screen_track: Track::None,
- microphone_track: Track::None,
+ screen_track: LocalTrack::None,
+ microphone_track: LocalTrack::None,
next_publish_id: 0,
_maintain_room,
_maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks],
@@ -985,13 +985,13 @@ impl Room {
pub fn is_screen_sharing(&self) -> bool {
self.live_kit.as_ref().map_or(false, |live_kit| {
- !matches!(live_kit.screen_track, Track::None)
+ !matches!(live_kit.screen_track, LocalTrack::None)
})
}
pub fn is_sharing_mic(&self) -> bool {
self.live_kit.as_ref().map_or(false, |live_kit| {
- !matches!(live_kit.microphone_track, Track::None)
+ !matches!(live_kit.microphone_track, LocalTrack::None)
})
}
@@ -1004,7 +1004,10 @@ impl Room {
let publish_id = if let Some(live_kit) = self.live_kit.as_mut() {
let publish_id = post_inc(&mut live_kit.next_publish_id);
- live_kit.microphone_track = Track::Pending { publish_id };
+ live_kit.microphone_track = LocalTrack::Pending {
+ publish_id,
+ muted: false,
+ };
cx.notify();
publish_id
} else {
@@ -1034,13 +1037,14 @@ impl Room {
.as_mut()
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
- let canceled = if let Track::Pending {
+ let (canceled, muted) = if let LocalTrack::Pending {
publish_id: cur_publish_id,
+ muted
} = &live_kit.microphone_track
{
- *cur_publish_id != publish_id
+ (*cur_publish_id != publish_id, *muted)
} else {
- true
+ (true, false)
};
match publication {
@@ -1048,7 +1052,13 @@ impl Room {
if canceled {
live_kit.room.unpublish_track(publication);
} else {
- live_kit.microphone_track = Track::Published(publication);
+ if muted {
+ cx.background().spawn(publication.mute()).detach();
+ }
+ live_kit.microphone_track = LocalTrack::Published {
+ track_publication: publication,
+ muted
+ };
cx.notify();
}
Ok(())
@@ -1057,7 +1067,7 @@ impl Room {
if canceled {
Ok(())
} else {
- live_kit.microphone_track = Track::None;
+ live_kit.microphone_track = LocalTrack::None;
cx.notify();
Err(error)
}
@@ -1076,7 +1086,10 @@ impl Room {
let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() {
let publish_id = post_inc(&mut live_kit.next_publish_id);
- live_kit.screen_track = Track::Pending { publish_id };
+ live_kit.screen_track = LocalTrack::Pending {
+ publish_id,
+ muted: false,
+ };
cx.notify();
(live_kit.room.display_sources(), publish_id)
} else {
@@ -1110,13 +1123,14 @@ impl Room {
.as_mut()
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
- let canceled = if let Track::Pending {
+ let (canceled, muted) = if let LocalTrack::Pending {
publish_id: cur_publish_id,
+ muted,
} = &live_kit.screen_track
{
- *cur_publish_id != publish_id
+ (*cur_publish_id != publish_id, *muted)
} else {
- true
+ (true, false)
};
match publication {
@@ -1124,7 +1138,13 @@ impl Room {
if canceled {
live_kit.room.unpublish_track(publication);
} else {
- live_kit.screen_track = Track::Published(publication);
+ if muted {
+ cx.background().spawn(publication.mute()).detach();
+ }
+ live_kit.screen_track = LocalTrack::Published {
+ track_publication: publication,
+ muted,
+ };
cx.notify();
}
Ok(())
@@ -1133,7 +1153,7 @@ impl Room {
if canceled {
Ok(())
} else {
- live_kit.screen_track = Track::None;
+ live_kit.screen_track = LocalTrack::None;
cx.notify();
Err(error)
}
@@ -1143,13 +1163,33 @@ impl Room {
})
}
- pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
- // https://docs.livekit.io/client/publish/
- // Should be acessible from local participant / publication
- todo!();
+ pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
+ if let Some(live_kit) = self.live_kit.as_mut() {
+ match &mut live_kit.microphone_track {
+ LocalTrack::None => Err(anyhow!("microphone was not shared")),
+ LocalTrack::Pending { muted, .. } => {
+ *muted = !*muted;
+ Ok(Task::Ready(Some(Ok(()))))
+ }
+ LocalTrack::Published {
+ track_publication,
+ muted,
+ } => {
+ *muted = !*muted;
+
+ if *muted {
+ Ok(cx.background().spawn(track_publication.mute()))
+ } else {
+ Ok(cx.background().spawn(track_publication.unmute()))
+ }
+ }
+ }
+ } else {
+ Err(anyhow!("LiveKit not started"))
+ }
}
- pub fn toggle_deafen(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+ pub fn toggle_deafen(&mut self, _cx: &mut ModelContext<Self>) -> Task<Result<()>> {
// iterate through publications and mute (?????)
todo!();
}
@@ -1164,13 +1204,15 @@ impl Room {
.as_mut()
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
match mem::take(&mut live_kit.screen_track) {
- Track::None => Err(anyhow!("screen was not shared")),
- Track::Pending { .. } => {
+ LocalTrack::None => Err(anyhow!("screen was not shared")),
+ LocalTrack::Pending { .. } => {
cx.notify();
Ok(())
}
- Track::Published(track) => {
- live_kit.room.unpublish_track(track);
+ LocalTrack::Published {
+ track_publication, ..
+ } => {
+ live_kit.room.unpublish_track(track_publication);
cx.notify();
Ok(())
}
@@ -1189,20 +1231,26 @@ impl Room {
struct LiveKitRoom {
room: Arc<live_kit_client::Room>,
- screen_track: Track,
- microphone_track: Track,
+ screen_track: LocalTrack,
+ microphone_track: LocalTrack,
next_publish_id: usize,
_maintain_room: Task<()>,
_maintain_tracks: [Task<()>; 2],
}
-enum Track {
+enum LocalTrack {
None,
- Pending { publish_id: usize },
- Published(LocalTrackPublication),
+ Pending {
+ publish_id: usize,
+ muted: bool,
+ },
+ Published {
+ track_publication: LocalTrackPublication,
+ muted: bool,
+ },
}
-impl Default for Track {
+impl Default for LocalTrack {
fn default() -> Self {
Self::None
}
@@ -9,9 +9,10 @@ mod notifications;
mod project_shared_notification;
mod sharing_status_indicator;
-use call::ActiveCall;
+use call::{ActiveCall, Room};
pub use collab_titlebar_item::{CollabTitlebarItem, ToggleContactsMenu};
use gpui::{actions, AppContext, Task};
+use util::ResultExt;
use std::sync::Arc;
use workspace::AppState;
@@ -46,20 +47,12 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- let toggle_mut = room.update(cx, |room, cx| {
- if room.is_sharing_mic() {
- room.toggle_mute(cx)
- } else {
- room.share_mic(cx)
- }
- });
- toggle_mut.detach_and_log_err(cx);
+ room.update(cx, Room::toggle_mute).map(Task::detach).log_err();
}
}
pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- let toggle_deafan = room.update(cx, |room, cx| room.toggle_deafen(cx));
- toggle_deafan.detach_and_log_err(cx);
+ room.update(cx, Room::toggle_deafen).detach_and_log_err(cx);
}
}
@@ -201,19 +201,6 @@ public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer)
return Unmanaged.passRetained(track).toOpaque()
}
-@_cdecl("LKRemoteAudioTrackStart")
-public func LKRemoteAudioTrackStart(track: UnsafeRawPointer, onStart: @escaping @convention(c) (UnsafeRawPointer, Bool) -> Void, callbackData: UnsafeRawPointer) {
- let track = Unmanaged<Track>.fromOpaque(track).takeUnretainedValue() as! RemoteAudioTrack
-
- track.start().then { success in
- onStart(callbackData, success)
- }
- .catch { _ in
- onStart(callbackData, false)
- }
-}
-
-
@_cdecl("LKVideoRendererCreate")
public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer {
Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque()
@@ -247,3 +234,34 @@ public func LKDisplaySources(data: UnsafeRawPointer, callback: @escaping @conven
callback(data, nil, error.localizedDescription as CFString)
}
}
+
+@_cdecl("LKLocalTrackPublicationMute")
+public func LKLocalTrackPublicationMute(
+ publication: UnsafeRawPointer,
+ on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void,
+ callback_data: UnsafeRawPointer
+) {
+ let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue()
+
+ publication.mute().then {
+ on_complete(callback_data, nil)
+ }.catch { error in
+ on_complete(callback_data, error.localizedDescription as CFString)
+ }
+
+}
+
+@_cdecl("LKLocalTrackPublicationUnmute")
+public func LKLocalTrackPublicationUnmute(
+ publication: UnsafeRawPointer,
+ on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void,
+ callback_data: UnsafeRawPointer
+) {
+ let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue()
+
+ publication.unmute().then {
+ on_complete(callback_data, nil)
+ }.catch { error in
+ on_complete(callback_data, error.localizedDescription as CFString)
+ }
+}
@@ -84,12 +84,6 @@ extern "C" {
) -> *const c_void;
fn LKRemoteAudioTrackGetSid(track: *const c_void) -> CFStringRef;
- // fn LKRemoteAudioTrackStart(
- // track: *const c_void,
- // callback: extern "C" fn(*mut c_void, bool),
- // callback_data: *mut c_void
- // );
-
fn LKVideoTrackAddRenderer(track: *const c_void, renderer: *const c_void);
fn LKRemoteVideoTrackGetSid(track: *const c_void) -> CFStringRef;
@@ -103,6 +97,17 @@ extern "C" {
);
fn LKCreateScreenShareTrackForDisplay(display: *const c_void) -> *const c_void;
fn LKLocalAudioTrackCreateTrack() -> *const c_void;
+
+ fn LKLocalTrackPublicationMute(
+ publication: *const c_void,
+ on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef),
+ callback_data: *mut c_void,
+ );
+ fn LKLocalTrackPublicationUnmute(
+ publication: *const c_void,
+ on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef),
+ callback_data: *mut c_void,
+ );
}
pub type Sid = String;
@@ -525,6 +530,56 @@ impl Drop for LocalVideoTrack {
pub struct LocalTrackPublication(*const c_void);
+impl LocalTrackPublication {
+ pub fn mute(&self) -> impl Future<Output = Result<()>> {
+ let (tx, rx) = futures::channel::oneshot::channel();
+
+ extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) {
+ let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender<Result<()>>) };
+ if error.is_null() {
+ tx.send(Ok(())).ok();
+ } else {
+ let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
+ tx.send(Err(anyhow!(error))).ok();
+ }
+ }
+
+ unsafe {
+ LKLocalTrackPublicationMute(
+ self.0,
+ complete_callback,
+ Box::into_raw(Box::new(tx)) as *mut c_void,
+ )
+ }
+
+ async move { rx.await.unwrap() }
+ }
+
+ pub fn unmute(&self) -> impl Future<Output = Result<()>> {
+ let (tx, rx) = futures::channel::oneshot::channel();
+
+ extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) {
+ let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender<Result<()>>) };
+ if error.is_null() {
+ tx.send(Ok(())).ok();
+ } else {
+ let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
+ tx.send(Err(anyhow!(error))).ok();
+ }
+ }
+
+ unsafe {
+ LKLocalTrackPublicationUnmute(
+ self.0,
+ complete_callback,
+ Box::into_raw(Box::new(tx)) as *mut c_void,
+ )
+ }
+
+ async move { rx.await.unwrap() }
+ }
+}
+
impl Drop for LocalTrackPublication {
fn drop(&mut self) {
unsafe { CFRelease(self.0) }