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