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;