gosuto_livekit/room/track/
mod.rs1use std::{fmt::Debug, sync::Arc};
16
17use gosuto_libwebrtc::{prelude::*, stats::RtcStats};
18use livekit_protocol::enum_dispatch;
19use livekit_protocol::{self as proto};
20use parking_lot::{Mutex, RwLock};
21use thiserror::Error;
22
23use crate::prelude::*;
24
25mod audio_track;
26mod local_audio_track;
27mod local_track;
28mod local_video_track;
29mod remote_audio_track;
30mod remote_track;
31mod remote_video_track;
32mod video_track;
33
34pub use audio_track::*;
35pub use local_audio_track::*;
36pub use local_track::*;
37pub use local_video_track::*;
38pub use remote_audio_track::*;
39pub use remote_track::*;
40pub use remote_video_track::*;
41pub use video_track::*;
42
43#[derive(Error, Debug, Clone)]
44pub enum TrackError {
45 #[error("could not find published track with sid: {0:?}")]
46 TrackNotFound(TrackSid),
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum TrackKind {
51 Audio,
52 Video,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum StreamState {
57 Active,
58 Paused,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum TrackSource {
63 Unknown,
64 Camera,
65 Microphone,
66 Screenshare,
67 ScreenshareAudio,
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct TrackDimension(pub u32, pub u32);
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
75pub enum VideoQuality {
76 Low,
77 Medium,
78 High,
79}
80
81macro_rules! track_dispatch {
82 ([$($variant:ident),+]) => {
83 enum_dispatch!(
84 [$($variant),+];
85 pub fn sid(self: &Self) -> TrackSid;
86 pub fn name(self: &Self) -> String;
87 pub fn kind(self: &Self) -> TrackKind;
88 pub fn source(self: &Self) -> TrackSource;
89 pub fn stream_state(self: &Self) -> StreamState;
90 pub fn is_enabled(self: &Self) -> bool;
91 pub fn enable(self: &Self) -> ();
92 pub fn disable(self: &Self) -> ();
93 pub fn is_muted(self: &Self) -> bool;
94 pub fn is_remote(self: &Self) -> bool;
95 pub fn on_muted(self: &Self, on_mute: impl Fn(Track) + Send + 'static) -> ();
96 pub fn on_unmuted(self: &Self, on_unmute: impl Fn(Track) + Send + 'static) -> ();
97
98 pub(crate) fn transceiver(self: &Self) -> Option<RtpTransceiver>;
99 pub(crate) fn set_transceiver(self: &Self, transceiver: Option<RtpTransceiver>) -> ();
100 pub(crate) fn update_info(self: &Self, info: proto::TrackInfo) -> ();
101 );
102 };
103}
104
105#[derive(Clone, Debug)]
106pub enum Track {
107 LocalAudio(LocalAudioTrack),
108 LocalVideo(LocalVideoTrack),
109 RemoteAudio(RemoteAudioTrack),
110 RemoteVideo(RemoteVideoTrack),
111}
112
113impl Track {
114 track_dispatch!([LocalAudio, LocalVideo, RemoteAudio, RemoteVideo]);
115
116 pub fn rtc_track(&self) -> MediaStreamTrack {
117 match self {
118 Self::LocalAudio(track) => track.rtc_track().into(),
119 Self::LocalVideo(track) => track.rtc_track().into(),
120 Self::RemoteAudio(track) => track.rtc_track().into(),
121 Self::RemoteVideo(track) => track.rtc_track().into(),
122 }
123 }
124
125 pub async fn get_stats(&self) -> RoomResult<Vec<RtcStats>> {
126 match self {
127 Self::LocalAudio(track) => track.get_stats().await,
128 Self::LocalVideo(track) => track.get_stats().await,
129 Self::RemoteAudio(track) => track.get_stats().await,
130 Self::RemoteVideo(track) => track.get_stats().await,
131 }
132 }
133}
134
135pub(super) use track_dispatch;
136
137type MutedHandler = Box<dyn Fn(Track) + Send>;
138type UnmutedHandler = Box<dyn Fn(Track) + Send>;
139
140#[derive(Default)]
141struct TrackEvents {
142 pub muted: Option<MutedHandler>,
144 pub unmuted: Option<UnmutedHandler>,
145}
146
147#[derive(Debug)]
148struct TrackInfo {
149 pub sid: TrackSid,
150 pub name: String,
151 pub kind: TrackKind,
152 pub source: TrackSource,
153 pub stream_state: StreamState,
154 pub muted: bool,
155 pub transceiver: Option<RtpTransceiver>,
156 pub audio_features: Vec<proto::AudioTrackFeature>,
157}
158
159pub(super) struct TrackInner {
160 info: RwLock<TrackInfo>,
161 rtc_track: MediaStreamTrack,
162 events: Mutex<TrackEvents>,
163}
164
165pub(super) fn new_inner(
166 sid: TrackSid,
167 name: String,
168 kind: TrackKind,
169 rtc_track: MediaStreamTrack,
170) -> TrackInner {
171 TrackInner {
172 info: RwLock::new(TrackInfo {
173 sid,
174 name,
175 kind,
176 source: TrackSource::Unknown,
177 stream_state: StreamState::Active,
178 muted: false,
179 transceiver: None,
180 audio_features: Vec::new(),
181 }),
182 rtc_track,
183 events: Default::default(),
184 }
185}
186
187pub(super) fn set_muted(inner: &Arc<TrackInner>, track: &Track, muted: bool) {
189 let info = inner.info.read();
190 if info.muted == muted {
191 return;
192 }
193 drop(info);
194
195 if muted {
196 inner.rtc_track.set_enabled(false);
197 } else {
198 inner.rtc_track.set_enabled(true);
199 }
200
201 inner.info.write().muted = muted;
202
203 if muted {
204 if let Some(on_mute) = inner.events.lock().muted.as_ref() {
205 on_mute(track.clone());
206 }
207 } else if let Some(on_unmute) = inner.events.lock().unmuted.as_ref() {
208 on_unmute(track.clone());
209 }
210}
211
212pub(super) fn update_info(inner: &Arc<TrackInner>, _track: &Track, new_info: proto::TrackInfo) {
213 let mut info = inner.info.write();
214 info.kind = TrackKind::try_from(new_info.r#type()).unwrap();
215 info.source = TrackSource::from(new_info.source());
216 info.name = new_info.name.clone();
217 info.sid = new_info.sid.clone().try_into().unwrap();
218 info.audio_features =
219 new_info.audio_features().into_iter().map(|item| item.try_into().unwrap()).collect();
220}