use crate::{ConnectionState, RoomUpdate, Sid};use anyhow::{anyhow, Context as _, Result};use core_foundation::{ array::{CFArray, CFArrayRef}, base::{CFRelease, CFRetain, TCFType}, string::{CFString, CFStringRef},};use futures::{ channel::{mpsc, oneshot}, Future,};pub use media::core_video::CVImageBuffer;use media::core_video::CVImageBufferRef;use parking_lot::Mutex;use postage::watch;use std::{ ffi::c_void, sync::{Arc, Weak},}; macro_rules! pointer_type { ($pointer_name:ident) => { #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct $pointer_name(pub *const std::ffi::c_void); unsafe impl Send for $pointer_name {} };} mod swift { pointer_type!(Room); pointer_type!(LocalAudioTrack); pointer_type!(RemoteAudioTrack); pointer_type!(LocalVideoTrack); pointer_type!(RemoteVideoTrack); pointer_type!(LocalTrackPublication); pointer_type!(RemoteTrackPublication); pointer_type!(MacOSDisplay); pointer_type!(RoomDelegate);} extern "C" { fn LKRoomDelegateCreate( callback_data: *mut c_void, on_did_disconnect: extern "C" fn(callback_data: *mut c_void), on_did_subscribe_to_remote_audio_track: extern "C" fn( callback_data: *mut c_void, publisher_id: CFStringRef, track_id: CFStringRef, remote_track: swift::RemoteAudioTrack, remote_publication: swift::RemoteTrackPublication, ), on_did_unsubscribe_from_remote_audio_track: extern "C" fn( callback_data: *mut c_void, publisher_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( callback_data: *mut c_void, publisher_id: CFStringRef, track_id: CFStringRef, remote_track: swift::RemoteVideoTrack, ), on_did_unsubscribe_from_remote_video_track: extern "C" fn( callback_data: *mut c_void, publisher_id: CFStringRef, track_id: CFStringRef, ), on_did_publish_or_unpublish_local_audio_track: extern "C" fn( callback_data: *mut c_void, publication: swift::LocalTrackPublication, is_published: bool, ), on_did_publish_or_unpublish_local_video_track: extern "C" fn( callback_data: *mut c_void, publication: swift::LocalTrackPublication, is_published: bool, ), ) -> swift::RoomDelegate; fn LKRoomCreate(delegate: swift::RoomDelegate) -> swift::Room; fn LKRoomConnect( room: swift::Room, url: CFStringRef, token: CFStringRef, callback: extern "C" fn(*mut c_void, CFStringRef), callback_data: *mut c_void, ); fn LKRoomDisconnect(room: swift::Room); fn LKRoomPublishVideoTrack( room: swift::Room, track: swift::LocalVideoTrack, callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), callback_data: *mut c_void, ); fn LKRoomPublishAudioTrack( room: swift::Room, track: swift::LocalAudioTrack, callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), callback_data: *mut c_void, ); fn LKRoomUnpublishTrack(room: swift::Room, publication: swift::LocalTrackPublication); fn LKRoomAudioTracksForRemoteParticipant( room: swift::Room, participant_id: CFStringRef, ) -> CFArrayRef; fn LKRoomAudioTrackPublicationsForRemoteParticipant( room: swift::Room, participant_id: CFStringRef, ) -> CFArrayRef; fn LKRoomVideoTracksForRemoteParticipant( room: swift::Room, participant_id: CFStringRef, ) -> CFArrayRef; fn LKVideoRendererCreate( callback_data: *mut c_void, on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool, on_drop: extern "C" fn(callback_data: *mut c_void), ) -> *const c_void; fn LKRemoteAudioTrackGetSid(track: swift::RemoteAudioTrack) -> CFStringRef; fn LKRemoteVideoTrackGetSid(track: swift::RemoteVideoTrack) -> CFStringRef; fn LKRemoteAudioTrackStart(track: swift::RemoteAudioTrack); fn LKRemoteAudioTrackStop(track: swift::RemoteAudioTrack); fn LKVideoTrackAddRenderer(track: swift::RemoteVideoTrack, renderer: *const c_void); fn LKDisplaySources( callback_data: *mut c_void, callback: extern "C" fn( callback_data: *mut c_void, sources: CFArrayRef, error: CFStringRef, ), ); fn LKCreateScreenShareTrackForDisplay(display: swift::MacOSDisplay) -> swift::LocalVideoTrack; fn LKLocalAudioTrackCreateTrack() -> swift::LocalAudioTrack; fn LKLocalTrackPublicationSetMute( publication: swift::LocalTrackPublication, muted: bool, on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), callback_data: *mut c_void, ); fn LKRemoteTrackPublicationSetEnabled( publication: swift::RemoteTrackPublication, enabled: bool, on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), callback_data: *mut c_void, ); fn LKLocalTrackPublicationIsMuted(publication: swift::LocalTrackPublication) -> bool; fn LKRemoteTrackPublicationIsMuted(publication: swift::RemoteTrackPublication) -> bool; fn LKLocalTrackPublicationGetSid(publication: swift::LocalTrackPublication) -> CFStringRef; fn LKRemoteTrackPublicationGetSid(publication: swift::RemoteTrackPublication) -> CFStringRef;} pub struct Room { native_room: swift::Room, connection: Mutex<( watch::Sender<ConnectionState>, watch::Receiver<ConnectionState>, )>, update_subscribers: Mutex<Vec<mpsc::UnboundedSender<RoomUpdate>>>, _delegate: RoomDelegate,} impl Room { pub fn new() -> Arc<Self> { Arc::new_cyclic(|weak_room| { let delegate = RoomDelegate::new(weak_room.clone()); Self { native_room: unsafe { LKRoomCreate(delegate.native_delegate) }, connection: Mutex::new(watch::channel_with(ConnectionState::Disconnected)), update_subscribers: Default::default(), _delegate: delegate, } }) } pub fn status(&self) -> watch::Receiver<ConnectionState> { self.connection.lock().1.clone() } pub fn connect(self: &Arc<Self>, url: &str, token: &str) -> impl Future<Output = Result<()>> { let url = CFString::new(url); let token = CFString::new(token); let (did_connect, tx, rx) = Self::build_done_callback(); unsafe { LKRoomConnect( self.native_room, url.as_concrete_TypeRef(), token.as_concrete_TypeRef(), did_connect, tx, ) } let this = self.clone(); let url = url.to_string(); let token = token.to_string(); async move { rx.await.unwrap().context("error connecting to room")?; *this.connection.lock().0.borrow_mut() = ConnectionState::Connected { url, token }; Ok(()) } } fn did_disconnect(&self) { *self.connection.lock().0.borrow_mut() = ConnectionState::Disconnected; } pub fn display_sources(self: &Arc<Self>) -> impl Future<Output = Result<Vec<MacOSDisplay>>> { extern "C" fn callback(tx: *mut c_void, sources: CFArrayRef, error: CFStringRef) { unsafe { let tx = Box::from_raw(tx as *mut oneshot::Sender<Result<Vec<MacOSDisplay>>>); if sources.is_null() { let _ = tx.send(Err(anyhow!("{}", CFString::wrap_under_get_rule(error)))); } else { let sources = CFArray::wrap_under_get_rule(sources) .into_iter() .map(|source| MacOSDisplay::new(swift::MacOSDisplay(*source))) .collect(); let _ = tx.send(Ok(sources)); } } } let (tx, rx) = oneshot::channel(); unsafe { LKDisplaySources(Box::into_raw(Box::new(tx)) as *mut _, callback); } async move { rx.await.unwrap() } } pub fn publish_video_track( self: &Arc<Self>, track: LocalVideoTrack, ) -> impl Future<Output = Result<LocalTrackPublication>> { let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>(); extern "C" fn callback( tx: *mut c_void, publication: swift::LocalTrackPublication, error: CFStringRef, ) { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) }; if error.is_null() { let _ = tx.send(Ok(LocalTrackPublication::new(publication))); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); } } unsafe { LKRoomPublishVideoTrack( self.native_room, track.0, callback, Box::into_raw(Box::new(tx)) as *mut c_void, ); } async { rx.await.unwrap().context("error publishing video track") } } pub fn publish_audio_track( self: &Arc<Self>, track: LocalAudioTrack, ) -> impl Future<Output = Result<LocalTrackPublication>> { let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>(); extern "C" fn callback( tx: *mut c_void, publication: swift::LocalTrackPublication, error: CFStringRef, ) { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) }; if error.is_null() { let _ = tx.send(Ok(LocalTrackPublication::new(publication))); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); } } unsafe { LKRoomPublishAudioTrack( self.native_room, track.0, callback, Box::into_raw(Box::new(tx)) as *mut c_void, ); } async { rx.await.unwrap().context("error publishing audio track") } } pub fn unpublish_track(&self, publication: LocalTrackPublication) { unsafe { LKRoomUnpublishTrack(self.native_room, publication.0); } } pub fn remote_video_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteVideoTrack>> { unsafe { let tracks = LKRoomVideoTracksForRemoteParticipant( self.native_room, CFString::new(participant_id).as_concrete_TypeRef(), ); if tracks.is_null() { Vec::new() } else { let tracks = CFArray::wrap_under_get_rule(tracks); tracks .into_iter() .map(|native_track| { let native_track = swift::RemoteVideoTrack(*native_track); let id = CFString::wrap_under_get_rule(LKRemoteVideoTrackGetSid(native_track)) .to_string(); Arc::new(RemoteVideoTrack::new( native_track, id, participant_id.into(), )) }) .collect() } } } pub fn remote_audio_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteAudioTrack>> { unsafe { let tracks = LKRoomAudioTracksForRemoteParticipant( self.native_room, CFString::new(participant_id).as_concrete_TypeRef(), ); if tracks.is_null() { Vec::new() } else { let tracks = CFArray::wrap_under_get_rule(tracks); tracks .into_iter() .map(|native_track| { let native_track = swift::RemoteAudioTrack(*native_track); let id = CFString::wrap_under_get_rule(LKRemoteAudioTrackGetSid(native_track)) .to_string(); Arc::new(RemoteAudioTrack::new( native_track, id, participant_id.into(), )) }) .collect() } } } pub fn remote_audio_track_publications( &self, participant_id: &str, ) -> Vec<Arc<RemoteTrackPublication>> { unsafe { let tracks = LKRoomAudioTrackPublicationsForRemoteParticipant( self.native_room, CFString::new(participant_id).as_concrete_TypeRef(), ); if tracks.is_null() { Vec::new() } else { let tracks = CFArray::wrap_under_get_rule(tracks); tracks .into_iter() .map(|native_track_publication| { let native_track_publication = swift::RemoteTrackPublication(*native_track_publication); Arc::new(RemoteTrackPublication::new(native_track_publication)) }) .collect() } } } pub fn updates(&self) -> mpsc::UnboundedReceiver<RoomUpdate> { let (tx, rx) = mpsc::unbounded(); self.update_subscribers.lock().push(tx); rx } fn did_subscribe_to_remote_audio_track( &self, track: RemoteAudioTrack, publication: RemoteTrackPublication, ) { let track = Arc::new(track); let publication = Arc::new(publication); self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::SubscribedToRemoteAudioTrack( track.clone(), publication.clone(), )) .is_ok() }); } fn did_unsubscribe_from_remote_audio_track(&self, publisher_id: String, track_id: String) { self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::UnsubscribedFromRemoteAudioTrack { publisher_id: publisher_id.clone(), track_id: track_id.clone(), }) .is_ok() }); } fn mute_changed_from_remote_audio_track(&self, track_id: String, muted: bool) { self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::RemoteAudioTrackMuteChanged { track_id: track_id.clone(), muted, }) .is_ok() }); } fn active_speakers_changed(&self, speakers: Vec<String>) { self.update_subscribers.lock().retain(move |tx| { tx.unbounded_send(RoomUpdate::ActiveSpeakersChanged { speakers: speakers.clone(), }) .is_ok() }); } fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { let track = Arc::new(track); self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::SubscribedToRemoteVideoTrack(track.clone())) .is_ok() }); } fn did_unsubscribe_from_remote_video_track(&self, publisher_id: String, track_id: String) { self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::UnsubscribedFromRemoteVideoTrack { publisher_id: publisher_id.clone(), track_id: track_id.clone(), }) .is_ok() }); } fn build_done_callback() -> ( extern "C" fn(*mut c_void, CFStringRef), *mut c_void, oneshot::Receiver<Result<()>>, ) { let (tx, rx) = oneshot::channel(); extern "C" fn done_callback(tx: *mut c_void, error: CFStringRef) { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<()>>) }; if error.is_null() { let _ = tx.send(Ok(())); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); } } ( done_callback, Box::into_raw(Box::new(tx)) as *mut c_void, rx, ) } pub fn set_display_sources(&self, _: Vec<MacOSDisplay>) { unreachable!("This is a test-only function") }} impl Drop for Room { fn drop(&mut self) {Lines 1–500 of 981. Load more
use crate::{ConnectionState, RoomUpdate, Sid}; use anyhow::{anyhow, Context as _, Result}; use core_foundation::{ array::{CFArray, CFArrayRef}, base::{CFRelease, CFRetain, TCFType}, string::{CFString, CFStringRef}, }; use futures::{ channel::{mpsc, oneshot}, Future, }; pub use media::core_video::CVImageBuffer; use media::core_video::CVImageBufferRef; use parking_lot::Mutex; use postage::watch; use std::{ ffi::c_void, sync::{Arc, Weak}, }; macro_rules! pointer_type { ($pointer_name:ident) => { #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct $pointer_name(pub *const std::ffi::c_void); unsafe impl Send for $pointer_name {} }; } mod swift { pointer_type!(Room); pointer_type!(LocalAudioTrack); pointer_type!(RemoteAudioTrack); pointer_type!(LocalVideoTrack); pointer_type!(RemoteVideoTrack); pointer_type!(LocalTrackPublication); pointer_type!(RemoteTrackPublication); pointer_type!(MacOSDisplay); pointer_type!(RoomDelegate); } extern "C" { fn LKRoomDelegateCreate( callback_data: *mut c_void, on_did_disconnect: extern "C" fn(callback_data: *mut c_void), on_did_subscribe_to_remote_audio_track: extern "C" fn( callback_data: *mut c_void, publisher_id: CFStringRef, track_id: CFStringRef, remote_track: swift::RemoteAudioTrack, remote_publication: swift::RemoteTrackPublication, ), on_did_unsubscribe_from_remote_audio_track: extern "C" fn( callback_data: *mut c_void, publisher_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( callback_data: *mut c_void, publisher_id: CFStringRef, track_id: CFStringRef, remote_track: swift::RemoteVideoTrack, ), on_did_unsubscribe_from_remote_video_track: extern "C" fn( callback_data: *mut c_void, publisher_id: CFStringRef, track_id: CFStringRef, ), on_did_publish_or_unpublish_local_audio_track: extern "C" fn( callback_data: *mut c_void, publication: swift::LocalTrackPublication, is_published: bool, ), on_did_publish_or_unpublish_local_video_track: extern "C" fn( callback_data: *mut c_void, publication: swift::LocalTrackPublication, is_published: bool, ), ) -> swift::RoomDelegate; fn LKRoomCreate(delegate: swift::RoomDelegate) -> swift::Room; fn LKRoomConnect( room: swift::Room, url: CFStringRef, token: CFStringRef, callback: extern "C" fn(*mut c_void, CFStringRef), callback_data: *mut c_void, ); fn LKRoomDisconnect(room: swift::Room); fn LKRoomPublishVideoTrack( room: swift::Room, track: swift::LocalVideoTrack, callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), callback_data: *mut c_void, ); fn LKRoomPublishAudioTrack( room: swift::Room, track: swift::LocalAudioTrack, callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), callback_data: *mut c_void, ); fn LKRoomUnpublishTrack(room: swift::Room, publication: swift::LocalTrackPublication); fn LKRoomAudioTracksForRemoteParticipant( room: swift::Room, participant_id: CFStringRef, ) -> CFArrayRef; fn LKRoomAudioTrackPublicationsForRemoteParticipant( room: swift::Room, participant_id: CFStringRef, ) -> CFArrayRef; fn LKRoomVideoTracksForRemoteParticipant( room: swift::Room, participant_id: CFStringRef, ) -> CFArrayRef; fn LKVideoRendererCreate( callback_data: *mut c_void, on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool, on_drop: extern "C" fn(callback_data: *mut c_void), ) -> *const c_void; fn LKRemoteAudioTrackGetSid(track: swift::RemoteAudioTrack) -> CFStringRef; fn LKRemoteVideoTrackGetSid(track: swift::RemoteVideoTrack) -> CFStringRef; fn LKRemoteAudioTrackStart(track: swift::RemoteAudioTrack); fn LKRemoteAudioTrackStop(track: swift::RemoteAudioTrack); fn LKVideoTrackAddRenderer(track: swift::RemoteVideoTrack, renderer: *const c_void); fn LKDisplaySources( callback_data: *mut c_void, callback: extern "C" fn( callback_data: *mut c_void, sources: CFArrayRef, error: CFStringRef, ), ); fn LKCreateScreenShareTrackForDisplay(display: swift::MacOSDisplay) -> swift::LocalVideoTrack; fn LKLocalAudioTrackCreateTrack() -> swift::LocalAudioTrack; fn LKLocalTrackPublicationSetMute( publication: swift::LocalTrackPublication, muted: bool, on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), callback_data: *mut c_void, ); fn LKRemoteTrackPublicationSetEnabled( publication: swift::RemoteTrackPublication, enabled: bool, on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), callback_data: *mut c_void, ); fn LKLocalTrackPublicationIsMuted(publication: swift::LocalTrackPublication) -> bool; fn LKRemoteTrackPublicationIsMuted(publication: swift::RemoteTrackPublication) -> bool; fn LKLocalTrackPublicationGetSid(publication: swift::LocalTrackPublication) -> CFStringRef; fn LKRemoteTrackPublicationGetSid(publication: swift::RemoteTrackPublication) -> CFStringRef; } pub struct Room { native_room: swift::Room, connection: Mutex<( watch::Sender<ConnectionState>, watch::Receiver<ConnectionState>, )>, update_subscribers: Mutex<Vec<mpsc::UnboundedSender<RoomUpdate>>>, _delegate: RoomDelegate, } impl Room { pub fn new() -> Arc<Self> { Arc::new_cyclic(|weak_room| { let delegate = RoomDelegate::new(weak_room.clone()); Self { native_room: unsafe { LKRoomCreate(delegate.native_delegate) }, connection: Mutex::new(watch::channel_with(ConnectionState::Disconnected)), update_subscribers: Default::default(), _delegate: delegate, } }) } pub fn status(&self) -> watch::Receiver<ConnectionState> { self.connection.lock().1.clone() } pub fn connect(self: &Arc<Self>, url: &str, token: &str) -> impl Future<Output = Result<()>> { let url = CFString::new(url); let token = CFString::new(token); let (did_connect, tx, rx) = Self::build_done_callback(); unsafe { LKRoomConnect( self.native_room, url.as_concrete_TypeRef(), token.as_concrete_TypeRef(), did_connect, tx, ) } let this = self.clone(); let url = url.to_string(); let token = token.to_string(); async move { rx.await.unwrap().context("error connecting to room")?; *this.connection.lock().0.borrow_mut() = ConnectionState::Connected { url, token }; Ok(()) } } fn did_disconnect(&self) { *self.connection.lock().0.borrow_mut() = ConnectionState::Disconnected; } pub fn display_sources(self: &Arc<Self>) -> impl Future<Output = Result<Vec<MacOSDisplay>>> { extern "C" fn callback(tx: *mut c_void, sources: CFArrayRef, error: CFStringRef) { unsafe { let tx = Box::from_raw(tx as *mut oneshot::Sender<Result<Vec<MacOSDisplay>>>); if sources.is_null() { let _ = tx.send(Err(anyhow!("{}", CFString::wrap_under_get_rule(error)))); } else { let sources = CFArray::wrap_under_get_rule(sources) .into_iter() .map(|source| MacOSDisplay::new(swift::MacOSDisplay(*source))) .collect(); let _ = tx.send(Ok(sources)); } } } let (tx, rx) = oneshot::channel(); unsafe { LKDisplaySources(Box::into_raw(Box::new(tx)) as *mut _, callback); } async move { rx.await.unwrap() } } pub fn publish_video_track( self: &Arc<Self>, track: LocalVideoTrack, ) -> impl Future<Output = Result<LocalTrackPublication>> { let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>(); extern "C" fn callback( tx: *mut c_void, publication: swift::LocalTrackPublication, error: CFStringRef, ) { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) }; if error.is_null() { let _ = tx.send(Ok(LocalTrackPublication::new(publication))); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); } } unsafe { LKRoomPublishVideoTrack( self.native_room, track.0, callback, Box::into_raw(Box::new(tx)) as *mut c_void, ); } async { rx.await.unwrap().context("error publishing video track") } } pub fn publish_audio_track( self: &Arc<Self>, track: LocalAudioTrack, ) -> impl Future<Output = Result<LocalTrackPublication>> { let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>(); extern "C" fn callback( tx: *mut c_void, publication: swift::LocalTrackPublication, error: CFStringRef, ) { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) }; if error.is_null() { let _ = tx.send(Ok(LocalTrackPublication::new(publication))); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); } } unsafe { LKRoomPublishAudioTrack( self.native_room, track.0, callback, Box::into_raw(Box::new(tx)) as *mut c_void, ); } async { rx.await.unwrap().context("error publishing audio track") } } pub fn unpublish_track(&self, publication: LocalTrackPublication) { unsafe { LKRoomUnpublishTrack(self.native_room, publication.0); } } pub fn remote_video_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteVideoTrack>> { unsafe { let tracks = LKRoomVideoTracksForRemoteParticipant( self.native_room, CFString::new(participant_id).as_concrete_TypeRef(), ); if tracks.is_null() { Vec::new() } else { let tracks = CFArray::wrap_under_get_rule(tracks); tracks .into_iter() .map(|native_track| { let native_track = swift::RemoteVideoTrack(*native_track); let id = CFString::wrap_under_get_rule(LKRemoteVideoTrackGetSid(native_track)) .to_string(); Arc::new(RemoteVideoTrack::new( native_track, id, participant_id.into(), )) }) .collect() } } } pub fn remote_audio_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteAudioTrack>> { unsafe { let tracks = LKRoomAudioTracksForRemoteParticipant( self.native_room, CFString::new(participant_id).as_concrete_TypeRef(), ); if tracks.is_null() { Vec::new() } else { let tracks = CFArray::wrap_under_get_rule(tracks); tracks .into_iter() .map(|native_track| { let native_track = swift::RemoteAudioTrack(*native_track); let id = CFString::wrap_under_get_rule(LKRemoteAudioTrackGetSid(native_track)) .to_string(); Arc::new(RemoteAudioTrack::new( native_track, id, participant_id.into(), )) }) .collect() } } } pub fn remote_audio_track_publications( &self, participant_id: &str, ) -> Vec<Arc<RemoteTrackPublication>> { unsafe { let tracks = LKRoomAudioTrackPublicationsForRemoteParticipant( self.native_room, CFString::new(participant_id).as_concrete_TypeRef(), ); if tracks.is_null() { Vec::new() } else { let tracks = CFArray::wrap_under_get_rule(tracks); tracks .into_iter() .map(|native_track_publication| { let native_track_publication = swift::RemoteTrackPublication(*native_track_publication); Arc::new(RemoteTrackPublication::new(native_track_publication)) }) .collect() } } } pub fn updates(&self) -> mpsc::UnboundedReceiver<RoomUpdate> { let (tx, rx) = mpsc::unbounded(); self.update_subscribers.lock().push(tx); rx } fn did_subscribe_to_remote_audio_track( &self, track: RemoteAudioTrack, publication: RemoteTrackPublication, ) { let track = Arc::new(track); let publication = Arc::new(publication); self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::SubscribedToRemoteAudioTrack( track.clone(), publication.clone(), )) .is_ok() }); } fn did_unsubscribe_from_remote_audio_track(&self, publisher_id: String, track_id: String) { self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::UnsubscribedFromRemoteAudioTrack { publisher_id: publisher_id.clone(), track_id: track_id.clone(), }) .is_ok() }); } fn mute_changed_from_remote_audio_track(&self, track_id: String, muted: bool) { self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::RemoteAudioTrackMuteChanged { track_id: track_id.clone(), muted, }) .is_ok() }); } fn active_speakers_changed(&self, speakers: Vec<String>) { self.update_subscribers.lock().retain(move |tx| { tx.unbounded_send(RoomUpdate::ActiveSpeakersChanged { speakers: speakers.clone(), }) .is_ok() }); } fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { let track = Arc::new(track); self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::SubscribedToRemoteVideoTrack(track.clone())) .is_ok() }); } fn did_unsubscribe_from_remote_video_track(&self, publisher_id: String, track_id: String) { self.update_subscribers.lock().retain(|tx| { tx.unbounded_send(RoomUpdate::UnsubscribedFromRemoteVideoTrack { publisher_id: publisher_id.clone(), track_id: track_id.clone(), }) .is_ok() }); } fn build_done_callback() -> ( extern "C" fn(*mut c_void, CFStringRef), *mut c_void, oneshot::Receiver<Result<()>>, ) { let (tx, rx) = oneshot::channel(); extern "C" fn done_callback(tx: *mut c_void, error: CFStringRef) { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<()>>) }; if error.is_null() { let _ = tx.send(Ok(())); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); } } ( done_callback, Box::into_raw(Box::new(tx)) as *mut c_void, rx, ) } pub fn set_display_sources(&self, _: Vec<MacOSDisplay>) { unreachable!("This is a test-only function") } } impl Drop for Room { fn drop(&mut self) {