Skip to main content

script/dom/media/
mediadevices.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::realm::CurrentRealm;
8use script_bindings::reflector::reflect_dom_object_with_cx;
9use servo_media::ServoMedia;
10use servo_media::streams::MediaStreamType;
11use servo_media::streams::capture::{Constrain, ConstrainRange, MediaTrackConstraintSet};
12
13use crate::conversions::Convert;
14use crate::dom::bindings::codegen::Bindings::MediaDevicesBinding::{
15    MediaDevicesMethods, MediaStreamConstraints,
16};
17use crate::dom::bindings::codegen::UnionTypes::{
18    BooleanOrMediaTrackConstraints, ClampedUnsignedLongOrConstrainULongRange as ConstrainULong,
19    DoubleOrConstrainDoubleRange as ConstrainDouble,
20};
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::eventtarget::EventTarget;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::media::mediadeviceinfo::MediaDeviceInfo;
26use crate::dom::media::mediastream::MediaStream;
27use crate::dom::media::mediastreamtrack::MediaStreamTrack;
28use crate::dom::promise::{Promise, RootedPromise};
29
30#[dom_struct]
31pub(crate) struct MediaDevices {
32    eventtarget: EventTarget,
33}
34
35impl MediaDevices {
36    pub(crate) fn new_inherited() -> MediaDevices {
37        MediaDevices {
38            eventtarget: EventTarget::new_inherited(),
39        }
40    }
41
42    pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<MediaDevices> {
43        reflect_dom_object_with_cx(Box::new(MediaDevices::new_inherited()), global, cx)
44    }
45}
46
47impl MediaDevicesMethods<crate::DomTypeHolder> for MediaDevices {
48    /// <https://w3c.github.io/mediacapture-main/#dom-mediadevices-getusermedia>
49    fn GetUserMedia(
50        &self,
51        cx: &mut CurrentRealm,
52        constraints: &MediaStreamConstraints,
53    ) -> RootedPromise {
54        let p = Promise::new_in_realm_rooted(cx);
55        let media = ServoMedia::get();
56        let stream = MediaStream::new(cx, &self.global());
57        if let Some(constraints) = convert_constraints(&constraints.audio) &&
58            let Some(audio) = media.create_audioinput_stream(constraints)
59        {
60            let track = MediaStreamTrack::new(cx, &self.global(), audio, MediaStreamType::Audio);
61            stream.add_track(&track);
62        }
63        if let Some(constraints) = convert_constraints(&constraints.video) &&
64            let Some(video) = media.create_videoinput_stream(constraints)
65        {
66            let track = MediaStreamTrack::new(cx, &self.global(), video, MediaStreamType::Video);
67            stream.add_track(&track);
68        }
69
70        p.resolve_native(cx, &stream);
71        p
72    }
73
74    /// <https://w3c.github.io/mediacapture-main/#dom-mediadevices-enumeratedevices>
75    fn EnumerateDevices(&self, cx: &mut JSContext) -> RootedPromise {
76        // Step 1.
77        let mut realm = CurrentRealm::assert(cx);
78        let p = Promise::new_in_realm_rooted(&mut realm);
79
80        // Step 2.
81        // XXX These steps should be run in parallel.
82        // XXX Steps 2.1 - 2.4
83
84        // Step 2.5
85        let media = ServoMedia::get();
86        let device_monitor = media.get_device_monitor();
87        let result_list = device_monitor
88            .enumerate_devices()
89            .map(|devices| {
90                devices
91                    .iter()
92                    .map(|device| {
93                        // XXX The media backend has no way to group devices yet.
94                        MediaDeviceInfo::new(
95                            cx,
96                            &self.global(),
97                            &device.device_id,
98                            device.kind.convert(),
99                            &device.label,
100                            "",
101                        )
102                    })
103                    .collect::<Vec<_>>()
104            })
105            .unwrap_or_default();
106
107        p.resolve_native(cx, &result_list);
108
109        // Step 3.
110        p
111    }
112}
113
114fn convert_constraints(js: &BooleanOrMediaTrackConstraints) -> Option<MediaTrackConstraintSet> {
115    match js {
116        BooleanOrMediaTrackConstraints::Boolean(false) => None,
117        BooleanOrMediaTrackConstraints::Boolean(true) => Some(Default::default()),
118        BooleanOrMediaTrackConstraints::MediaTrackConstraints(c) => Some(MediaTrackConstraintSet {
119            height: c.parent.height.as_ref().and_then(convert_culong),
120            width: c.parent.width.as_ref().and_then(convert_culong),
121            aspect: c.parent.aspectRatio.as_ref().and_then(convert_cdouble),
122            frame_rate: c.parent.frameRate.as_ref().and_then(convert_cdouble),
123            sample_rate: c.parent.sampleRate.as_ref().and_then(convert_culong),
124        }),
125    }
126}
127
128fn convert_culong(js: &ConstrainULong) -> Option<Constrain<u32>> {
129    match js {
130        ConstrainULong::ClampedUnsignedLong(val) => Some(Constrain::Value(*val)),
131        ConstrainULong::ConstrainULongRange(range) => {
132            if range.parent.min.is_some() || range.parent.max.is_some() {
133                Some(Constrain::Range(ConstrainRange {
134                    min: range.parent.min,
135                    max: range.parent.max,
136                    ideal: range.ideal,
137                }))
138            } else {
139                range.exact.map(Constrain::Value)
140            }
141        },
142    }
143}
144
145fn convert_cdouble(js: &ConstrainDouble) -> Option<Constrain<f64>> {
146    match js {
147        ConstrainDouble::Double(val) => Some(Constrain::Value(**val)),
148        ConstrainDouble::ConstrainDoubleRange(range) => {
149            if range.parent.min.is_some() || range.parent.max.is_some() {
150                Some(Constrain::Range(ConstrainRange {
151                    min: range.parent.min.map(|x| *x),
152                    max: range.parent.max.map(|x| *x),
153                    ideal: range.ideal.map(|x| *x),
154                }))
155            } else {
156                range.exact.map(|exact| Constrain::Value(*exact))
157            }
158        },
159    }
160}