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}