use std::{cell::RefCell, rc::Rc};
use derive_more::with_trait::Display;
use futures::stream::LocalBoxStream;
use medea_client_api_proto::{
AudioSettings as ProtoAudioConstraints, MediaSourceKind,
MediaType as ProtoTrackConstraints, MediaType, VideoSettings,
};
use medea_reactive::ObservableCell;
use crate::{
media::{MediaKind, track::MediaStreamTrackState},
peer::{
LocalStreamUpdateCriteria, MediaState, media_exchange_state, mute_state,
},
platform,
};
#[derive(Clone, Copy, Debug, Display, Eq, PartialEq)]
#[repr(u8)]
pub enum FacingMode {
#[display("user")]
User = 0,
#[display("environment")]
Environment = 1,
#[display("left")]
Left = 2,
#[display("right")]
Right = 3,
}
#[derive(Clone, Debug, Default)]
pub struct LocalTracksConstraints(Rc<RefCell<MediaStreamSettings>>);
#[derive(Debug)]
pub struct RecvConstraints {
is_audio_enabled: ObservableCell<bool>,
is_video_device_enabled: ObservableCell<bool>,
is_video_display_enabled: ObservableCell<bool>,
}
impl Clone for RecvConstraints {
fn clone(&self) -> Self {
Self {
is_audio_enabled: ObservableCell::new(self.is_audio_enabled.get()),
is_video_device_enabled: ObservableCell::new(
self.is_video_device_enabled.get(),
),
is_video_display_enabled: ObservableCell::new(
self.is_video_display_enabled.get(),
),
}
}
}
impl Default for RecvConstraints {
fn default() -> Self {
Self {
is_audio_enabled: ObservableCell::new(true),
is_video_device_enabled: ObservableCell::new(true),
is_video_display_enabled: ObservableCell::new(true),
}
}
}
impl RecvConstraints {
pub fn set_enabled(
&self,
enabled: bool,
kind: MediaKind,
source_kind: Option<MediaSourceKind>,
) {
match kind {
MediaKind::Audio => {
self.is_audio_enabled.set(enabled);
}
MediaKind::Video => source_kind.map_or_else(
|| {
self.is_video_device_enabled.set(enabled);
self.is_video_display_enabled.set(enabled);
},
|sk| match sk {
MediaSourceKind::Device => {
self.is_video_device_enabled.set(enabled);
}
MediaSourceKind::Display => {
self.is_video_display_enabled.set(enabled);
}
},
),
}
}
pub fn is_audio_enabled(&self) -> bool {
self.is_audio_enabled.get()
}
pub fn is_video_device_enabled(&self) -> bool {
self.is_video_device_enabled.get()
}
pub fn is_video_display_enabled(&self) -> bool {
self.is_video_display_enabled.get()
}
pub fn on_audio_enabled_change(&self) -> LocalBoxStream<'static, bool> {
self.is_audio_enabled.subscribe()
}
pub fn on_video_device_enabled_change(
&self,
) -> LocalBoxStream<'static, bool> {
self.is_video_device_enabled.subscribe()
}
pub fn on_video_display_enabled_change(
&self,
) -> LocalBoxStream<'static, bool> {
self.is_video_display_enabled.subscribe()
}
}
#[cfg(feature = "mockable")]
impl From<MediaStreamSettings> for LocalTracksConstraints {
fn from(from: MediaStreamSettings) -> Self {
Self(Rc::new(RefCell::new(from)))
}
}
impl LocalTracksConstraints {
#[must_use]
pub fn calculate_kinds_diff(
&self,
settings: &MediaStreamSettings,
) -> LocalStreamUpdateCriteria {
self.0.borrow().calculate_kinds_diff(settings)
}
pub fn constrain(&self, other: MediaStreamSettings) {
self.0.borrow_mut().constrain(other);
}
#[must_use]
pub fn inner(&self) -> MediaStreamSettings {
self.0.borrow().clone()
}
pub fn set_media_state(
&self,
state: MediaState,
kind: MediaKind,
source_kind: Option<MediaSourceKind>,
) {
self.0.borrow_mut().set_track_media_state(state, kind, source_kind);
}
pub fn set_media_exchange_state_by_kinds(
&self,
state: media_exchange_state::Stable,
kinds: LocalStreamUpdateCriteria,
) {
self.0.borrow_mut().set_media_exchange_state_by_kinds(state, kinds);
}
#[must_use]
pub fn enabled(&self, kind: &MediaType) -> bool {
self.0.borrow().enabled(kind)
}
#[must_use]
pub fn muted(&self, kind: &MediaType) -> bool {
self.0.borrow().muted(kind)
}
#[must_use]
pub fn is_track_enabled_and_constrained(
&self,
kind: MediaKind,
source: Option<MediaSourceKind>,
) -> bool {
self.0.borrow().is_track_enabled_and_constrained(kind, source)
}
#[must_use]
pub fn is_track_enabled(
&self,
kind: MediaKind,
source: Option<MediaSourceKind>,
) -> bool {
self.0.borrow().is_track_enabled(kind, source)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AudioMediaTracksSettings {
constraints: AudioTrackConstraints,
enabled: bool,
muted: bool,
}
impl Default for AudioMediaTracksSettings {
fn default() -> Self {
Self {
constraints: AudioTrackConstraints::default(),
enabled: true,
muted: false,
}
}
}
async fn satisfies_track(
track: &platform::MediaStreamTrack,
kind: MediaKind,
) -> bool {
track.kind() == kind
&& track.ready_state().await == MediaStreamTrackState::Live
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VideoTrackConstraints<C> {
constraints: Option<C>,
enabled: bool,
muted: bool,
}
impl<C: Default> Default for VideoTrackConstraints<C> {
fn default() -> Self {
Self { constraints: Some(C::default()), enabled: true, muted: false }
}
}
impl<C> VideoTrackConstraints<C> {
const fn enabled(&self) -> bool {
self.enabled && self.is_constrained()
}
fn set(&mut self, cons: C) {
self.constraints = Some(cons);
}
fn unconstrain(&mut self) {
drop(self.constraints.take());
}
const fn is_constrained(&self) -> bool {
self.constraints.is_some()
}
fn constrain(&mut self, other: Self) {
self.enabled &= other.enabled;
self.constraints = other.constraints;
}
}
impl VideoTrackConstraints<DeviceVideoTrackConstraints> {
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
if let Some(constraints) = &self.constraints {
self.enabled() && constraints.satisfies(track).await
} else {
false
}
}
}
impl VideoTrackConstraints<DisplayVideoTrackConstraints> {
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
if let Some(constraints) = &self.constraints {
self.enabled() && constraints.satisfies(track).await
} else {
false
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct MediaStreamSettings {
audio: AudioMediaTracksSettings,
device_video: VideoTrackConstraints<DeviceVideoTrackConstraints>,
display_video: VideoTrackConstraints<DisplayVideoTrackConstraints>,
}
impl MediaStreamSettings {
#[must_use]
pub fn new() -> Self {
Self {
audio: AudioMediaTracksSettings {
constraints: AudioTrackConstraints::default(),
enabled: false,
muted: false,
},
display_video: VideoTrackConstraints {
enabled: true,
constraints: None,
muted: false,
},
device_video: VideoTrackConstraints {
enabled: true,
constraints: None,
muted: false,
},
}
}
pub fn audio(&mut self, constraints: AudioTrackConstraints) {
self.audio.enabled = true;
self.audio.constraints = constraints;
}
pub fn device_video(&mut self, constraints: DeviceVideoTrackConstraints) {
self.device_video.set(constraints);
}
pub fn display_video(&mut self, constraints: DisplayVideoTrackConstraints) {
self.display_video.set(constraints);
}
pub async fn unconstrain_if_satisfies_video<T>(&mut self, track: T) -> bool
where
T: AsRef<platform::MediaStreamTrack>,
{
if self.device_video.satisfies(&track).await {
self.device_video.unconstrain();
true
} else if self.display_video.satisfies(&track).await {
self.display_video.unconstrain();
true
} else {
false
}
}
#[must_use]
pub fn calculate_kinds_diff(
&self,
another: &Self,
) -> LocalStreamUpdateCriteria {
let mut kinds = LocalStreamUpdateCriteria::empty();
if self.device_video != another.device_video {
kinds.add(MediaKind::Video, MediaSourceKind::Device);
}
if self.display_video != another.display_video {
kinds.add(MediaKind::Video, MediaSourceKind::Display);
}
if self.audio != another.audio {
kinds.add(MediaKind::Audio, MediaSourceKind::Device);
}
kinds
}
#[must_use]
pub const fn get_audio(&self) -> &AudioTrackConstraints {
&self.audio.constraints
}
#[must_use]
pub const fn get_display_video(
&self,
) -> Option<&DisplayVideoTrackConstraints> {
self.display_video.constraints.as_ref()
}
#[must_use]
pub const fn get_device_video(
&self,
) -> Option<&DeviceVideoTrackConstraints> {
self.device_video.constraints.as_ref()
}
pub fn set_track_media_state(
&mut self,
state: MediaState,
kind: MediaKind,
source_kind: Option<MediaSourceKind>,
) {
match kind {
MediaKind::Audio => match state {
MediaState::Mute(muted) => {
self.set_audio_muted(muted == mute_state::Stable::Muted);
}
MediaState::MediaExchange(media_exchange) => {
self.set_audio_publish(
media_exchange == media_exchange_state::Stable::Enabled,
);
}
},
MediaKind::Video => match state {
MediaState::Mute(muted) => {
self.set_video_muted(
muted == mute_state::Stable::Muted,
source_kind,
);
}
MediaState::MediaExchange(media_exchange) => {
self.set_video_publish(
media_exchange == media_exchange_state::Stable::Enabled,
source_kind,
);
}
},
}
}
pub fn set_media_exchange_state_by_kinds(
&mut self,
state: media_exchange_state::Stable,
kinds: LocalStreamUpdateCriteria,
) {
let enabled = state == media_exchange_state::Stable::Enabled;
if kinds.has(MediaKind::Audio, MediaSourceKind::Device) {
self.set_audio_publish(enabled);
}
if kinds.has(MediaKind::Video, MediaSourceKind::Device) {
self.set_video_publish(enabled, Some(MediaSourceKind::Device));
}
if kinds.has(MediaKind::Video, MediaSourceKind::Display) {
self.set_video_publish(enabled, Some(MediaSourceKind::Display));
}
}
const fn set_audio_muted(&mut self, muted: bool) {
self.audio.muted = muted;
}
const fn set_video_muted(
&mut self,
muted: bool,
source_kind: Option<MediaSourceKind>,
) {
match source_kind {
None => {
self.display_video.muted = muted;
self.device_video.muted = muted;
}
Some(MediaSourceKind::Device) => {
self.device_video.muted = muted;
}
Some(MediaSourceKind::Display) => {
self.display_video.muted = muted;
}
}
}
pub const fn set_audio_publish(&mut self, enabled: bool) {
self.audio.enabled = enabled;
}
pub const fn set_video_publish(
&mut self,
enabled: bool,
source_kind: Option<MediaSourceKind>,
) {
match source_kind {
None => {
self.display_video.enabled = enabled;
self.device_video.enabled = enabled;
}
Some(MediaSourceKind::Device) => {
self.device_video.enabled = enabled;
}
Some(MediaSourceKind::Display) => {
self.display_video.enabled = enabled;
}
}
}
#[must_use]
pub const fn is_audio_enabled(&self) -> bool {
self.audio.enabled
}
#[must_use]
pub const fn is_device_video_enabled(&self) -> bool {
self.device_video.enabled()
}
#[must_use]
pub const fn is_display_video_enabled(&self) -> bool {
self.display_video.enabled()
}
#[must_use]
pub const fn enabled(&self, kind: &MediaType) -> bool {
match kind {
MediaType::Video(video) => self.is_track_enabled_and_constrained(
MediaKind::Video,
Some(video.source_kind),
),
MediaType::Audio(_) => self.is_track_enabled_and_constrained(
MediaKind::Audio,
Some(MediaSourceKind::Device),
),
}
}
#[must_use]
pub const fn muted(&self, kind: &MediaType) -> bool {
match kind {
MediaType::Video(video) => match video.source_kind {
MediaSourceKind::Device => self.device_video.muted,
MediaSourceKind::Display => self.display_video.muted,
},
MediaType::Audio(_) => self.audio.muted,
}
}
#[must_use]
pub const fn is_track_enabled_and_constrained(
&self,
kind: MediaKind,
source: Option<MediaSourceKind>,
) -> bool {
match (kind, source) {
(MediaKind::Video, Some(MediaSourceKind::Device)) => {
self.device_video.enabled()
}
(MediaKind::Video, Some(MediaSourceKind::Display)) => {
self.display_video.enabled()
}
(MediaKind::Video, None) => {
self.display_video.enabled() && self.device_video.enabled()
}
(MediaKind::Audio, _) => self.audio.enabled,
}
}
#[must_use]
pub const fn is_track_enabled(
&self,
kind: MediaKind,
source: Option<MediaSourceKind>,
) -> bool {
match (kind, source) {
(MediaKind::Video, Some(MediaSourceKind::Device)) => {
self.device_video.enabled
}
(MediaKind::Video, Some(MediaSourceKind::Display)) => {
self.display_video.enabled
}
(MediaKind::Video, None) => {
self.display_video.enabled && self.device_video.enabled
}
(MediaKind::Audio, _) => self.audio.enabled,
}
}
fn constrain(&mut self, other: Self) {
self.audio.enabled &= other.audio.enabled;
self.audio.constraints = other.audio.constraints;
self.display_video.constrain(other.display_video);
self.device_video.constrain(other.device_video);
}
}
#[derive(Debug)]
pub enum MultiSourceTracksConstraints {
Device(platform::MediaStreamConstraints),
Display(platform::DisplayMediaStreamConstraints),
DeviceAndDisplay(
platform::MediaStreamConstraints,
platform::DisplayMediaStreamConstraints,
),
}
impl From<MediaStreamSettings> for Option<MultiSourceTracksConstraints> {
fn from(constraints: MediaStreamSettings) -> Self {
let is_device_video_enabled = constraints.is_device_video_enabled();
let is_display_video_enabled = constraints.is_display_video_enabled();
let is_device_audio_enabled = constraints.is_audio_enabled();
let mut device_cons = None;
let mut display_cons = None;
if is_device_video_enabled {
if let Some(device_video_cons) =
constraints.device_video.constraints
{
device_cons
.get_or_insert_with(platform::MediaStreamConstraints::new)
.video(device_video_cons);
}
}
if is_display_video_enabled {
if let Some(display_video_cons) =
constraints.display_video.constraints
{
display_cons
.get_or_insert_with(
platform::DisplayMediaStreamConstraints::new,
)
.video(display_video_cons);
}
}
if is_device_audio_enabled {
device_cons
.get_or_insert_with(platform::MediaStreamConstraints::new)
.audio(constraints.audio.constraints);
}
match (device_cons, display_cons) {
(Some(device_cons), Some(display_cons)) => {
Some(MultiSourceTracksConstraints::DeviceAndDisplay(
device_cons,
display_cons,
))
}
(Some(device_cons), None) => {
Some(MultiSourceTracksConstraints::Device(device_cons))
}
(None, Some(display_cons)) => {
Some(MultiSourceTracksConstraints::Display(display_cons))
}
(None, None) => None,
}
}
}
#[derive(Clone, Debug)]
pub enum VideoSource {
Device(DeviceVideoTrackConstraints),
Display(DisplayVideoTrackConstraints),
}
impl VideoSource {
#[expect(clippy::use_self, reason = "because of `const` only")]
#[must_use]
pub const fn required(&self) -> bool {
match self {
VideoSource::Device(device) => device.required,
VideoSource::Display(display) => display.required,
}
}
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
match self {
Self::Display(display) => display.satisfies(&track).await,
Self::Device(device) => device.satisfies(track).await,
}
}
}
impl From<VideoSettings> for VideoSource {
fn from(settings: VideoSettings) -> Self {
match settings.source_kind {
MediaSourceKind::Device => {
Self::Device(DeviceVideoTrackConstraints {
device_id: None,
facing_mode: None,
width: None,
height: None,
required: settings.required,
})
}
MediaSourceKind::Display => {
Self::Display(DisplayVideoTrackConstraints {
height: None,
width: None,
frame_rate: None,
required: settings.required,
device_id: None,
})
}
}
}
}
#[derive(Clone, Debug)]
pub enum TrackConstraints {
Audio(AudioTrackConstraints),
Video(VideoSource),
}
impl TrackConstraints {
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
match self {
Self::Audio(audio) => audio.satisfies(&track).await,
Self::Video(video) => video.satisfies(&track).await,
}
}
#[expect(clippy::use_self, reason = "because of `const` only")]
#[must_use]
pub const fn required(&self) -> bool {
match self {
TrackConstraints::Video(video) => video.required(),
TrackConstraints::Audio(audio) => audio.required,
}
}
#[expect(clippy::use_self, reason = "because of `const` only")]
#[must_use]
pub const fn media_source_kind(&self) -> MediaSourceKind {
match &self {
TrackConstraints::Audio(..) => MediaSourceKind::Device,
TrackConstraints::Video(VideoSource::Device(..)) => {
MediaSourceKind::Device
}
TrackConstraints::Video(VideoSource::Display(..)) => {
MediaSourceKind::Display
}
}
}
#[expect(clippy::use_self, reason = "because of `const` only")]
#[must_use]
pub const fn media_kind(&self) -> MediaKind {
match &self {
TrackConstraints::Audio(..) => MediaKind::Audio,
TrackConstraints::Video(..) => MediaKind::Video,
}
}
}
impl From<ProtoTrackConstraints> for TrackConstraints {
fn from(caps: ProtoTrackConstraints) -> Self {
match caps {
ProtoTrackConstraints::Audio(audio) => Self::Audio(audio.into()),
ProtoTrackConstraints::Video(video) => Self::Video(video.into()),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct AudioTrackConstraints {
pub device_id: Option<ConstrainString<String>>,
pub auto_gain_control: Option<ConstrainBoolean>,
pub required: bool,
}
impl AudioTrackConstraints {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn device_id(&mut self, device_id: String) {
self.device_id = Some(ConstrainString::Exact(device_id));
}
pub const fn exact_auto_gain_control(&mut self, auto_gain_control: bool) {
self.auto_gain_control =
Some(ConstrainBoolean::Exact(auto_gain_control));
}
pub const fn ideal_auto_gain_control(&mut self, auto_gain_control: bool) {
self.auto_gain_control =
Some(ConstrainBoolean::Ideal(auto_gain_control));
}
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
let track = track.as_ref();
satisfies_track(track, MediaKind::Audio).await
&& ConstrainString::satisfies(
self.device_id.as_ref(),
track.device_id().as_ref(),
)
}
pub fn merge(&mut self, another: Self) {
if self.device_id.is_none() && another.device_id.is_some() {
self.device_id = another.device_id;
}
if !self.required && another.required {
self.required = another.required;
}
}
#[must_use]
pub const fn required(&self) -> bool {
self.required
}
}
impl From<ProtoAudioConstraints> for AudioTrackConstraints {
fn from(caps: ProtoAudioConstraints) -> Self {
Self {
required: caps.required,
device_id: None,
auto_gain_control: None,
}
}
}
impl AsRef<str> for FacingMode {
fn as_ref(&self) -> &str {
match self {
Self::User => "user",
Self::Environment => "environment",
Self::Left => "left",
Self::Right => "right",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ConstrainU32 {
Exact(u32),
Ideal(u32),
Range(u32, u32),
}
impl ConstrainU32 {
fn satisfies(this: Option<Self>, setting: Option<u32>) -> bool {
match this {
None | Some(Self::Ideal(_)) => true,
Some(Self::Exact(exact)) => setting.is_some_and(|val| val == exact),
Some(Self::Range(start, end)) => {
setting.is_some_and(|val| val >= start && val <= end)
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ConstrainString<T> {
Exact(T),
Ideal(T),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ConstrainBoolean {
Exact(bool),
Ideal(bool),
}
impl<T: AsRef<str>> ConstrainString<T> {
fn satisfies(this: Option<&Self>, setting: Option<&T>) -> bool {
match this {
None | Some(Self::Ideal(..)) => true,
Some(Self::Exact(constrain)) => {
setting.is_some_and(|val| val.as_ref() == constrain.as_ref())
}
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DeviceVideoTrackConstraints {
pub required: bool,
pub device_id: Option<ConstrainString<String>>,
pub facing_mode: Option<ConstrainString<FacingMode>>,
pub height: Option<ConstrainU32>,
pub width: Option<ConstrainU32>,
}
impl DeviceVideoTrackConstraints {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn device_id(&mut self, device_id: String) {
self.device_id = Some(ConstrainString::Exact(device_id));
}
pub const fn exact_facing_mode(&mut self, facing_mode: FacingMode) {
self.facing_mode = Some(ConstrainString::Exact(facing_mode));
}
pub const fn ideal_facing_mode(&mut self, facing_mode: FacingMode) {
self.facing_mode = Some(ConstrainString::Ideal(facing_mode));
}
pub const fn exact_height(&mut self, height: u32) {
self.height = Some(ConstrainU32::Exact(height));
}
pub const fn ideal_height(&mut self, height: u32) {
self.height = Some(ConstrainU32::Ideal(height));
}
pub const fn height_in_range(&mut self, min: u32, max: u32) {
self.height = Some(ConstrainU32::Range(min, max));
}
pub const fn exact_width(&mut self, width: u32) {
self.width = Some(ConstrainU32::Exact(width));
}
pub const fn ideal_width(&mut self, width: u32) {
self.width = Some(ConstrainU32::Ideal(width));
}
pub const fn width_in_range(&mut self, min: u32, max: u32) {
self.width = Some(ConstrainU32::Range(min, max));
}
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
let track = track.as_ref();
satisfies_track(track, MediaKind::Video).await
&& ConstrainString::satisfies(
self.device_id.as_ref(),
track.device_id().as_ref(),
)
&& ConstrainString::satisfies(
self.facing_mode.as_ref(),
track.facing_mode().as_ref(),
)
&& ConstrainU32::satisfies(self.height, track.height())
&& ConstrainU32::satisfies(self.width, track.width())
&& !track.guess_is_from_display()
}
pub fn merge(&mut self, another: Self) {
if self.device_id.is_none() && another.device_id.is_some() {
self.device_id = another.device_id;
}
if !self.required && another.required {
self.required = another.required;
}
if self.facing_mode.is_none() && another.facing_mode.is_some() {
self.facing_mode = another.facing_mode;
}
if self.height.is_none() && another.height.is_some() {
self.height = another.height;
}
if self.width.is_none() && another.width.is_some() {
self.width = another.width;
}
}
#[must_use]
pub const fn required(&self) -> bool {
self.required
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DisplayVideoTrackConstraints {
pub required: bool,
pub device_id: Option<ConstrainString<String>>,
pub height: Option<ConstrainU32>,
pub width: Option<ConstrainU32>,
pub frame_rate: Option<ConstrainU32>,
}
impl DisplayVideoTrackConstraints {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub async fn satisfies<T: AsRef<platform::MediaStreamTrack>>(
&self,
track: T,
) -> bool {
let track = track.as_ref();
satisfies_track(track, MediaKind::Video).await
&& ConstrainString::satisfies(
self.device_id.as_ref(),
track.device_id().as_ref(),
)
&& ConstrainU32::satisfies(self.height, track.height())
&& ConstrainU32::satisfies(self.width, track.width())
&& track.guess_is_from_display()
}
pub fn merge(&mut self, another: Self) {
if self.device_id.is_none() && another.device_id.is_some() {
self.device_id = another.device_id;
}
if !self.required && another.required {
self.required = another.required;
}
if self.height.is_none() && another.height.is_some() {
self.height = another.height;
}
if self.width.is_none() && another.width.is_some() {
self.width = another.width;
}
if self.frame_rate.is_none() && another.frame_rate.is_some() {
self.frame_rate = another.frame_rate;
}
}
pub const fn exact_height(&mut self, height: u32) {
self.height = Some(ConstrainU32::Exact(height));
}
pub const fn ideal_height(&mut self, height: u32) {
self.height = Some(ConstrainU32::Ideal(height));
}
pub const fn exact_width(&mut self, width: u32) {
self.width = Some(ConstrainU32::Exact(width));
}
pub const fn ideal_width(&mut self, width: u32) {
self.width = Some(ConstrainU32::Ideal(width));
}
pub fn device_id(&mut self, device_id: String) {
self.device_id = Some(ConstrainString::Exact(device_id));
}
pub const fn exact_frame_rate(&mut self, frame_rate: u32) {
self.frame_rate = Some(ConstrainU32::Exact(frame_rate));
}
pub const fn ideal_frame_rate(&mut self, frame_rate: u32) {
self.frame_rate = Some(ConstrainU32::Ideal(frame_rate));
}
#[must_use]
pub const fn required(&self) -> bool {
self.required
}
}