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