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