o_sfu/application/
stream_catalog.rs1use std::collections::BTreeMap;
2
3use o_sfu_protocol::wire::{DownloadStates, StreamType, UserInfo};
4use o_sfu_router::MediaKind;
5
6use crate::core::prelude::{
7 ActiveSpeakerGroup, ActiveSpeakerPolicy, ActiveSpeakerSourceRole, SourceAdaptationPolicy,
8 SourceDeactivateIntent, SourceLayoutPolicy, SourcePolicy, SourcePublishIntent,
9 SourceRoomPolicySelector, SourceSubscriptionIntent, UserStreamId,
10};
11
12pub(crate) const AUDIO_STREAM_LABEL: &str = "audio";
13pub(crate) const CAMERA_STREAM_LABEL: &str = "camera";
14pub(crate) const SCREEN_STREAM_LABEL: &str = "screen";
15
16const DISCUSS_STREAMS: [DiscussStream; 3] = [
17 DiscussStream {
18 stream_type: StreamType::Audio,
19 label: AUDIO_STREAM_LABEL,
20 media_kind: MediaKind::Audio,
21 policy: SourcePolicy::new(
22 None,
23 SourceAdaptationPolicy::None,
24 Some(ActiveSpeakerPolicy::new(
25 ActiveSpeakerGroup::MAIN,
26 ActiveSpeakerSourceRole::Detector,
27 )),
28 ),
29 },
30 DiscussStream {
31 stream_type: StreamType::Camera,
32 label: CAMERA_STREAM_LABEL,
33 media_kind: MediaKind::Video,
34 policy: SourcePolicy::new(
35 Some(SourceLayoutPolicy::new(
36 SourceRoomPolicySelector::VisibleThumbnail,
37 Some(SourceRoomPolicySelector::ActiveSpeaker),
38 )),
39 SourceAdaptationPolicy::ScalableVideo,
40 Some(ActiveSpeakerPolicy::new(
41 ActiveSpeakerGroup::MAIN,
42 ActiveSpeakerSourceRole::Promotable,
43 )),
44 ),
45 },
46 DiscussStream {
47 stream_type: StreamType::Screen,
48 label: SCREEN_STREAM_LABEL,
49 media_kind: MediaKind::Video,
50 policy: SourcePolicy::new(
51 Some(SourceLayoutPolicy::new(
52 SourceRoomPolicySelector::ReadableDetail,
53 None,
54 )),
55 SourceAdaptationPolicy::ReadableDetail,
56 None,
57 ),
58 },
59];
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub(crate) struct DiscussStream {
63 stream_type: StreamType,
64 label: &'static str,
65 media_kind: MediaKind,
66 policy: SourcePolicy,
67}
68
69impl DiscussStream {
70 pub(crate) fn all() -> impl Iterator<Item = Self> {
71 DISCUSS_STREAMS.into_iter()
72 }
73
74 pub(crate) const fn for_type(stream_type: StreamType) -> Self {
75 match stream_type {
76 StreamType::Audio => DISCUSS_STREAMS[0],
77 StreamType::Camera => DISCUSS_STREAMS[1],
78 StreamType::Screen => DISCUSS_STREAMS[2],
79 }
80 }
81
82 pub(crate) fn for_stream_id(stream_id: &UserStreamId) -> Option<Self> {
83 match stream_id.as_str() {
84 AUDIO_STREAM_LABEL => Some(Self::for_type(StreamType::Audio)),
85 CAMERA_STREAM_LABEL => Some(Self::for_type(StreamType::Camera)),
86 SCREEN_STREAM_LABEL => Some(Self::for_type(StreamType::Screen)),
87 _ => None,
88 }
89 }
90
91 pub(crate) fn stream_id(self) -> UserStreamId {
92 UserStreamId::new(self.label)
93 }
94
95 pub(crate) fn publish_intent(self) -> SourcePublishIntent {
96 SourcePublishIntent::new(self.stream_id(), self.media_kind, self.policy)
97 .with_presence(self.publication_presence(true))
98 }
99
100 pub(crate) fn deactivate_intent(self) -> SourceDeactivateIntent {
101 SourceDeactivateIntent::new(self.stream_id())
102 .with_presence(self.publication_presence(false))
103 }
104
105 pub(crate) fn subscription_intent_if_requested(
106 self,
107 states: &DownloadStates,
108 ) -> Option<(UserStreamId, SourceSubscriptionIntent)> {
109 let (active, layout) = match self.stream_type {
110 StreamType::Audio => (states.audio, None),
111 StreamType::Camera => (states.camera, states.camera_layout),
112 StreamType::Screen => (states.screen, states.screen_layout),
113 };
114 let intent = SourceSubscriptionIntent::new(active, layout);
115 (!intent.is_empty()).then(|| (self.stream_id(), intent))
116 }
117
118 fn publication_presence(self, active: bool) -> Option<UserInfo> {
119 match self.stream_type {
120 StreamType::Audio => None,
121 StreamType::Camera => Some(UserInfo {
122 is_camera_on: Some(active),
123 ..UserInfo::default()
124 }),
125 StreamType::Screen => Some(UserInfo {
126 is_screen_sharing_on: Some(active),
127 ..UserInfo::default()
128 }),
129 }
130 }
131}
132
133pub(crate) fn source_publish_intent_for_stream_type(
134 stream_type: StreamType,
135) -> SourcePublishIntent {
136 DiscussStream::for_type(stream_type).publish_intent()
137}
138
139pub(crate) fn stream_id_for_stream_type(stream_type: StreamType) -> UserStreamId {
140 DiscussStream::for_type(stream_type).stream_id()
141}
142
143pub(crate) fn stream_type_for_stream_id(stream_id: &UserStreamId) -> Option<StreamType> {
144 DiscussStream::for_stream_id(stream_id).map(|stream| stream.stream_type)
145}
146
147pub(crate) fn counter_for_stream_type(
148 by_stream: &BTreeMap<UserStreamId, u64>,
149 stream_type: StreamType,
150) -> u64 {
151 by_stream
152 .get(&stream_id_for_stream_type(stream_type))
153 .copied()
154 .unwrap_or(0)
155}
156
157#[cfg(test)]
158#[path = "TESTS/stream_catalog.rs"]
159mod tests;