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    pressure_observations: u8,
53    upgrade_observations: u8,
54}
55
56impl ConsumerSourceSelection {
57    #[must_use]
58    pub const fn open(active: bool) -> Self {
59        Self {
60            active,
61            selector: SourceSelector::Open,
62            policy_pause_reason: None,
63            budget: ReceiverVideoBudgetDiagnostics::new(None, None, 0, Bitrate::zero()),
64            pressure_observations: 0,
65            upgrade_observations: 0,
66        }
67    }
68
69    #[must_use]
70    pub const fn active(self) -> bool {
71        self.active
72    }
73
74    #[must_use]
75    pub const fn selector(self) -> SourceSelector {
76        self.selector
77    }
78
79    #[must_use]
80    pub const fn policy_pause_reason(self) -> Option<PolicyPauseReason> {
81        self.policy_pause_reason
82    }
83
84    #[must_use]
85    pub const fn policy_allows_delivery(self) -> bool {
86        self.policy_pause_reason.is_none()
87    }
88
89    /// Returns whether this receiver selection currently permits packet delivery.
90    ///
91    /// Use this for route-state projections, load accounting and keyframe
92    /// targeting. Source-policy planners should read [`Self::active`] so
93    /// policy-paused routes can be resumed.
94    #[must_use]
95    pub const fn delivery_active(self) -> bool {
96        self.active && self.policy_allows_delivery()
97    }
98
99    #[must_use]
100    pub const fn budget(self) -> ReceiverVideoBudgetDiagnostics {
101        self.budget
102    }
103
104    #[must_use]
105    pub const fn pressure_observations(self) -> u8 {
106        self.pressure_observations
107    }
108
109    #[must_use]
110    pub const fn upgrade_observations(self) -> u8 {
111        self.upgrade_observations
112    }
113
114    pub const fn set_active(&mut self, active: bool) {
115        self.active = active;
116    }
117
118    pub const fn set_selector(&mut self, selector: SourceSelector) {
119        self.selector = selector;
120    }
121
122    pub const fn set_policy_pause_reason(&mut self, reason: Option<PolicyPauseReason>) {
123        self.policy_pause_reason = reason;
124    }
125
126    pub const fn set_budget(&mut self, budget: ReceiverVideoBudgetDiagnostics) {
127        self.budget = budget;
128    }
129
130    pub const fn set_adaptation_observations(
131        &mut self,
132        pressure_observations: u8,
133        upgrade_observations: u8,
134    ) {
135        self.pressure_observations = pressure_observations;
136        self.upgrade_observations = upgrade_observations;
137    }
138}