script/dom/media/
mediadevices.rs1use 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 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 fn EnumerateDevices(&self, cx: &mut JSContext) -> Rc<Promise> {
78 let mut realm = CurrentRealm::assert(cx);
80 let p = Promise::new_in_realm(&mut realm);
81
82 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 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 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}