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