Skip to main content

o_sfu_protocol/signaling/
payloads.rs

1use std::num::NonZeroU16;
2
3use o_sfu_rfc::webrtc::MediaKind;
4use secrecy::{ExposeSecret, SecretString};
5use serde::{Deserialize, Serialize};
6
7use crate::shared::{
8    AvailableFeatures, DownloadStates, JsonPayload, PeerSnapshot, RecordingState, StreamType,
9    UserId, UserInfo,
10};
11
12/// The client's bearer JWT, carried in the clear (JWTs are only base64url
13/// encoded, not encrypted) as `SecretString` so it auto-redacts from `Debug`
14/// and zeroizes on drop instead of lingering in process memory as a plain
15/// `String`.
16///
17/// Every embedder of [`crate::host::ProtocolCore`] (browser, native, or test
18/// host) plays the client role and needs to serialize this payload to send
19/// its own token, which the `client` feature (on by default) provides.
20/// The one consumer that only ever decodes an `AuthPayload` it received is
21/// the SFU server itself, whose own dependency on this crate disables
22/// default features, so its build never compiles in the capability to
23/// serialize this type at all.
24#[derive(Debug, Clone, Deserialize)]
25pub struct AuthPayload {
26    pub jwt: SecretString,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub channel: Option<String>,
29}
30
31impl PartialEq for AuthPayload {
32    fn eq(&self, other: &Self) -> bool {
33        self.jwt.expose_secret() == other.jwt.expose_secret() && self.channel == other.channel
34    }
35}
36
37impl Eq for AuthPayload {}
38
39#[cfg(feature = "client")]
40impl Serialize for AuthPayload {
41    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42    where
43        S: serde::Serializer,
44    {
45        #[derive(Serialize)]
46        struct WireAuthPayload<'a> {
47            jwt: &'a str,
48            #[serde(skip_serializing_if = "Option::is_none")]
49            channel: Option<&'a str>,
50        }
51        WireAuthPayload {
52            jwt: self.jwt.expose_secret(),
53            channel: self.channel.as_deref(),
54        }
55        .serialize(serializer)
56    }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct WelcomePayload {
61    pub features: AvailableFeatures,
62    pub recording: RecordingState,
63    pub peers: Vec<PeerSnapshot>,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct SessionDescriptionPayload {
68    pub sdp: String,
69    #[serde(default, rename = "uploadSlots", skip_serializing_if = "Vec::is_empty")]
70    pub upload_slots: Vec<NegotiationUploadSlot>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct NegotiationUploadSlot {
76    pub mid: String,
77    pub kind: MediaKind,
78    #[serde(default, skip_serializing_if = "Vec::is_empty")]
79    pub codecs: Vec<String>,
80    #[serde(default, skip_serializing_if = "Vec::is_empty")]
81    pub simulcast_encodings: Vec<NegotiationUploadEncoding>,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct NegotiationUploadEncoding {
87    pub rid: String,
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub max_bitrate: Option<u64>,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub resolution_scale: Option<u16>,
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub max_framerate: Option<u16>,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub enum UploadLayerPolicyRole {
99    Featured,
100    Thumbnail,
101    DegradedThumbnail,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub struct StreamIntentPayload {
106    #[serde(rename = "type")]
107    pub stream_type: StreamType,
108}
109
110/// Sparse receiver preferences for the protocol's audio, camera and screen
111/// streams. Unknown fields are ignored and never become retained stream ids.
112/// An empty preference update leaves every existing stream preference unchanged.
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub struct SubscribePayload {
116    /// Wire shape is flat: `{ sessionId, audio?, camera?, screen? }`.
117    /// Adding fields to `DownloadStates` implicitly changes the subscribe payload shape.
118    #[serde(rename = "sessionId")]
119    pub user_id: UserId,
120    #[serde(flatten)]
121    pub states: DownloadStates,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct TrackBinding {
127    pub mid: String,
128    #[serde(rename = "sessionId")]
129    pub user_id: UserId,
130    #[serde(rename = "type")]
131    pub stream_type: StreamType,
132    pub active: bool,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub struct SourceDescriptor {
138    pub source_id: String,
139    #[serde(rename = "sessionId")]
140    pub user_id: UserId,
141    #[serde(rename = "type")]
142    pub stream_type: StreamType,
143    pub active: bool,
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub mid: Option<String>,
146    pub encodings: Vec<SourceEncodingDescriptor>,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
150#[serde(rename_all = "camelCase")]
151pub struct SourceEncodingDescriptor {
152    pub encoding_id: String,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub rid: Option<String>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub max_bitrate: Option<u64>,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub resolution_scale: Option<NonZeroU16>,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub max_framerate: Option<u16>,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub policy_role: Option<UploadLayerPolicyRole>,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct PeerInfoPayload {
168    #[serde(rename = "sessionId")]
169    pub user_id: UserId,
170    pub info: UserInfo,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(rename_all = "camelCase")]
175pub struct PeerLeftPayload {
176    #[serde(rename = "sessionId")]
177    pub user_id: UserId,
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
181pub struct ClientBroadcastPayload {
182    pub message: JsonPayload,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186#[serde(rename_all = "camelCase")]
187pub struct ServerBroadcastPayload {
188    pub sender_id: UserId,
189    pub message: JsonPayload,
190}
191
192#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct RecordingActionResult {
194    pub ok: bool,
195}