Skip to main content

o_sfu_core/engine/source_model/
ids.rs

1use std::fmt::{self, Display, Formatter};
2
3use serde::{Deserialize, Serialize};
4
5use crate::engine::UserId;
6
7/// User-scoped stream identity supplied with a publish intent.
8///
9/// The room treats this value as an opaque slot name scoped by the publishing
10/// user. Callers allocate the slot before entering core and pass media kind
11/// plus room-policy metadata through the publish intent.
12///
13/// # Invariant
14///
15/// `UserStreamId` is not globally unique. It is unique only together with the
16/// publishing user id. Room indexes that need publication identity must pair it
17/// with the publishing user or use [`PublishedSourceId`] after a publish
18/// commits.
19#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20#[serde(transparent)]
21pub struct UserStreamId(String);
22
23impl UserStreamId {
24    #[must_use]
25    pub fn new(value: impl Into<String>) -> Self {
26        Self(value.into())
27    }
28
29    #[must_use]
30    pub fn as_str(&self) -> &str {
31        &self.0
32    }
33}
34
35impl Display for UserStreamId {
36    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
37        formatter.write_str(self.as_str())
38    }
39}
40
41impl From<&str> for UserStreamId {
42    fn from(value: &str) -> Self {
43        Self::new(value)
44    }
45}
46
47impl From<String> for UserStreamId {
48    fn from(value: String) -> Self {
49        Self::new(value)
50    }
51}
52
53/// Stable room-domain identity for one logical published source.
54///
55/// A source id identifies the publication itself, not the SDP media section,
56/// negotiated RID, RTP SSRC or transport media handle currently realizing it.
57/// Room state should allocate it when a publish becomes a room-domain source
58/// and keep using it across renegotiation or transport reattachment.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub struct PublishedSourceId(u64);
61
62impl PublishedSourceId {
63    #[must_use]
64    pub fn allocate(next_source_id: &mut u64) -> Self {
65        let source_id = Self(*next_source_id);
66        *next_source_id = next_source_id.saturating_add(1);
67        source_id
68    }
69
70    #[cfg(any(test, feature = "testing-transport"))]
71    #[must_use]
72    pub const fn from_raw(raw: u64) -> Self {
73        Self(raw)
74    }
75
76    #[must_use]
77    pub const fn as_u64(self) -> u64 {
78        self.0
79    }
80}
81
82impl Display for PublishedSourceId {
83    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
84        write!(formatter, "source-{}", self.0)
85    }
86}
87
88/// Stable room-domain identity for one advertised source encoding
89///
90/// The id names an advertised encoding of a source such as a simulcast `lo` or
91/// `hi` layer. It does not encode a RID string or worker-local route so
92/// selectors can keep pointing at the same encoding after transport details are
93/// refreshed.
94#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub struct SourceEncodingId(u64);
96
97impl SourceEncodingId {
98    #[must_use]
99    pub fn allocate(next_encoding_id: &mut u64) -> Self {
100        let encoding_id = Self(*next_encoding_id);
101        *next_encoding_id = next_encoding_id.saturating_add(1);
102        encoding_id
103    }
104
105    #[cfg(any(test, feature = "testing-transport"))]
106    #[must_use]
107    pub const fn from_raw(raw: u64) -> Self {
108        Self(raw)
109    }
110
111    #[must_use]
112    pub const fn as_u64(self) -> u64 {
113        self.0
114    }
115}
116
117impl Display for SourceEncodingId {
118    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
119        write!(formatter, "encoding-{}", self.0)
120    }
121}
122
123/// Logical publishing user attached to a source descriptor.
124///
125/// The user identifies the logical owner visible to room policy. Connection
126/// freshness is tracked by producer and transport indexes, because source
127/// descriptors are room-domain metadata rather than async commit guards.
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct PublishedSourceOwner {
130    user_id: UserId,
131}
132
133impl PublishedSourceOwner {
134    #[must_use]
135    pub fn new(user_id: UserId) -> Self {
136        Self { user_id }
137    }
138
139    #[must_use]
140    pub fn user_id(&self) -> &UserId {
141        &self.user_id
142    }
143}