1use std::{collections::HashMap, fmt::Debug, sync::Arc};
16
17use libwebrtc::enum_dispatch;
18use livekit_protocol as proto;
19use parking_lot::{Mutex, RwLock};
20
21use crate::{prelude::*, rtc_engine::RtcEngine};
22
23mod local_participant;
24mod remote_participant;
25mod rpc;
26
27pub use local_participant::*;
28pub use remote_participant::*;
29pub use rpc::*;
30
31#[derive(Debug, Clone, Copy, Eq, PartialEq)]
32pub enum ConnectionQuality {
33 Excellent,
34 Good,
35 Poor,
36 Lost,
37}
38
39#[derive(Debug, Clone, Copy, Eq, PartialEq)]
40pub enum ParticipantState {
41 Joining,
42 Joined,
43 Active,
44 Disconnected,
45}
46
47#[derive(Debug, Clone, Copy, Eq, PartialEq)]
48pub enum ParticipantKind {
49 Standard,
50 Ingress,
51 Egress,
52 Sip,
53 Agent,
54 Connector,
55 Bridge,
56}
57
58#[derive(Debug, Clone, Copy, Eq, PartialEq)]
59pub enum ParticipantKindDetail {
60 CloudAgent,
61 Forwarded,
62 ConnectorWhatsapp,
63 ConnectorTwilio,
64 BridgeRtsp,
65}
66
67#[derive(Debug, Clone, Copy, Eq, PartialEq)]
68pub enum DisconnectReason {
69 UnknownReason,
70 ClientInitiated,
71 DuplicateIdentity,
72 ServerShutdown,
73 ParticipantRemoved,
74 RoomDeleted,
75 StateMismatch,
76 JoinFailure,
77 Migration,
78 SignalClose,
79 RoomClosed,
80 UserUnavailable,
81 UserRejected,
82 SipTrunkFailure,
83 ConnectionTimeout,
84 MediaFailure,
85 AgentError,
86}
87
88#[derive(Debug, Clone)]
89pub enum Participant {
90 Local(LocalParticipant),
91 Remote(RemoteParticipant),
92}
93
94impl Participant {
95 enum_dispatch!(
96 [Local, Remote];
97 pub fn sid(self: &Self) -> ParticipantSid;
98 pub fn identity(self: &Self) -> ParticipantIdentity;
99 pub fn name(self: &Self) -> String;
100 pub fn state(self: &Self) -> ParticipantState;
101 pub fn metadata(self: &Self) -> String;
102 pub fn attributes(self: &Self) -> HashMap<String, String>;
103 pub fn is_speaking(self: &Self) -> bool;
104 pub fn audio_level(self: &Self) -> f32;
105 pub fn connection_quality(self: &Self) -> ConnectionQuality;
106 pub fn kind(self: &Self) -> ParticipantKind;
107 pub fn kind_details(self: &Self) -> Vec<ParticipantKindDetail>;
108 pub fn disconnect_reason(self: &Self) -> DisconnectReason;
109 pub fn joined_at(self: &Self) -> i64;
110 pub fn is_encrypted(self: &Self) -> bool;
111 pub fn permission(self: &Self) -> Option<proto::ParticipantPermission>;
112 pub fn client_protocol(self: &Self) -> i32;
113
114 pub(crate) fn update_info(self: &Self, info: proto::ParticipantInfo) -> ();
115
116 pub(crate) fn set_speaking(self: &Self, speaking: bool) -> ();
118 pub(crate) fn set_audio_level(self: &Self, level: f32) -> ();
119 pub(crate) fn set_connection_quality(self: &Self, quality: ConnectionQuality) -> ();
120 pub(crate) fn add_publication(self: &Self, publication: TrackPublication) -> ();
121 pub(crate) fn remove_publication(self: &Self, sid: &TrackSid) -> Option<TrackPublication>;
122 pub(crate) fn update_data_encryption_status(self: &Self, is_encrypted: bool) -> ();
123 );
124
125 pub fn track_publications(&self) -> HashMap<TrackSid, TrackPublication> {
126 match self {
127 Participant::Local(p) => p.internal_track_publications(),
128 Participant::Remote(p) => p.internal_track_publications(),
129 }
130 }
131}
132
133struct ParticipantInfo {
134 pub sid: ParticipantSid,
135 pub identity: ParticipantIdentity,
136 pub name: String,
137 pub state: ParticipantState,
138 pub metadata: String,
139 pub attributes: HashMap<String, String>,
140 pub speaking: bool,
141 pub audio_level: f32,
142 pub connection_quality: ConnectionQuality,
143 pub kind: ParticipantKind,
144 pub kind_details: Vec<ParticipantKindDetail>,
145 pub disconnect_reason: DisconnectReason,
146 pub joined_at: i64,
147 pub permission: Option<proto::ParticipantPermission>,
148 pub client_protocol: i32,
149}
150
151type TrackMutedHandler = Box<dyn Fn(Participant, TrackPublication) + Send>;
152type TrackUnmutedHandler = Box<dyn Fn(Participant, TrackPublication) + Send>;
153type MetadataChangedHandler = Box<dyn Fn(Participant, String, String) + Send>;
154type AttributesChangedHandler = Box<dyn Fn(Participant, HashMap<String, String>) + Send>;
155type NameChangedHandler = Box<dyn Fn(Participant, String, String) + Send>;
156type EncryptionStatusChangedHandler = Box<dyn Fn(Participant, bool) + Send>;
157type PermissionChangedHandler =
158 Box<dyn Fn(Participant, Option<proto::ParticipantPermission>) + Send>;
159
160#[derive(Default)]
161struct ParticipantEvents {
162 track_muted: Mutex<Option<TrackMutedHandler>>,
163 track_unmuted: Mutex<Option<TrackUnmutedHandler>>,
164 metadata_changed: Mutex<Option<MetadataChangedHandler>>,
165 attributes_changed: Mutex<Option<AttributesChangedHandler>>,
166 name_changed: Mutex<Option<NameChangedHandler>>,
167 encryption_status_changed: Mutex<Option<EncryptionStatusChangedHandler>>,
168 permission_changed: Mutex<Option<PermissionChangedHandler>>,
169}
170
171pub(super) struct ParticipantInner {
172 rtc_engine: Arc<RtcEngine>,
173 info: RwLock<ParticipantInfo>,
174 track_publications: RwLock<HashMap<TrackSid, TrackPublication>>,
175 events: Arc<ParticipantEvents>,
176 is_encrypted: RwLock<bool>,
177 is_data_encrypted: RwLock<Option<bool>>,
178}
179
180#[derive(Clone)]
181pub struct ParticipantTrackPermission {
182 pub participant_identity: ParticipantIdentity,
183 pub allow_all: bool,
184 pub allowed_track_sids: Vec<TrackSid>,
185}
186
187pub(super) fn new_inner(
188 rtc_engine: Arc<RtcEngine>,
189 sid: ParticipantSid,
190 identity: ParticipantIdentity,
191 name: String,
192 state: ParticipantState,
193 metadata: String,
194 attributes: HashMap<String, String>,
195 kind: ParticipantKind,
196 kind_details: Vec<ParticipantKindDetail>,
197 joined_at: i64,
198 permission: Option<proto::ParticipantPermission>,
199 client_protocol: i32,
200) -> Arc<ParticipantInner> {
201 Arc::new(ParticipantInner {
202 rtc_engine,
203 info: RwLock::new(ParticipantInfo {
204 sid,
205 identity,
206 name,
207 state,
208 metadata,
209 attributes,
210 kind,
211 kind_details,
212 speaking: false,
213 audio_level: 0.0,
214 connection_quality: ConnectionQuality::Excellent,
215 disconnect_reason: DisconnectReason::UnknownReason,
216 joined_at,
217 permission,
218 client_protocol,
219 }),
220 track_publications: Default::default(),
221 events: Default::default(),
222 is_encrypted: RwLock::new(false),
223 is_data_encrypted: RwLock::new(None),
224 })
225}
226
227pub(super) fn update_info(
228 inner: &Arc<ParticipantInner>,
229 participant: &Participant,
230 new_info: proto::ParticipantInfo,
231) {
232 let mut info = inner.info.write();
233 info.state = new_info.state().into();
234 info.disconnect_reason = new_info.disconnect_reason().into();
235 info.kind = new_info.kind().into();
236 info.kind_details = crate::utils::convert_kind_details(&new_info.kind_details);
237 info.sid = new_info.sid.try_into().unwrap();
238 info.identity = new_info.identity.into();
239 info.joined_at = new_info.joined_at_ms;
240
241 let old_name = std::mem::replace(&mut info.name, new_info.name.clone());
242 if old_name != new_info.name {
243 if let Some(cb) = inner.events.name_changed.lock().as_ref() {
244 cb(participant.clone(), old_name, new_info.name);
245 }
246 }
247
248 let old_metadata = std::mem::replace(&mut info.metadata, new_info.metadata.clone());
249 if old_metadata != new_info.metadata {
250 if let Some(cb) = inner.events.metadata_changed.lock().as_ref() {
251 cb(participant.clone(), old_metadata, new_info.metadata);
252 }
253 }
254
255 let old_attributes = std::mem::replace(&mut info.attributes, new_info.attributes.clone());
256 let changed_attributes =
257 crate::utils::calculate_changed_attributes(old_attributes, new_info.attributes.clone());
258 if changed_attributes.len() != 0 {
259 if let Some(cb) = inner.events.attributes_changed.lock().as_ref() {
260 cb(participant.clone(), changed_attributes);
261 }
262 }
263
264 let old_permission = std::mem::replace(&mut info.permission, new_info.permission.clone());
265 if old_permission != new_info.permission {
266 if let Some(cb) = inner.events.permission_changed.lock().as_ref() {
267 cb(participant.clone(), new_info.permission.clone());
268 }
269 }
270
271 info.client_protocol = new_info.client_protocol;
272}
273
274pub(super) fn set_speaking(
275 inner: &Arc<ParticipantInner>,
276 _participant: &Participant,
277 speaking: bool,
278) {
279 inner.info.write().speaking = speaking;
280}
281
282pub(super) fn set_audio_level(
283 inner: &Arc<ParticipantInner>,
284 _participant: &Participant,
285 audio_level: f32,
286) {
287 inner.info.write().audio_level = audio_level;
288}
289
290pub(super) fn set_connection_quality(
291 inner: &Arc<ParticipantInner>,
292 _participant: &Participant,
293 quality: ConnectionQuality,
294) {
295 inner.info.write().connection_quality = quality;
296}
297
298pub(super) fn on_track_muted(
299 inner: &Arc<ParticipantInner>,
300 handler: impl Fn(Participant, TrackPublication) + Send + 'static,
301) {
302 *inner.events.track_muted.lock() = Some(Box::new(handler));
303}
304
305pub(super) fn on_track_unmuted(
306 inner: &Arc<ParticipantInner>,
307 handler: impl Fn(Participant, TrackPublication) + Send + 'static,
308) {
309 *inner.events.track_unmuted.lock() = Some(Box::new(handler));
310}
311
312pub(super) fn on_metadata_changed(
313 inner: &Arc<ParticipantInner>,
314 handler: impl Fn(Participant, String, String) + Send + 'static,
315) {
316 *inner.events.metadata_changed.lock() = Some(Box::new(handler));
317}
318
319pub(super) fn on_name_changed(
320 inner: &Arc<ParticipantInner>,
321 handler: impl Fn(Participant, String, String) + Send + 'static,
322) {
323 *inner.events.name_changed.lock() = Some(Box::new(handler));
324}
325
326pub(super) fn on_attributes_changed(
327 inner: &Arc<ParticipantInner>,
328 handler: impl Fn(Participant, HashMap<String, String>) + Send + 'static,
329) {
330 *inner.events.attributes_changed.lock() = Some(Box::new(handler));
331}
332
333pub(super) fn on_encryption_status_changed(
334 inner: &Arc<ParticipantInner>,
335 handler: impl Fn(Participant, bool) + Send + 'static,
336) {
337 *inner.events.encryption_status_changed.lock() = Some(Box::new(handler));
338}
339
340pub(super) fn on_permission_changed(
341 inner: &Arc<ParticipantInner>,
342 handler: impl Fn(Participant, Option<proto::ParticipantPermission>) + Send + 'static,
343) {
344 *inner.events.permission_changed.lock() = Some(Box::new(handler));
345}
346
347pub(super) fn update_encryption_status(inner: &Arc<ParticipantInner>, participant: &Participant) {
348 use crate::e2ee::EncryptionType;
349
350 let track_publications = inner.track_publications.read();
351 let data_encryption_status = inner.is_data_encrypted.read();
352
353 let tracks_encrypted = !track_publications.is_empty()
355 && track_publications.values().all(|pub_| pub_.encryption_type() != EncryptionType::None);
356
357 let is_encrypted = match *data_encryption_status {
359 Some(data_encrypted) => tracks_encrypted && data_encrypted,
360 None => tracks_encrypted, };
362
363 let mut current_status = inner.is_encrypted.write();
364 if *current_status != is_encrypted {
365 *current_status = is_encrypted;
366 drop(current_status);
367 drop(track_publications);
368 drop(data_encryption_status);
369
370 if let Some(cb) = inner.events.encryption_status_changed.lock().as_ref() {
371 cb(participant.clone(), is_encrypted);
372 }
373 }
374}
375
376pub(super) fn update_data_encryption_status(
377 inner: &Arc<ParticipantInner>,
378 participant: &Participant,
379 is_encrypted: bool,
380) {
381 let mut data_encryption_status = inner.is_data_encrypted.write();
382 let previous_status = *data_encryption_status;
383
384 match previous_status {
385 Some(current) if current == is_encrypted => {
386 return;
388 }
389 Some(true) if !is_encrypted => {
390 *data_encryption_status = Some(false);
392 }
393 Some(false) if is_encrypted => {
394 return;
397 }
398 None => {
399 *data_encryption_status = Some(is_encrypted);
401 }
402 _ => return,
403 }
404
405 drop(data_encryption_status);
406
407 update_encryption_status(inner, participant);
409}
410
411pub(super) fn remove_publication(
412 inner: &Arc<ParticipantInner>,
413 participant: &Participant,
414 sid: &TrackSid,
415) -> Option<TrackPublication> {
416 let mut tracks = inner.track_publications.write();
417 let publication = tracks.remove(sid);
418 if let Some(publication) = publication.clone() {
419 publication.on_muted(|_| {});
421 publication.on_unmuted(|_| {});
422 } else {
423 log::warn!("could not find publication to remove: {:?}", sid);
425 }
426 drop(tracks);
427
428 update_encryption_status(inner, participant);
430
431 publication
432}
433
434pub(super) fn add_publication(
435 inner: &Arc<ParticipantInner>,
436 participant: &Participant,
437 publication: TrackPublication,
438) {
439 let mut tracks = inner.track_publications.write();
440 tracks.insert(publication.sid(), publication.clone());
441
442 publication.on_muted({
443 let events = inner.events.clone();
444 let participant = participant.clone();
445 let rtc_engine = inner.rtc_engine.clone();
446 move |publication| {
447 if let Some(cb) = events.track_muted.lock().as_ref() {
448 if !publication.is_remote() {
449 let rtc_engine = rtc_engine.clone();
450 let publication_cloned = publication.clone();
451 livekit_runtime::spawn(async move {
452 let engine_request = rtc_engine
453 .mute_track(proto::MuteTrackRequest {
454 sid: publication_cloned.sid().to_string(),
455 muted: true,
456 })
457 .await;
458 if let Err(e) = engine_request {
459 log::error!("could not mute track: {e:?}");
460 }
461 });
462 }
463 cb(participant.clone(), publication);
464 }
465 }
466 });
467
468 publication.on_unmuted({
469 let events = inner.events.clone();
470 let participant = participant.clone();
471 let rtc_engine = inner.rtc_engine.clone();
472 move |publication| {
473 if let Some(cb) = events.track_unmuted.lock().as_ref() {
474 if !publication.is_remote() {
475 let rtc_engine = rtc_engine.clone();
476 let publication_cloned = publication.clone();
477 livekit_runtime::spawn(async move {
478 let engine_request = rtc_engine
479 .mute_track(proto::MuteTrackRequest {
480 sid: publication_cloned.sid().to_string(),
481 muted: false,
482 })
483 .await;
484 if let Err(e) = engine_request {
485 log::error!("could not unmute track: {e:?}");
486 }
487 });
488 }
489 cb(participant.clone(), publication);
490 }
491 }
492 });
493 drop(tracks);
494
495 update_encryption_status(inner, participant);
497}