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