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}