lib.rs

  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(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(Debug, Clone)]
 71pub enum TrackPublication {
 72    Local(LocalTrackPublication),
 73    Remote(RemoteTrackPublication),
 74}
 75
 76impl TrackPublication {
 77    pub fn sid(&self) -> TrackSid {
 78        match self {
 79            TrackPublication::Local(local) => local.sid(),
 80            TrackPublication::Remote(remote) => remote.sid(),
 81        }
 82    }
 83
 84    pub fn is_muted(&self) -> bool {
 85        match self {
 86            TrackPublication::Local(local) => local.is_muted(),
 87            TrackPublication::Remote(remote) => remote.is_muted(),
 88        }
 89    }
 90}
 91
 92#[derive(Clone, Debug)]
 93pub enum RemoteTrack {
 94    Audio(RemoteAudioTrack),
 95    Video(RemoteVideoTrack),
 96}
 97
 98impl RemoteTrack {
 99    pub fn sid(&self) -> TrackSid {
100        match self {
101            RemoteTrack::Audio(remote_audio_track) => remote_audio_track.sid(),
102            RemoteTrack::Video(remote_video_track) => remote_video_track.sid(),
103        }
104    }
105}
106
107#[derive(Clone, Debug)]
108pub enum LocalTrack {
109    Audio(LocalAudioTrack),
110    Video(LocalVideoTrack),
111}
112
113#[derive(Clone, Debug)]
114#[non_exhaustive]
115pub enum RoomEvent {
116    ParticipantConnected(RemoteParticipant),
117    ParticipantDisconnected(RemoteParticipant),
118    LocalTrackPublished {
119        publication: LocalTrackPublication,
120        track: LocalTrack,
121        participant: LocalParticipant,
122    },
123    LocalTrackUnpublished {
124        publication: LocalTrackPublication,
125        participant: LocalParticipant,
126    },
127    LocalTrackSubscribed {
128        track: LocalTrack,
129    },
130    TrackSubscribed {
131        track: RemoteTrack,
132        publication: RemoteTrackPublication,
133        participant: RemoteParticipant,
134    },
135    TrackUnsubscribed {
136        track: RemoteTrack,
137        publication: RemoteTrackPublication,
138        participant: RemoteParticipant,
139    },
140    TrackSubscriptionFailed {
141        participant: RemoteParticipant,
142        // error: livekit::track::TrackError,
143        track_sid: TrackSid,
144    },
145    TrackPublished {
146        publication: RemoteTrackPublication,
147        participant: RemoteParticipant,
148    },
149    TrackUnpublished {
150        publication: RemoteTrackPublication,
151        participant: RemoteParticipant,
152    },
153    TrackMuted {
154        participant: Participant,
155        publication: TrackPublication,
156    },
157    TrackUnmuted {
158        participant: Participant,
159        publication: TrackPublication,
160    },
161    RoomMetadataChanged {
162        old_metadata: String,
163        metadata: String,
164    },
165    ParticipantMetadataChanged {
166        participant: Participant,
167        old_metadata: String,
168        metadata: String,
169    },
170    ParticipantNameChanged {
171        participant: Participant,
172        old_name: String,
173        name: String,
174    },
175    ParticipantAttributesChanged {
176        participant: Participant,
177        changed_attributes: HashMap<String, String>,
178    },
179    ActiveSpeakersChanged {
180        speakers: Vec<Participant>,
181    },
182    ConnectionStateChanged(ConnectionState),
183    Connected {
184        participants_with_tracks: Vec<(RemoteParticipant, Vec<RemoteTrackPublication>)>,
185    },
186    Disconnected {
187        reason: &'static str,
188    },
189    Reconnecting,
190    Reconnected,
191}
192
193pub(crate) fn default_device(
194    input: bool,
195) -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> {
196    let device;
197    let config;
198    if input {
199        device = cpal::default_host()
200            .default_input_device()
201            .context("no audio input device available")?;
202        config = device
203            .default_input_config()
204            .context("failed to get default input config")?;
205    } else {
206        device = cpal::default_host()
207            .default_output_device()
208            .context("no audio output device available")?;
209        config = device
210            .default_output_config()
211            .context("failed to get default output config")?;
212    }
213    Ok((device, config))
214}
215
216pub(crate) fn get_sample_data(
217    sample_format: cpal::SampleFormat,
218    data: &cpal::Data,
219) -> anyhow::Result<Vec<i16>> {
220    match sample_format {
221        cpal::SampleFormat::I8 => Ok(convert_sample_data::<i8, i16>(data)),
222        cpal::SampleFormat::I16 => Ok(data.as_slice::<i16>().unwrap().to_vec()),
223        cpal::SampleFormat::I24 => Ok(convert_sample_data::<cpal::I24, i16>(data)),
224        cpal::SampleFormat::I32 => Ok(convert_sample_data::<i32, i16>(data)),
225        cpal::SampleFormat::I64 => Ok(convert_sample_data::<i64, i16>(data)),
226        cpal::SampleFormat::U8 => Ok(convert_sample_data::<u8, i16>(data)),
227        cpal::SampleFormat::U16 => Ok(convert_sample_data::<u16, i16>(data)),
228        cpal::SampleFormat::U32 => Ok(convert_sample_data::<u32, i16>(data)),
229        cpal::SampleFormat::U64 => Ok(convert_sample_data::<u64, i16>(data)),
230        cpal::SampleFormat::F32 => Ok(convert_sample_data::<f32, i16>(data)),
231        cpal::SampleFormat::F64 => Ok(convert_sample_data::<f64, i16>(data)),
232        _ => anyhow::bail!("Unsupported sample format"),
233    }
234}
235
236pub(crate) fn convert_sample_data<
237    TSource: cpal::SizedSample,
238    TDest: cpal::SizedSample + cpal::FromSample<TSource>,
239>(
240    data: &cpal::Data,
241) -> Vec<TDest> {
242    data.as_slice::<TSource>()
243        .unwrap()
244        .iter()
245        .map(|e| e.to_sample::<TDest>())
246        .collect()
247}