o_sfu_protocol/core/
sticky_replay.rs1use std::collections::BTreeMap;
2
3use crate::{
4 shared::{DownloadStates, StreamType, UserId, UserInfo},
5 signaling::{ClientEnvelope, ClientMessage, EnvelopeBatch, SubscribePayload},
6};
7
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
13pub(super) struct StickyReplayState {
14 audio_publication_active: bool,
15 camera_publication_active: bool,
16 screen_publication_active: bool,
17 desired_subscriptions: BTreeMap<UserId, DownloadStates>,
18 desired_info: Option<UserInfo>,
19}
20
21impl StickyReplayState {
22 pub(super) fn new() -> Self {
23 Self::default()
24 }
25
26 pub(super) fn clear(&mut self) {
27 self.audio_publication_active = false;
28 self.camera_publication_active = false;
29 self.screen_publication_active = false;
30 self.desired_subscriptions.clear();
31 self.desired_info = None;
32 }
33
34 pub(super) fn set_publish_active(&mut self, stream_type: StreamType, active: bool) {
35 match stream_type {
36 StreamType::Audio => self.audio_publication_active = active,
37 StreamType::Camera => self.camera_publication_active = active,
38 StreamType::Screen => self.screen_publication_active = active,
39 }
40 }
41
42 pub(super) fn active_publications(&self) -> impl Iterator<Item = StreamType> + '_ {
43 [
44 (StreamType::Audio, self.audio_publication_active),
45 (StreamType::Camera, self.camera_publication_active),
46 (StreamType::Screen, self.screen_publication_active),
47 ]
48 .into_iter()
49 .filter_map(|(stream_type, active)| active.then_some(stream_type))
50 }
51
52 pub(super) fn remember_subscription_states(
53 &mut self,
54 user_id: &UserId,
55 states: &DownloadStates,
56 ) {
57 let existing_states = self
58 .desired_subscriptions
59 .entry(user_id.clone())
60 .or_default();
61 existing_states.apply_partial_update(states);
62 if *existing_states == DownloadStates::default() {
63 self.desired_subscriptions.remove(user_id);
64 }
65 }
66
67 pub(super) fn remember_info(&mut self, info: &UserInfo) {
68 let existing_info = self.desired_info.get_or_insert_with(UserInfo::default);
69 let is_featured = existing_info.is_featured;
70 existing_info.apply_partial_update(info);
71 existing_info.is_featured = is_featured;
72 }
73
74 pub(super) fn replay_session_batch(&self) -> Option<EnvelopeBatch> {
75 let mut replay_batch = Vec::new();
76
77 for (user_id, states) in &self.desired_subscriptions {
78 let Some(envelope) =
79 ClientEnvelope::Message(ClientMessage::Subscribe(SubscribePayload {
80 user_id: user_id.clone(),
81 states: states.clone(),
82 }))
83 .into_envelope()
84 .ok()
85 else {
86 continue;
87 };
88 replay_batch.push(envelope);
89 }
90
91 if let Some(info) = self.desired_info.clone() {
92 let envelope = ClientEnvelope::Message(ClientMessage::Info(info))
93 .into_envelope()
94 .ok()?;
95 replay_batch.push(envelope);
96 }
97
98 if replay_batch.is_empty() {
99 None
100 } else {
101 Some(replay_batch)
102 }
103 }
104}