medea_jason/platform/dart/
media_devices.rs

1//! Representation of [MediaDevices][0].
2//!
3//! [0]: https://w3.org/TR/mediacapture-streams#mediadevices
4
5use medea_macro::dart_bridge;
6use tracerr::Traced;
7
8use super::{
9    constraints::{DisplayMediaStreamConstraints, MediaStreamConstraints},
10    media_device_info::MediaDeviceInfo,
11    media_display_info::MediaDisplayInfo,
12    media_track::MediaStreamTrack,
13};
14use crate::{
15    media::MediaSourceKind,
16    platform::{
17        Error, GetUserMediaError,
18        dart::utils::{
19            dart_future::FutureFromDart, handle::DartHandle, list::DartList,
20            string_into_c_str,
21        },
22        utils::callback::Callback,
23    },
24};
25
26#[dart_bridge("flutter/lib/src/native/platform/media_devices.g.dart")]
27mod media_devices {
28    use std::{os::raw::c_char, ptr};
29
30    use dart_sys::Dart_Handle;
31
32    use crate::platform::Error;
33
34    extern "C" {
35        /// Returns information about available media input devices.
36        pub fn enumerate_devices() -> Result<Dart_Handle, Error>;
37
38        /// Returns information about available displays.
39        pub fn enumerate_displays() -> Result<Dart_Handle, Error>;
40
41        /// Prompts a user for permissions to use a media input device,
42        /// producing a vector of [MediaStreamTrack][1]s containing the
43        /// requested types of media.
44        ///
45        /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
46        pub fn get_user_media(
47            constraints: Dart_Handle,
48        ) -> Result<Dart_Handle, Error>;
49
50        /// Prompts a user to select and grant permissions to capture contents
51        /// of a display or portion thereof (such as a single window), producing
52        /// a vector of [MediaStreamTrack][1]s containing the requested types
53        /// of media.
54        ///
55        /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
56        pub fn get_display_media(
57            constraints: Dart_Handle,
58        ) -> Result<Dart_Handle, Error>;
59
60        /// Switches the current output audio device to the device with the
61        /// provided `device_id`.
62        pub fn set_output_audio_id(
63            device_id: ptr::NonNull<c_char>,
64        ) -> Result<Dart_Handle, Error>;
65
66        /// Indicates whether it's possible to access microphone volume
67        /// settings.
68        pub fn microphone_volume_is_available() -> Result<Dart_Handle, Error>;
69
70        /// Returns the current microphone volume level in percents.
71        pub fn microphone_volume() -> Result<Dart_Handle, Error>;
72
73        /// Sets the microphone volume level in percents.
74        pub fn set_microphone_volume(level: i64) -> Result<Dart_Handle, Error>;
75
76        /// Subscribes onto the `MediaDevices`'s `devicechange` event.
77        pub fn on_device_change(cb: Dart_Handle) -> Result<(), Error>;
78
79        /// Returns the kind of the Dart side `GetMediaException`.
80        pub fn get_media_exception_kind(
81            exception: Dart_Handle,
82        ) -> Result<i64, Error>;
83    }
84}
85
86#[expect(clippy::fallible_impl_from, reason = "FFI error is unexpected")]
87impl From<Error> for GetUserMediaError {
88    fn from(err: Error) -> Self {
89        let kind = unsafe {
90            media_devices::get_media_exception_kind(err.get_handle())
91        }
92        .unwrap();
93
94        match kind {
95            0 => Self::Audio(err),
96            1 => Self::Video(err),
97            _ => Self::Unknown(err),
98        }
99    }
100}
101
102/// Media devices controller.
103#[derive(Clone, Copy, Debug, Default)]
104pub struct MediaDevices;
105
106impl MediaDevices {
107    /// Collects information about available media input devices.
108    ///
109    /// Adapter for the [MediaDevices.enumerateDevices()][1] function.
110    ///
111    /// # Errors
112    ///
113    /// If [MediaDevices.enumerateDevices()][1] errors itself or unable to get
114    /// [MediaDevices][2].
115    ///
116    /// [1]: https://tinyurl.com/w3-streams#dom-mediadevices-enumeratedevices
117    /// [2]: https://w3.org/TR/mediacapture-streams#mediadevices
118    pub async fn enumerate_devices(
119        &self,
120    ) -> Result<Vec<MediaDeviceInfo>, Traced<Error>> {
121        let fut = unsafe { media_devices::enumerate_devices() }.unwrap();
122        let devices = unsafe { FutureFromDart::execute::<DartHandle>(fut) }
123            .await
124            .map(DartList::from)
125            .map_err(tracerr::wrap!())?;
126
127        let len = devices.length();
128        let mut result = Vec::with_capacity(len);
129        for i in 0..len {
130            let val = devices.get(i).unwrap();
131            if let Ok(v) = val.try_into() {
132                result.push(v);
133            }
134        }
135        Ok(result)
136    }
137
138    /// Collects information about available displays.
139    ///
140    /// # Errors
141    ///
142    /// If platform call returns error.
143    pub async fn enumerate_displays(
144        &self,
145    ) -> Result<Vec<MediaDisplayInfo>, Traced<Error>> {
146        let fut = unsafe { media_devices::enumerate_displays() }.unwrap();
147        let displays = unsafe { FutureFromDart::execute::<DartHandle>(fut) }
148            .await
149            .map(DartList::from)
150            .map_err(tracerr::from_and_wrap!())?;
151
152        Ok(<Vec<DartHandle>>::from(displays)
153            .into_iter()
154            .map(MediaDisplayInfo::from)
155            .collect())
156    }
157
158    /// Prompts a user for permissions to use a media input device, producing
159    /// [`MediaStreamTrack`]s containing the requested types of media.
160    ///
161    /// Adapter for the [MediaDevices.getUserMedia()][1] function.
162    ///
163    /// # Errors
164    ///
165    /// If [MediaDevices.getUserMedia()][1] errors itself or unable to get
166    /// [MediaDevices][2].
167    ///
168    /// [1]: https://tinyurl.com/w3-streams#dom-mediadevices-getusermedia
169    /// [2]: https://w3.org/TR/mediacapture-streams#mediadevices
170    pub async fn get_user_media(
171        &self,
172        caps: MediaStreamConstraints,
173    ) -> Result<Vec<MediaStreamTrack>, Traced<GetUserMediaError>> {
174        let fut =
175            unsafe { media_devices::get_user_media(caps.into()) }.unwrap();
176        let tracks = unsafe { FutureFromDart::execute::<DartHandle>(fut) }
177            .await
178            .map_err(tracerr::from_and_wrap!())?;
179
180        let tracks = Vec::from(DartList::from(tracks))
181            .into_iter()
182            .map(|track| {
183                MediaStreamTrack::new(track, Some(MediaSourceKind::Device))
184            })
185            .collect();
186
187        Ok(tracks)
188    }
189
190    /// Prompts a user to select and grant permissions to capture contents of a
191    /// display or portion thereof (such as a single window), producing
192    /// [`MediaStreamTrack`]s containing the requested types of media.
193    ///
194    /// Adapter for a [MediaDevices.getDisplayMedia()][1] function.
195    ///
196    /// # Errors
197    ///
198    /// If [MediaDevices.getDisplayMedia()][1] errors itself or unable to get
199    /// [MediaDevices][2].
200    ///
201    /// [1]: https://w3.org/TR/screen-capture#dom-mediadevices-getdisplaymedia
202    /// [2]: https://w3.org/TR/mediacapture-streams#mediadevices
203    pub async fn get_display_media(
204        &self,
205        caps: DisplayMediaStreamConstraints,
206    ) -> Result<Vec<MediaStreamTrack>, Traced<Error>> {
207        let fut =
208            unsafe { media_devices::get_display_media(caps.into()) }.unwrap();
209        let tracks = unsafe { FutureFromDart::execute::<DartHandle>(fut) }
210            .await
211            .map_err(tracerr::wrap!())?;
212
213        let tracks = Vec::from(DartList::from(tracks))
214            .into_iter()
215            .map(|track| {
216                MediaStreamTrack::new(track, Some(MediaSourceKind::Display))
217            })
218            .collect();
219
220        Ok(tracks)
221    }
222
223    /// Switches the current output audio device to the device with the provided
224    /// `device_id`.
225    ///
226    /// # Errors
227    ///
228    /// If output audio device with the provided `device_id` is not available.
229    pub async fn set_output_audio_id(
230        &self,
231        device_id: String,
232    ) -> Result<(), Traced<Error>> {
233        let fut = unsafe {
234            media_devices::set_output_audio_id(string_into_c_str(device_id))
235        }
236        .unwrap();
237        unsafe { FutureFromDart::execute::<()>(fut) }
238            .await
239            .map_err(tracerr::wrap!())
240    }
241
242    /// Indicates whether it's possible to access microphone volume settings.
243    pub async fn microphone_volume_is_available(&self) -> bool {
244        let fut =
245            unsafe { media_devices::microphone_volume_is_available() }.unwrap();
246
247        unsafe { FutureFromDart::execute::<bool>(fut) }.await.unwrap()
248    }
249
250    /// Returns the current microphone volume level in percents.
251    ///
252    /// # Errors
253    ///
254    /// If it the "Audio Device Module" is not initialized or there is no
255    /// connected audio input devices.
256    pub async fn microphone_volume(&self) -> Result<i64, Traced<Error>> {
257        let fut = unsafe { media_devices::microphone_volume() }.unwrap();
258        unsafe { FutureFromDart::execute::<i64>(fut) }
259            .await
260            .map_err(tracerr::wrap!())
261    }
262
263    /// Sets the microphone volume level in percents.
264    ///
265    /// # Errors
266    ///
267    /// If it the "Audio Device Module" is not initialized or there is no
268    /// connected audio input devices.
269    pub async fn set_microphone_volume(
270        &self,
271        level: i64,
272    ) -> Result<(), Traced<Error>> {
273        let fut =
274            unsafe { media_devices::set_microphone_volume(level) }.unwrap();
275        unsafe { FutureFromDart::execute::<()>(fut) }
276            .await
277            .map_err(tracerr::wrap!())
278    }
279
280    /// Subscribes onto the [`MediaDevices`]'s `devicechange` event.
281    pub fn on_device_change<F>(&self, handler: Option<F>)
282    where
283        F: 'static + FnMut(),
284    {
285        if let Some(mut h) = handler {
286            unsafe {
287                media_devices::on_device_change(
288                    Callback::from_fn_mut(move |(): ()| {
289                        h();
290                    })
291                    .into_dart(),
292                )
293            }
294            .unwrap();
295        }
296    }
297}