1use anyhow::Context as _;
  2use collections::HashMap;
  3
  4mod remote_video_track_view;
  5use cpal::traits::HostTrait as _;
  6pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent};
  7use rodio::DeviceTrait as _;
  8
  9mod record;
 10pub use record::CaptureInput;
 11
 12#[cfg(not(any(
 13    test,
 14    feature = "test-support",
 15    all(target_os = "windows", target_env = "gnu"),
 16    target_os = "freebsd"
 17)))]
 18mod livekit_client;
 19#[cfg(not(any(
 20    test,
 21    feature = "test-support",
 22    all(target_os = "windows", target_env = "gnu"),
 23    target_os = "freebsd"
 24)))]
 25pub use livekit_client::*;
 26
 27// If you need proper LSP in livekit_client you've got to comment
 28// - the cfg blocks above
 29// - the mods: mock_client & test and their conditional blocks
 30// - the pub use mock_client::* and their conditional blocks
 31
 32#[cfg(any(
 33    test,
 34    feature = "test-support",
 35    all(target_os = "windows", target_env = "gnu"),
 36    target_os = "freebsd"
 37))]
 38mod mock_client;
 39#[cfg(any(
 40    test,
 41    feature = "test-support",
 42    all(target_os = "windows", target_env = "gnu"),
 43    target_os = "freebsd"
 44))]
 45pub mod test;
 46#[cfg(any(
 47    test,
 48    feature = "test-support",
 49    all(target_os = "windows", target_env = "gnu"),
 50    target_os = "freebsd"
 51))]
 52pub use mock_client::*;
 53
 54#[derive(Debug, Clone)]
 55pub enum Participant {
 56    Local(LocalParticipant),
 57    Remote(RemoteParticipant),
 58}
 59
 60#[derive(Debug, Clone)]
 61pub enum TrackPublication {
 62    Local(LocalTrackPublication),
 63    Remote(RemoteTrackPublication),
 64}
 65
 66impl TrackPublication {
 67    pub fn sid(&self) -> TrackSid {
 68        match self {
 69            TrackPublication::Local(local) => local.sid(),
 70            TrackPublication::Remote(remote) => remote.sid(),
 71        }
 72    }
 73
 74    pub fn is_muted(&self) -> bool {
 75        match self {
 76            TrackPublication::Local(local) => local.is_muted(),
 77            TrackPublication::Remote(remote) => remote.is_muted(),
 78        }
 79    }
 80}
 81
 82#[derive(Clone, Debug)]
 83pub enum RemoteTrack {
 84    Audio(RemoteAudioTrack),
 85    Video(RemoteVideoTrack),
 86}
 87
 88impl RemoteTrack {
 89    pub fn sid(&self) -> TrackSid {
 90        match self {
 91            RemoteTrack::Audio(remote_audio_track) => remote_audio_track.sid(),
 92            RemoteTrack::Video(remote_video_track) => remote_video_track.sid(),
 93        }
 94    }
 95}
 96
 97#[derive(Clone, Debug)]
 98pub enum LocalTrack {
 99    Audio(LocalAudioTrack),
100    Video(LocalVideoTrack),
101}
102
103#[derive(Clone, Debug)]
104#[non_exhaustive]
105pub enum RoomEvent {
106    ParticipantConnected(RemoteParticipant),
107    ParticipantDisconnected(RemoteParticipant),
108    LocalTrackPublished {
109        publication: LocalTrackPublication,
110        track: LocalTrack,
111        participant: LocalParticipant,
112    },
113    LocalTrackUnpublished {
114        publication: LocalTrackPublication,
115        participant: LocalParticipant,
116    },
117    LocalTrackSubscribed {
118        track: LocalTrack,
119    },
120    TrackSubscribed {
121        track: RemoteTrack,
122        publication: RemoteTrackPublication,
123        participant: RemoteParticipant,
124    },
125    TrackUnsubscribed {
126        track: RemoteTrack,
127        publication: RemoteTrackPublication,
128        participant: RemoteParticipant,
129    },
130    TrackSubscriptionFailed {
131        participant: RemoteParticipant,
132        // error: livekit::track::TrackError,
133        track_sid: TrackSid,
134    },
135    TrackPublished {
136        publication: RemoteTrackPublication,
137        participant: RemoteParticipant,
138    },
139    TrackUnpublished {
140        publication: RemoteTrackPublication,
141        participant: RemoteParticipant,
142    },
143    TrackMuted {
144        participant: Participant,
145        publication: TrackPublication,
146    },
147    TrackUnmuted {
148        participant: Participant,
149        publication: TrackPublication,
150    },
151    RoomMetadataChanged {
152        old_metadata: String,
153        metadata: String,
154    },
155    ParticipantMetadataChanged {
156        participant: Participant,
157        old_metadata: String,
158        metadata: String,
159    },
160    ParticipantNameChanged {
161        participant: Participant,
162        old_name: String,
163        name: String,
164    },
165    ParticipantAttributesChanged {
166        participant: Participant,
167        changed_attributes: HashMap<String, String>,
168    },
169    ActiveSpeakersChanged {
170        speakers: Vec<Participant>,
171    },
172    ConnectionStateChanged(ConnectionState),
173    Connected {
174        participants_with_tracks: Vec<(RemoteParticipant, Vec<RemoteTrackPublication>)>,
175    },
176    Disconnected {
177        reason: &'static str,
178    },
179    Reconnecting,
180    Reconnected,
181}
182
183pub(crate) fn default_device(
184    input: bool,
185) -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> {
186    let device;
187    let config;
188    if input {
189        device = cpal::default_host()
190            .default_input_device()
191            .context("no audio input device available")?;
192        config = device
193            .default_input_config()
194            .context("failed to get default input config")?;
195    } else {
196        device = cpal::default_host()
197            .default_output_device()
198            .context("no audio output device available")?;
199        config = device
200            .default_output_config()
201            .context("failed to get default output config")?;
202    }
203    Ok((device, config))
204}
205
206pub(crate) fn get_sample_data(
207    sample_format: cpal::SampleFormat,
208    data: &cpal::Data,
209) -> anyhow::Result<Vec<i16>> {
210    match sample_format {
211        cpal::SampleFormat::I8 => Ok(convert_sample_data::<i8, i16>(data)),
212        cpal::SampleFormat::I16 => Ok(data.as_slice::<i16>().unwrap().to_vec()),
213        cpal::SampleFormat::I24 => Ok(convert_sample_data::<cpal::I24, i16>(data)),
214        cpal::SampleFormat::I32 => Ok(convert_sample_data::<i32, i16>(data)),
215        cpal::SampleFormat::I64 => Ok(convert_sample_data::<i64, i16>(data)),
216        cpal::SampleFormat::U8 => Ok(convert_sample_data::<u8, i16>(data)),
217        cpal::SampleFormat::U16 => Ok(convert_sample_data::<u16, i16>(data)),
218        cpal::SampleFormat::U32 => Ok(convert_sample_data::<u32, i16>(data)),
219        cpal::SampleFormat::U64 => Ok(convert_sample_data::<u64, i16>(data)),
220        cpal::SampleFormat::F32 => Ok(convert_sample_data::<f32, i16>(data)),
221        cpal::SampleFormat::F64 => Ok(convert_sample_data::<f64, i16>(data)),
222        _ => anyhow::bail!("Unsupported sample format"),
223    }
224}
225
226pub(crate) fn convert_sample_data<
227    TSource: cpal::SizedSample,
228    TDest: cpal::SizedSample + cpal::FromSample<TSource>,
229>(
230    data: &cpal::Data,
231) -> Vec<TDest> {
232    data.as_slice::<TSource>()
233        .unwrap()
234        .iter()
235        .map(|e| e.to_sample::<TDest>())
236        .collect()
237}