webrtc_constraints/algorithms/
select_settings.rs

1use std::collections::HashSet;
2
3use crate::{
4    algorithms::fitness_distance::SettingFitnessDistanceError, errors::OverconstrainedError,
5    MediaTrackSettings, SanitizedMediaTrackConstraints,
6};
7
8mod apply_advanced;
9mod apply_mandatory;
10mod select_optimal;
11mod tie_breaking;
12
13pub use self::tie_breaking::*;
14
15use 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)]
19pub 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)]
28pub enum SelectSettingsError {
29    /// An error caused by one or more over-constrained settings.
30    Overconstrained(OverconstrainedError),
31}
32
33impl 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.
46pub fn select_settings_candidates<'a, I>(
47    possible_settings: I,
48    constraints: &SanitizedMediaTrackConstraints,
49    exposure_mode: DeviceInformationExposureMode,
50) -> Result<Vec<&'a MediaTrackSettings>, SelectSettingsError>
51where
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)]
118pub(crate) struct ConstraintFailureInfo {
119    pub(crate) failures: usize,
120    pub(crate) errors: HashSet<SettingFitnessDistanceError>,
121}
122
123#[cfg(test)]
124mod tests;