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(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 device_id: Option<&DeviceId>,
196) -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> {
197 let device = audio::resolve_device(device_id, input)?;
198 let config = if input {
199 device
200 .default_input_config()
201 .context("failed to get default input config")?
202 } else {
203 device
204 .default_output_config()
205 .context("failed to get default output config")?
206 };
207 Ok((device, config))
208}
209
210pub(crate) fn get_sample_data(
211 sample_format: cpal::SampleFormat,
212 data: &cpal::Data,
213) -> anyhow::Result<Vec<i16>> {
214 match sample_format {
215 cpal::SampleFormat::I8 => Ok(convert_sample_data::<i8, i16>(data)),
216 cpal::SampleFormat::I16 => Ok(data.as_slice::<i16>().unwrap().to_vec()),
217 cpal::SampleFormat::I24 => Ok(convert_sample_data::<cpal::I24, i16>(data)),
218 cpal::SampleFormat::I32 => Ok(convert_sample_data::<i32, i16>(data)),
219 cpal::SampleFormat::I64 => Ok(convert_sample_data::<i64, i16>(data)),
220 cpal::SampleFormat::U8 => Ok(convert_sample_data::<u8, i16>(data)),
221 cpal::SampleFormat::U16 => Ok(convert_sample_data::<u16, i16>(data)),
222 cpal::SampleFormat::U32 => Ok(convert_sample_data::<u32, i16>(data)),
223 cpal::SampleFormat::U64 => Ok(convert_sample_data::<u64, i16>(data)),
224 cpal::SampleFormat::F32 => Ok(convert_sample_data::<f32, i16>(data)),
225 cpal::SampleFormat::F64 => Ok(convert_sample_data::<f64, i16>(data)),
226 _ => anyhow::bail!("Unsupported sample format"),
227 }
228}
229
230pub(crate) fn convert_sample_data<
231 TSource: cpal::SizedSample,
232 TDest: cpal::SizedSample + cpal::FromSample<TSource>,
233>(
234 data: &cpal::Data,
235) -> Vec<TDest> {
236 data.as_slice::<TSource>()
237 .unwrap()
238 .iter()
239 .map(|e| e.to_sample::<TDest>())
240 .collect()
241}