Skip to main content

gosuto_livekit/room/publication/
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::sync::Arc;
16
17use livekit_protocol::enum_dispatch;
18use livekit_protocol::{self as proto, AudioTrackFeature};
19use parking_lot::{Mutex, RwLock};
20
21use super::track::TrackDimension;
22use crate::{e2ee::EncryptionType, prelude::*, track::Track};
23
24mod local;
25mod remote;
26
27pub use local::*;
28pub use remote::*;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum SubscriptionStatus {
32    Desired,
33    Subscribed,
34    Unsubscribed,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum PermissionStatus {
39    Allowed,
40    NotAllowed,
41}
42
43#[derive(Clone, Debug)]
44pub enum TrackPublication {
45    Local(LocalTrackPublication),
46    Remote(RemoteTrackPublication),
47}
48
49impl TrackPublication {
50    enum_dispatch!(
51        [Local, Remote];
52        pub fn sid(self: &Self) -> TrackSid;
53        pub fn name(self: &Self) -> String;
54        pub fn kind(self: &Self) -> TrackKind;
55        pub fn source(self: &Self) -> TrackSource;
56        pub fn simulcasted(self: &Self) -> bool;
57        pub fn dimension(self: &Self) -> TrackDimension;
58        pub fn mime_type(self: &Self) -> String;
59        pub fn is_muted(self: &Self) -> bool;
60        pub fn is_remote(self: &Self) -> bool;
61        pub fn encryption_type(self: &Self) -> EncryptionType;
62        pub fn audio_features(self: &Self) -> Vec<AudioTrackFeature>;
63
64        pub(crate) fn on_muted(self: &Self, on_mute: impl Fn(TrackPublication) + Send + 'static) -> ();
65        pub(crate) fn on_unmuted(self: &Self, on_unmute: impl Fn(TrackPublication) + Send + 'static) -> ();
66        pub(crate) fn proto_info(self: &Self) -> proto::TrackInfo;
67        pub(crate) fn update_info(self: &Self, info: proto::TrackInfo) -> ();
68    );
69
70    #[allow(dead_code)]
71    pub(crate) fn set_track(&self, track: Option<Track>) {
72        match self {
73            TrackPublication::Local(p) => p.set_track(track),
74            TrackPublication::Remote(p) => p.set_track(track.map(|t| t.try_into().unwrap())),
75        }
76    }
77
78    pub fn track(&self) -> Option<Track> {
79        match self {
80            TrackPublication::Local(p) => p.track().map(Into::into),
81            TrackPublication::Remote(p) => p.track().map(Into::into),
82        }
83    }
84}
85
86struct PublicationInfo {
87    pub track: Option<Track>,
88    pub name: String,
89    pub sid: TrackSid,
90    pub kind: TrackKind,
91    pub source: TrackSource,
92    pub simulcasted: bool,
93    pub dimension: TrackDimension,
94    pub mime_type: String,
95    pub muted: bool,
96    pub proto_info: proto::TrackInfo,
97    pub encryption_type: EncryptionType,
98    pub audio_features: Vec<AudioTrackFeature>,
99}
100
101pub(crate) type MutedHandler = Box<dyn Fn(TrackPublication) + Send>;
102pub(crate) type UnmutedHandler = Box<dyn Fn(TrackPublication) + Send>;
103
104#[derive(Default)]
105struct PublicationEvents {
106    muted: Mutex<Option<MutedHandler>>,
107    unmuted: Mutex<Option<UnmutedHandler>>,
108}
109
110pub(super) struct TrackPublicationInner {
111    info: RwLock<PublicationInfo>,
112    events: Arc<PublicationEvents>,
113}
114
115pub(super) fn new_inner(
116    info: proto::TrackInfo,
117    track: Option<Track>,
118) -> Arc<TrackPublicationInner> {
119    let info = PublicationInfo {
120        track,
121        proto_info: info.clone(),
122        source: info.source().into(),
123        kind: info.r#type().try_into().unwrap(),
124        encryption_type: info.encryption().into(),
125        name: info.clone().name,
126        sid: info.sid.clone().try_into().unwrap(),
127        simulcasted: info.simulcast,
128        dimension: TrackDimension(info.width, info.height),
129        mime_type: info.mime_type.clone(),
130        muted: info.muted,
131        audio_features: info
132            .audio_features()
133            .into_iter()
134            .map(|item| item.try_into().unwrap())
135            .collect(),
136    };
137
138    Arc::new(TrackPublicationInner { info: RwLock::new(info), events: Default::default() })
139}
140
141pub(super) fn update_info(
142    inner: &TrackPublicationInner,
143    _publication: &TrackPublication,
144    new_info: proto::TrackInfo,
145) {
146    let mut info = inner.info.write();
147    info.kind = TrackKind::try_from(new_info.r#type()).unwrap();
148    info.source = TrackSource::from(new_info.source());
149    info.encryption_type = new_info.encryption().into();
150    info.proto_info = new_info.clone();
151    info.name = new_info.name.clone();
152    info.sid = new_info.sid.clone().try_into().unwrap();
153    info.dimension = TrackDimension(new_info.width, new_info.height);
154    info.mime_type = new_info.mime_type.clone();
155    info.simulcasted = new_info.simulcast;
156    info.audio_features = new_info.audio_features().collect();
157}
158
159pub(super) fn set_track(
160    inner: &TrackPublicationInner,
161    publication: &TrackPublication,
162    track: Option<Track>,
163) {
164    let mut info = inner.info.write();
165    if let Some(prev_track) = info.track.as_ref() {
166        prev_track.on_muted(|_| {});
167        prev_track.on_unmuted(|_| {});
168    }
169
170    info.track = track.clone();
171
172    if let Some(track) = track.as_ref() {
173        info.sid = track.sid();
174
175        track.on_muted({
176            let events = inner.events.clone();
177            let publication = publication.clone();
178            move |_| {
179                if let Some(on_muted) = events.muted.lock().as_ref() {
180                    on_muted(publication.clone());
181                }
182            }
183        });
184
185        track.on_unmuted({
186            let events = inner.events.clone();
187            let publication = publication.clone();
188            move |_| {
189                if let Some(on_unmuted) = events.unmuted.lock().as_ref() {
190                    on_unmuted(publication.clone());
191                }
192            }
193        });
194    }
195}