1 use std::collections::HashSet; 2 3 use crate::{ 4 algorithms::fitness_distance::SettingFitnessDistanceError, errors::OverconstrainedError, 5 MediaTrackSettings, SanitizedMediaTrackConstraints, 6 }; 7 8 mod apply_advanced; 9 mod apply_mandatory; 10 mod select_optimal; 11 mod tie_breaking; 12 13 pub use self::tie_breaking::*; 14 15 use self::{apply_advanced::*, apply_mandatory::*, select_optimal::*}; 16 17 #[derive(Copy, Clone, Eq, PartialEq, Debug)] 18 pub enum DeviceInformationExposureMode { 19 Exposed, 20 Protected, 21 } 22 23 #[derive(Clone, Eq, PartialEq, Debug)] 24 pub enum SelectSettingsError { 25 Overconstrained(OverconstrainedError), 26 } 27 28 impl From<OverconstrainedError> for SelectSettingsError { 29 fn from(error: OverconstrainedError) -> Self { 30 Self::Overconstrained(error) 31 } 32 } 33 34 /// This function implements steps 1-5 of the `SelectSettings` algorithm 35 /// as defined by the W3C spec: 36 /// https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 37 /// 38 /// Step 6 (tie-breaking) is omitted by this implementation and expected to be performed 39 /// manually on the returned candidates. 40 /// For this several implementation of `TieBreakingPolicy` are provided by this crate. 41 pub fn select_settings_candidates<'a, I>( 42 possible_settings: I, 43 constraints: &SanitizedMediaTrackConstraints, 44 exposure_mode: DeviceInformationExposureMode, 45 ) -> Result<Vec<&'a MediaTrackSettings>, SelectSettingsError> 46 where 47 I: IntoIterator<Item = &'a MediaTrackSettings>, 48 { 49 let possible_settings = possible_settings.into_iter(); 50 51 // As specified in step 1 of the `SelectSettings` algorithm: 52 // https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 53 // 54 // > Each constraint specifies one or more values (or a range of values) for its property. 55 // > A property MAY appear more than once in the list of 'advanced' ConstraintSets. 56 // > If an empty list has been given as the value for a constraint, 57 // > it MUST be interpreted as if the constraint were not specified 58 // > (in other words, an empty constraint == no constraint). 59 // > 60 // > Note that unknown properties are discarded by WebIDL, 61 // > which means that unknown/unsupported required constraints will silently disappear. 62 // > To avoid this being a surprise, application authors are expected to first use 63 // > the `getSupportedConstraints()` method […]. 64 65 // We expect "sanitized" constraints to not contain empty constraints: 66 debug_assert!(constraints 67 .mandatory 68 .iter() 69 .all(|(_, constraint)| !constraint.is_empty())); 70 71 // Obtain candidates by filtering possible settings, dropping those with infinite fitness distances: 72 // 73 // This function call corresponds to steps 3 & 4 of the `SelectSettings` algorithm: 74 // https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 75 76 let candidates_and_fitness_distances = 77 apply_mandatory_constraints(possible_settings, &constraints.mandatory, exposure_mode)?; 78 79 // As specified in step 5 of the `SelectSettings` algorithm: 80 // https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 81 // 82 // > Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified. 83 // > 84 // > For each ConstraintSet: 85 // > 86 // > 1. compute the fitness distance between it and each settings dictionary in candidates, 87 // > treating bare values of properties as exact. 88 // > 89 // > 2. If the fitness distance is finite for one or more settings dictionaries in candidates, 90 // > keep those settings dictionaries in candidates, discarding others. 91 // > 92 // > If the fitness distance is infinite for all settings dictionaries in candidates, 93 // > ignore this ConstraintSet. 94 let candidates = 95 apply_advanced_constraints(candidates_and_fitness_distances, &constraints.advanced); 96 97 // As specified in step 6 of the `SelectSettings` algorithm: 98 // https://www.w3.org/TR/mediacapture-streams/#dfn-selectsettings 99 // 100 // > Select one settings dictionary from candidates, and return it as the result of the `SelectSettings` algorithm. 101 // > The User Agent MUST use one with the smallest fitness distance, as calculated in step 3. 102 // > If more than one settings dictionary have the smallest fitness distance, 103 // > the User Agent chooses one of them based on system default property values and User Agent default property values. 104 // 105 // # Important 106 // Instead of return just ONE settings instance "with the smallest fitness distance, as calculated in step 3" 107 // we instead return ALL settings instances "with the smallest fitness distance, as calculated in step 3" 108 // and leave tie-breaking to the User Agent in a seperate step: 109 Ok(select_optimal_candidates(candidates)) 110 } 111 112 #[derive(Default)] 113 pub(crate) struct ConstraintFailureInfo { 114 pub(crate) failures: usize, 115 pub(crate) errors: HashSet<SettingFitnessDistanceError>, 116 } 117 118 #[cfg(test)] 119 mod tests; 120