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}