Skip to main content

o_sfu_core/engine/source_model/
selection.rs

1use super::{PolicyPauseReason, ReceiverVideoBudgetDiagnostics, SourceEncodingId};
2use crate::Bitrate;
3
4/// Resolved packet-selection command for one consumer/source route.
5///
6/// The budget planner writes selectors into room state. A later projection step
7/// turns them into transport packet gates such as "open" or "forward this RID".
8/// # Example situations
9///
10/// [`Self::Open`] means the route has no source-level packet gate.
11/// [`Self::Encoding`] means "forward the negotiated RID for this encoding".
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum SourceSelector {
14    /// Forward the source without a source-level packet gate.
15    ///
16    /// This is the default for sources that are not controlled by receiver-video
17    /// adaptation or when the planner has not selected a narrower gate.
18    #[default]
19    Open,
20    /// Forward only one advertised source encoding.
21    ///
22    /// Projection maps the encoding id to its negotiated RID. If the encoding
23    /// has no RID, projection fails rather than guessing at packet identity.
24    Encoding(SourceEncodingId),
25}
26
27impl SourceSelector {
28    #[must_use]
29    pub const fn selected_encoding(self) -> Option<SourceEncodingId> {
30        match self {
31            Self::Encoding(encoding_id) => Some(encoding_id),
32            Self::Open => None,
33        }
34    }
35}
36
37/// Receiver-side policy state for one attached publication.
38///
39/// `active` preserves stored subscription intent while `policy_pause_reason`
40/// may withhold delivery without erasing that intent. `selector` is a resolved
41/// room choice that projection maps to a transport packet gate.
42///
43/// `ConsumerSourceSelection` carries no publication or route identity. Async
44/// updates must still match the current `PublishedSourceId` and exact consumer
45/// route.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct ConsumerSourceSelection {
48    active: bool,
49    selector: SourceSelector,
50    policy_pause_reason: Option<PolicyPauseReason>,
51    budget: ReceiverVideoBudgetDiagnostics,
52}
53
54impl ConsumerSourceSelection {
55    #[must_use]
56    pub const fn open(active: bool) -> Self {
57        Self {
58            active,
59            selector: SourceSelector::Open,
60            policy_pause_reason: None,
61            budget: ReceiverVideoBudgetDiagnostics::new(None, None, 0, Bitrate::zero()),
62        }
63    }
64
65    #[must_use]
66    pub const fn active(self) -> bool {
67        self.active
68    }
69
70    #[must_use]
71    pub const fn selector(self) -> SourceSelector {
72        self.selector
73    }
74
75    #[must_use]
76    pub const fn policy_pause_reason(self) -> Option<PolicyPauseReason> {
77        self.policy_pause_reason
78    }
79
80    #[must_use]
81    pub const fn policy_allows_delivery(self) -> bool {
82        self.policy_pause_reason.is_none()
83    }
84
85    /// Returns whether this receiver selection currently permits packet delivery.
86    ///
87    /// Use this for route-state projections, load accounting and keyframe
88    /// targeting. Source-policy planners should read [`Self::active`] so
89    /// policy-paused routes can be resumed.
90    #[must_use]
91    pub const fn delivery_active(self) -> bool {
92        self.active && self.policy_allows_delivery()
93    }
94
95    #[must_use]
96    pub const fn budget(self) -> ReceiverVideoBudgetDiagnostics {
97        self.budget
98    }
99
100    pub const fn set_active(&mut self, active: bool) {
101        self.active = active;
102    }
103
104    pub const fn set_selector(&mut self, selector: SourceSelector) {
105        self.selector = selector;
106    }
107
108    pub const fn set_policy_pause_reason(&mut self, reason: Option<PolicyPauseReason>) {
109        self.policy_pause_reason = reason;
110    }
111
112    pub const fn set_budget(&mut self, budget: ReceiverVideoBudgetDiagnostics) {
113        self.budget = budget;
114    }
115}