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