Skip to main content

o_sfu_core/engine/room/source_policy/video/
input.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use o_sfu_router::MediaKind;
4
5use super::super::input::SourcePolicySnapshot;
6use crate::{
7    Bitrate,
8    engine::{
9        UserId, VideoLayoutIntent,
10        media_transport::TransportConsumerRoute,
11        room::{media_graph::SubscriptionKey, state::RoomState},
12        source_model::{
13            ConsumerSourceSelection, PublishedSourceDescriptor, SourceAdaptationPolicy,
14            SourceRoomPolicySelector,
15        },
16    },
17};
18
19pub(super) fn receiver_video_routes<'a>(
20    state: &RoomState,
21    input: &SourcePolicySnapshot<'a>,
22) -> Vec<ReceiverVideoRouteInput<'a>> {
23    let mut visible_scalable_route_counts = BTreeMap::new();
24    let mut routes = Vec::with_capacity(input.routes.len());
25    for route in &input.routes {
26        let source = &route.source.descriptor;
27        if source.media_kind() != MediaKind::Video
28            || (source.policy().adaptation() == SourceAdaptationPolicy::None
29                && source.policy().video_bitrate_cap().is_none())
30        {
31            continue;
32        }
33        let layout_role = state.receiver_video_layout_role(
34            &route.key.receiver,
35            source,
36            &input.featured_source_user_ids,
37        );
38        if source.policy().adaptation() == SourceAdaptationPolicy::ScalableVideo
39            && layout_role.counts_toward_visible_budget()
40        {
41            *visible_scalable_route_counts
42                .entry(route.key.receiver.clone())
43                .or_default() += 1;
44        }
45        routes.push(ReceiverVideoRouteInput {
46            user_count: input.user_count,
47            source,
48            key: route.key,
49            route: route.route,
50            current_selection: route.selection,
51            layout_role,
52            visible_scalable_route_count: 1,
53            active_speaker_rank: input
54                .active_speaker_rank_by_user
55                .get(source.owner().user_id())
56                .copied(),
57            receiver_bandwidth: input
58                .receiver_bandwidth_by_connection
59                .get(&route.route.consumer_session_key().connection_id())
60                .copied(),
61            source_bitrate: input
62                .source_bitrate_by_media
63                .get(&route.route.source_transport_media_id())
64                .copied(),
65            audio_budget_reserve: input
66                .audio_reserve_by_connection
67                .get(&route.route.consumer_session_key().connection_id())
68                .copied()
69                .unwrap_or_else(Bitrate::zero),
70        });
71    }
72    for route in &mut routes {
73        route.visible_scalable_route_count = visible_scalable_route_counts
74            .get(&route.key.receiver)
75            .copied()
76            .unwrap_or(1);
77    }
78    routes
79}
80
81#[derive(Debug)]
82pub(super) struct ReceiverVideoRouteInput<'a> {
83    pub(super) user_count: usize,
84    pub(super) source: &'a PublishedSourceDescriptor,
85    pub(super) key: &'a SubscriptionKey,
86    pub(super) route: &'a TransportConsumerRoute,
87    pub(super) current_selection: ConsumerSourceSelection,
88    pub(super) layout_role: SourceRoomPolicySelector,
89    pub(super) visible_scalable_route_count: usize,
90    pub(super) active_speaker_rank: Option<usize>,
91    pub(super) receiver_bandwidth: Option<Bitrate>,
92    pub(super) source_bitrate: Option<Bitrate>,
93    pub(super) audio_budget_reserve: Bitrate,
94}
95
96impl RoomState {
97    #[must_use]
98    pub(in crate::engine::room) fn receiver_video_layout_role(
99        &self,
100        consumer_user_id: &UserId,
101        source: &PublishedSourceDescriptor,
102        active_speaker_source_user_ids: &BTreeSet<UserId>,
103    ) -> SourceRoomPolicySelector {
104        let preference = layout_preference(self, consumer_user_id, source);
105        source
106            .policy()
107            .layout()
108            .map_or(SourceRoomPolicySelector::Hidden, |policy| {
109                policy.resolve(
110                    preference,
111                    active_speaker_source_user_ids.contains(source.owner().user_id()),
112                )
113            })
114    }
115
116    #[must_use]
117    pub(in crate::engine::room) fn diagnostics_video_layout_role(
118        &self,
119        consumer_user_id: &UserId,
120        source: &PublishedSourceDescriptor,
121    ) -> Option<SourceRoomPolicySelector> {
122        let policy = source.policy().layout()?;
123        let preference = layout_preference(self, consumer_user_id, source);
124        let active_speaker = self
125            .users
126            .get(source.owner().user_id())
127            .is_some_and(|user| user.featured() == Some(true));
128        Some(policy.resolve(preference, active_speaker))
129    }
130}
131
132fn layout_preference(
133    state: &RoomState,
134    consumer_user_id: &UserId,
135    source: &PublishedSourceDescriptor,
136) -> Option<VideoLayoutIntent> {
137    state
138        .topology
139        .subscription_intent(&SubscriptionKey::new(
140            consumer_user_id,
141            source.owner().user_id(),
142            source.stream_id(),
143        ))
144        .layout()
145}