Skip to main content

gosuto_livekit/room/track/
mod.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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/// Video quality for simulcasted tracks.
74#[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    // These mute handlers are only called for local tracks
143    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
187/// This is only called for local tracks
188pub(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}