lib.rs

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