Skip to main content

o_sfu_core/engine/source_model/
intent.rs

1use o_sfu_router::MediaKind;
2
3use super::{SourcePolicy, UserStreamId};
4use crate::engine::{UserInfo, VideoLayoutIntent};
5
6/// Publish intent for one user stream.
7///
8/// Application code passes this into core when a user starts publishing. It
9/// carries stream identity, media kind, room policy and optional publication
10/// presence. Core captures these values when the staged publish commits.
11///
12/// Compatibility concepts such as "camera" or "screen" must be translated into
13/// this type before entering core. If a product stream needs different layout
14/// or bandwidth behavior, change the application catalog that builds this intent
15/// instead of adding stream-specific branches to room state.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct SourcePublishIntent {
18    stream_id: UserStreamId,
19    media_kind: MediaKind,
20    policy: SourcePolicy,
21    presence: Option<UserInfo>,
22}
23
24impl SourcePublishIntent {
25    #[must_use]
26    pub fn new(stream_id: UserStreamId, media_kind: MediaKind, policy: SourcePolicy) -> Self {
27        Self {
28            stream_id,
29            media_kind,
30            policy,
31            presence: None,
32        }
33    }
34
35    #[must_use]
36    pub fn with_presence(mut self, presence: Option<UserInfo>) -> Self {
37        self.presence = presence;
38        self
39    }
40
41    #[must_use]
42    pub const fn stream_id(&self) -> &UserStreamId {
43        &self.stream_id
44    }
45
46    #[must_use]
47    pub const fn media_kind(&self) -> MediaKind {
48        self.media_kind
49    }
50
51    #[must_use]
52    pub const fn policy(&self) -> SourcePolicy {
53        self.policy
54    }
55
56    #[must_use]
57    pub const fn presence(&self) -> Option<&UserInfo> {
58        self.presence.as_ref()
59    }
60}
61
62/// Deactivation intent for one user stream.
63///
64/// Pending first publication is cancelled. A committed publication keeps its
65/// source identity and negotiated media until session teardown.
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct SourceDeactivateIntent {
68    stream_id: UserStreamId,
69    presence: Option<UserInfo>,
70}
71
72impl SourceDeactivateIntent {
73    #[must_use]
74    pub fn new(stream_id: UserStreamId) -> Self {
75        Self {
76            stream_id,
77            presence: None,
78        }
79    }
80
81    #[must_use]
82    pub fn with_presence(mut self, presence: Option<UserInfo>) -> Self {
83        self.presence = presence;
84        self
85    }
86
87    #[must_use]
88    pub const fn stream_id(&self) -> &UserStreamId {
89        &self.stream_id
90    }
91
92    #[must_use]
93    pub const fn presence(&self) -> Option<&UserInfo> {
94        self.presence.as_ref()
95    }
96}
97
98/// Per-source subscription update submitted by the caller.
99///
100/// This is the core shape for receiver download intent. Compatibility code
101/// decides which stream ids to include. Core merges partial
102/// updates by stream id and applies the resulting active or layout preference
103/// to current and later consumer routes.
104#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
105pub struct SourceSubscriptionIntent {
106    active: Option<bool>,
107    layout: Option<VideoLayoutIntent>,
108}
109
110impl SourceSubscriptionIntent {
111    #[must_use]
112    pub const fn new(active: Option<bool>, layout: Option<VideoLayoutIntent>) -> Self {
113        Self { active, layout }
114    }
115
116    #[must_use]
117    pub const fn active(self) -> Option<bool> {
118        self.active
119    }
120
121    #[must_use]
122    pub const fn layout(self) -> Option<VideoLayoutIntent> {
123        self.layout
124    }
125
126    #[must_use]
127    pub const fn is_empty(self) -> bool {
128        self.active.is_none() && self.layout.is_none()
129    }
130
131    /// Applies a sparse subscription update.
132    ///
133    /// `None` preserves the current field so `active` and `layout` can arrive
134    /// independently without resetting the other preference.
135    pub const fn merge(&mut self, update: Self) {
136        if update.active.is_some() {
137            self.active = update.active;
138        }
139        if update.layout.is_some() {
140            self.layout = update.layout;
141        }
142    }
143}