1 use crate::{ 2 algorithms::FitnessDistance, constraints::SanitizedAdvancedMediaTrackConstraints, 3 MediaTrackSettings, 4 }; 5 6 /// Returns the set of settings for which all non-overconstraining advanced constraints' 7 /// fitness distance is finite. 8 /// 9 /// Implements step 5 of the `SelectSettings` algorithm: 10 /// https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 11 /// 12 /// # Note: 13 /// This may change the order of items in `feasible_candidates`. 14 /// In practice however this is not a problem as we have to sort 15 /// it by fitness-distance eventually anyway. 16 pub(super) fn apply_advanced_constraints<'a>( 17 mut candidates: Vec<(&'a MediaTrackSettings, f64)>, 18 advanced_constraints: &SanitizedAdvancedMediaTrackConstraints, 19 ) -> Vec<(&'a MediaTrackSettings, f64)> { 20 // As specified in step 5 of the `SelectSettings` algorithm: 21 // https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 22 // 23 // > Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified. 24 // > 25 // > For each ConstraintSet: 26 // > 27 // > 1. compute the fitness distance between it and each settings dictionary in candidates, 28 // > treating bare values of properties as exact. 29 // > 30 // > 2. If the fitness distance is finite for one or more settings dictionaries in candidates, 31 // > keep those settings dictionaries in candidates, discarding others. 32 // > 33 // > If the fitness distance is infinite for all settings dictionaries in candidates, 34 // > ignore this ConstraintSet. 35 36 let mut selected_candidates = Vec::with_capacity(candidates.len()); 37 38 // Double-buffered sieving to avoid excessive vec allocations: 39 for advanced_constraint_set in advanced_constraints.iter() { 40 for (candidate, fitness_distance) in candidates.iter() { 41 if advanced_constraint_set.fitness_distance(candidate).is_ok() { 42 selected_candidates.push((*candidate, *fitness_distance)); 43 } 44 } 45 46 if !selected_candidates.is_empty() { 47 candidates.clear(); 48 std::mem::swap(&mut candidates, &mut selected_candidates); 49 } 50 } 51 52 candidates 53 } 54 55 #[cfg(test)] 56 mod tests { 57 use std::iter::FromIterator; 58 59 use crate::{ 60 property::all::name::*, MediaTrackSupportedConstraints, ResizeMode, 61 ResolvedAdvancedMediaTrackConstraints, ResolvedMediaTrackConstraintSet, 62 ResolvedValueConstraint, ResolvedValueRangeConstraint, 63 }; 64 65 use super::*; 66 67 // Advanced constraint sets that doe not match any 68 // candidates should just get ignored: 69 #[test] 70 fn overconstrained() { 71 let supported_constraints = MediaTrackSupportedConstraints::from_iter(vec![ 72 &DEVICE_ID, 73 &HEIGHT, 74 &WIDTH, 75 &RESIZE_MODE, 76 ]); 77 78 let settings = vec![ 79 MediaTrackSettings::from_iter([(&DEVICE_ID, "foo".into())]), 80 MediaTrackSettings::from_iter([(&DEVICE_ID, "bar".into())]), 81 ]; 82 83 let candidates: Vec<_> = settings 84 .iter() 85 // attach a dummy fitness function: 86 .map(|settings| (settings, 42.0)) 87 .collect(); 88 89 let constraints = ResolvedAdvancedMediaTrackConstraints::from_iter([ 90 ResolvedMediaTrackConstraintSet::from_iter([( 91 &DEVICE_ID, 92 ResolvedValueConstraint::default() 93 .exact("bazblee".to_owned()) 94 .into(), 95 )]), 96 ]); 97 98 let sanitized_constraints = constraints.to_sanitized(&supported_constraints); 99 100 let actual: Vec<_> = apply_advanced_constraints(candidates, &sanitized_constraints) 101 .into_iter() 102 // drop the dummy fitness distance: 103 .map(|(settings, _)| settings) 104 .collect(); 105 106 let expected: Vec<_> = settings.iter().collect(); 107 108 assert_eq!(actual, expected); 109 } 110 111 #[test] 112 fn constrained() { 113 let supported_constraints = MediaTrackSupportedConstraints::from_iter(vec![ 114 &DEVICE_ID, 115 &HEIGHT, 116 &WIDTH, 117 &RESIZE_MODE, 118 ]); 119 120 let settings = vec![ 121 MediaTrackSettings::from_iter([ 122 (&DEVICE_ID, "480p".into()), 123 (&HEIGHT, 480.into()), 124 (&WIDTH, 720.into()), 125 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()), 126 ]), 127 MediaTrackSettings::from_iter([ 128 (&DEVICE_ID, "720p".into()), 129 (&HEIGHT, 720.into()), 130 (&WIDTH, 1280.into()), 131 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()), 132 ]), 133 MediaTrackSettings::from_iter([ 134 (&DEVICE_ID, "1080p".into()), 135 (&HEIGHT, 1080.into()), 136 (&WIDTH, 1920.into()), 137 (&RESIZE_MODE, ResizeMode::none().into()), 138 ]), 139 MediaTrackSettings::from_iter([ 140 (&DEVICE_ID, "1440p".into()), 141 (&HEIGHT, 1440.into()), 142 (&WIDTH, 2560.into()), 143 (&RESIZE_MODE, ResizeMode::none().into()), 144 ]), 145 MediaTrackSettings::from_iter([ 146 (&DEVICE_ID, "2160p".into()), 147 (&HEIGHT, 2160.into()), 148 (&WIDTH, 3840.into()), 149 (&RESIZE_MODE, ResizeMode::none().into()), 150 ]), 151 ]; 152 153 let candidates: Vec<_> = settings.iter().map(|settings| (settings, 42.0)).collect(); 154 155 let constraints = ResolvedAdvancedMediaTrackConstraints::from_iter([ 156 // The first advanced constraint set of "exact 800p" does not match 157 // any candidate and should thus get ignored by the algorithm: 158 ResolvedMediaTrackConstraintSet::from_iter([( 159 &HEIGHT, 160 ResolvedValueRangeConstraint::default().exact(800).into(), 161 )]), 162 // The second advanced constraint set of "no resizing" does match 163 // candidates and should thus be applied by the algorithm: 164 ResolvedMediaTrackConstraintSet::from_iter([( 165 &RESIZE_MODE, 166 ResolvedValueConstraint::default() 167 .exact(ResizeMode::none()) 168 .into(), 169 )]), 170 // The second advanced constraint set of "max 1440p" does match 171 // candidates and should thus be applied by the algorithm: 172 ResolvedMediaTrackConstraintSet::from_iter([( 173 &HEIGHT, 174 ResolvedValueRangeConstraint::default().max(1440).into(), 175 )]), 176 ]); 177 178 let sanitized_constraints = constraints.to_sanitized(&supported_constraints); 179 180 let actual: Vec<_> = apply_advanced_constraints(candidates, &sanitized_constraints) 181 .into_iter() 182 // drop the dummy fitness distance: 183 .map(|(settings, _)| settings) 184 .collect(); 185 186 let expected = vec![&settings[2], &settings[3]]; 187 188 assert_eq!(actual, expected); 189 } 190 } 191