Skip to main content

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

1use o_sfu_router::MediaKind;
2
3use super::{
4    super::action::{ConsumerPacketSelectionUpdate, UpgradeChange, VideoRouteTransition},
5    solver::{PlannedReceiverRoute, selector_bitrate},
6};
7use crate::engine::{
8    media_transport::SourcePacketGate,
9    source_model::{PublishedSourceDescriptor, ReceiverVideoBudgetDiagnostics, SourceSelector},
10};
11
12/// Projects a room selector into a source-worker packet gate.
13///
14/// Returns `None` when the selected encoding is unknown or has no negotiated RID.
15pub(super) fn source_packet_gate_for_selector(
16    source: &PublishedSourceDescriptor,
17    selector: SourceSelector,
18) -> Option<SourcePacketGate> {
19    match selector {
20        SourceSelector::Open => Some(SourcePacketGate::Open),
21        SourceSelector::Encoding(encoding_id) => {
22            // Never fall back to `Open`: it would forward every encoding while
23            // room state records one selected encoding.
24            let encoding = source.encoding(encoding_id)?;
25            let rid = encoding.rid()?;
26            Some(SourcePacketGate::Rid(rid.as_str().to_owned()))
27        }
28    }
29}
30
31pub(super) fn consumer_packet_selection_update(
32    planned_route: &PlannedReceiverRoute<'_>,
33    planned_budget: ReceiverVideoBudgetDiagnostics,
34) -> Option<ConsumerPacketSelectionUpdate> {
35    let input = planned_route.input;
36    let selection = planned_route.selection;
37    let current_selection = input.current_selection;
38    if selection.selector == current_selection.selector()
39        && selection.policy_pause_reason == current_selection.policy_pause_reason()
40        && planned_budget == current_selection.budget()
41        && selection.pending_upgrade.as_ref() == input.pending_upgrade
42    {
43        return None;
44    }
45    let packet_gate = if selection.selector == current_selection.selector() {
46        None
47    } else {
48        Some(source_packet_gate_for_selector(
49            input.source,
50            selection.selector,
51        )?)
52    };
53    let route_activity_changed =
54        selection.policy_pause_reason != current_selection.policy_pause_reason();
55    // A newly selected RID or resumed route may not share the receiver's last
56    // decodable reference chain, so request a keyframe for either transition.
57    let request_keyframe = selection.policy_pause_reason.is_none()
58        && (selection.request_keyframe
59            || selection.selector != current_selection.selector()
60            || !current_selection.policy_allows_delivery());
61    let transition = route_transition(planned_route);
62    let selected_estimated_bitrate =
63        transition.and_then(|_| selector_bitrate(input, selection.selector));
64    Some(ConsumerPacketSelectionUpdate {
65        key: input.key.clone(),
66        source_id: input.source.source_id(),
67        route: input.route.clone(),
68        selector: selection.selector,
69        policy_pause_reason: selection.policy_pause_reason,
70        planned_budget,
71        transition,
72        selected_estimated_bitrate,
73        upgrade: if selection.pending_upgrade.as_ref() == input.pending_upgrade {
74            UpgradeChange::Unchanged
75        } else {
76            UpgradeChange::Set(selection.pending_upgrade)
77        },
78        interrupts_upgrade: selection.interrupts_upgrade(input.pending_upgrade),
79        packet_gate,
80        route_activity_changed,
81        request_keyframe: request_keyframe && input.source.media_kind() == MediaKind::Video,
82    })
83}
84
85fn route_transition(route: &PlannedReceiverRoute<'_>) -> Option<VideoRouteTransition> {
86    let selection = route.selection;
87    match (
88        selection.policy_pause_reason,
89        route.input.current_selection.policy_pause_reason(),
90    ) {
91        (None, Some(cleared_reason)) => Some(VideoRouteTransition::Resumed { cleared_reason }),
92        (Some(reason), None) => Some(VideoRouteTransition::Paused { reason }),
93        (Some(_), Some(_)) => None,
94        (None, None) if selection.selector == route.input.current_selection.selector() => None,
95        (None, None) => {
96            let current_selector @ SourceSelector::Encoding(_) =
97                route.input.current_selection.selector()
98            else {
99                return None;
100            };
101            let selected_selector @ SourceSelector::Encoding(_) = selection.selector else {
102                return None;
103            };
104            let current_bitrate = selector_bitrate(route.input, current_selector)?;
105            let selected_bitrate = selector_bitrate(route.input, selected_selector)?;
106            (selected_bitrate < current_bitrate).then_some(VideoRouteTransition::Degraded)
107        }
108    }
109}