medea_jason/platform/dart/
media_devices.rs1use 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 pub fn enumerate_devices() -> Result<Dart_Handle, Error>;
37
38 pub fn enumerate_displays() -> Result<Dart_Handle, Error>;
40
41 pub fn get_user_media(
47 constraints: Dart_Handle,
48 ) -> Result<Dart_Handle, Error>;
49
50 pub fn get_display_media(
57 constraints: Dart_Handle,
58 ) -> Result<Dart_Handle, Error>;
59
60 pub fn set_output_audio_id(
63 device_id: ptr::NonNull<c_char>,
64 ) -> Result<Dart_Handle, Error>;
65
66 pub fn microphone_volume_is_available() -> Result<Dart_Handle, Error>;
69
70 pub fn microphone_volume() -> Result<Dart_Handle, Error>;
72
73 pub fn set_microphone_volume(level: i64) -> Result<Dart_Handle, Error>;
75
76 pub fn on_device_change(cb: Dart_Handle) -> Result<(), Error>;
78
79 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#[derive(Clone, Copy, Debug, Default)]
104pub struct MediaDevices;
105
106impl MediaDevices {
107 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 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 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 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 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 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 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 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 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}