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