webrtc_constraints/algorithms/select_settings/
tie_breaking.rs1use std::iter::FromIterator;
2
3use ordered_float::NotNan;
4
5use crate::{
6 algorithms::FitnessDistance, MandatoryMediaTrackConstraints, MediaTrackSettings,
7 MediaTrackSupportedConstraints, SanitizedMandatoryMediaTrackConstraints,
8};
9
10pub trait TieBreakingPolicy {
13 fn select_candidate<'a, I>(&self, candidates: I) -> &'a MediaTrackSettings
25 where
26 I: IntoIterator<Item = &'a MediaTrackSettings>;
27}
28
29pub struct FirstPolicy;
31
32impl FirstPolicy {
33 pub fn new() -> Self {
35 Self
36 }
37}
38
39impl Default for FirstPolicy {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl TieBreakingPolicy for FirstPolicy {
46 fn select_candidate<'a, I>(&self, candidates: I) -> &'a MediaTrackSettings
47 where
48 I: IntoIterator<Item = &'a MediaTrackSettings>,
49 {
50 candidates
52 .into_iter()
53 .next()
54 .expect("The `candidates` iterator should have produced at least one item.")
55 }
56}
57
58pub struct ClosestToIdealPolicy {
60 sanitized_constraints: SanitizedMandatoryMediaTrackConstraints,
61}
62
63impl ClosestToIdealPolicy {
64 pub fn new(
66 ideal_settings: MediaTrackSettings,
67 supported_constraints: &MediaTrackSupportedConstraints,
68 ) -> Self {
69 let sanitized_constraints = MandatoryMediaTrackConstraints::from_iter(
70 ideal_settings
71 .into_iter()
72 .map(|(property, setting)| (property, setting.into())),
73 )
74 .into_resolved()
75 .into_sanitized(supported_constraints);
76
77 Self {
78 sanitized_constraints,
79 }
80 }
81}
82
83impl TieBreakingPolicy for ClosestToIdealPolicy {
84 fn select_candidate<'b, I>(&self, candidates: I) -> &'b MediaTrackSettings
85 where
86 I: IntoIterator<Item = &'b MediaTrackSettings>,
87 {
88 candidates
89 .into_iter()
90 .min_by_key(|settings| {
91 let fitness_distance = self
92 .sanitized_constraints
93 .fitness_distance(settings)
94 .expect("Fitness distance should be positive.");
95 NotNan::new(fitness_distance).expect("Expected non-NaN fitness distance.")
96 })
97 .expect("The `candidates` iterator should have produced at least one item.")
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 use std::iter::FromIterator;
106
107 use crate::{
108 property::all::name::*, MediaTrackSettings, MediaTrackSupportedConstraints, ResizeMode,
109 };
110
111 #[test]
112 fn first() {
113 let settings = vec![
114 MediaTrackSettings::from_iter([(&DEVICE_ID, "device-id-0".into())]),
115 MediaTrackSettings::from_iter([(&DEVICE_ID, "device-id-1".into())]),
116 MediaTrackSettings::from_iter([(&DEVICE_ID, "device-id-2".into())]),
117 ];
118
119 let policy = FirstPolicy::default();
120
121 let actual = policy.select_candidate(&settings);
122
123 let expected = &settings[0];
124
125 assert_eq!(actual, expected);
126 }
127
128 #[test]
129 fn closest_to_ideal() {
130 let supported_constraints = MediaTrackSupportedConstraints::from_iter(vec![
131 &DEVICE_ID,
132 &HEIGHT,
133 &WIDTH,
134 &RESIZE_MODE,
135 ]);
136
137 let settings = vec![
138 MediaTrackSettings::from_iter([
139 (&DEVICE_ID, "480p".into()),
140 (&HEIGHT, 480.into()),
141 (&WIDTH, 720.into()),
142 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()),
143 ]),
144 MediaTrackSettings::from_iter([
145 (&DEVICE_ID, "720p".into()),
146 (&HEIGHT, 720.into()),
147 (&WIDTH, 1280.into()),
148 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()),
149 ]),
150 MediaTrackSettings::from_iter([
151 (&DEVICE_ID, "1080p".into()),
152 (&HEIGHT, 1080.into()),
153 (&WIDTH, 1920.into()),
154 (&RESIZE_MODE, ResizeMode::none().into()),
155 ]),
156 MediaTrackSettings::from_iter([
157 (&DEVICE_ID, "1440p".into()),
158 (&HEIGHT, 1440.into()),
159 (&WIDTH, 2560.into()),
160 (&RESIZE_MODE, ResizeMode::none().into()),
161 ]),
162 MediaTrackSettings::from_iter([
163 (&DEVICE_ID, "2160p".into()),
164 (&HEIGHT, 2160.into()),
165 (&WIDTH, 3840.into()),
166 (&RESIZE_MODE, ResizeMode::none().into()),
167 ]),
168 ];
169
170 let ideal_settings = vec![
171 MediaTrackSettings::from_iter([(&HEIGHT, 450.into()), (&WIDTH, 700.into())]),
172 MediaTrackSettings::from_iter([(&HEIGHT, 700.into()), (&WIDTH, 1250.into())]),
173 MediaTrackSettings::from_iter([(&HEIGHT, 1000.into()), (&WIDTH, 2000.into())]),
174 MediaTrackSettings::from_iter([(&HEIGHT, 1500.into()), (&WIDTH, 2500.into())]),
175 MediaTrackSettings::from_iter([(&HEIGHT, 2000.into()), (&WIDTH, 3750.into())]),
176 ];
177
178 for (index, ideal) in ideal_settings.iter().enumerate() {
179 let policy = ClosestToIdealPolicy::new(ideal.clone(), &supported_constraints);
180
181 let actual = policy.select_candidate(&settings);
182
183 let expected = &settings[index];
184
185 assert_eq!(actual, expected);
186 }
187 }
188}