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 {
from(error: OverconstrainedError) -> Self34     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.
select_settings_candidates<'a, I>( possible_settings: I, constraints: &SanitizedMediaTrackConstraints, exposure_mode: DeviceInformationExposureMode, ) -> Result<Vec<&'a MediaTrackSettings>, SelectSettingsError> where I: IntoIterator<Item = &'a MediaTrackSettings>,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