1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! [MediaDeviceInfo][0] related representations.
//!
//! [0]: https://w3.org/TR/mediacapture-streams#device-info
use medea_macro::dart_bridge;
use crate::{
media::{AudioDeviceKind, MediaDeviceKind},
platform::dart::utils::{
NonNullDartValueArgExt as _, dart_string_into_rust, handle::DartHandle,
},
};
#[dart_bridge("flutter/lib/src/native/platform/media_device_info.g.dart")]
mod media_device_info {
use std::{os::raw::c_char, ptr};
use dart_sys::Dart_Handle;
use crate::{api::DartValueArg, platform::Error};
extern "C" {
/// Returns an unique identifier of the provided device.
pub fn device_id(
info: Dart_Handle,
) -> Result<ptr::NonNull<c_char>, Error>;
/// Returns a kind of the provided device.
pub fn kind(info: Dart_Handle) -> Result<i64, Error>;
/// Returns an audio device kind index of the provided device, if
/// present, or `-1` otherwise.
pub fn audio_device_kind(info: Dart_Handle) -> Result<i64, Error>;
/// Returns a label describing the provided device (for example,
/// "External USB Webcam").
///
/// If the provided device has no associated label, then returns an
/// empty string.
pub fn label(info: Dart_Handle) -> Result<ptr::NonNull<c_char>, Error>;
/// Returns a group identifier of the provided device.
pub fn group_id(
info: Dart_Handle,
) -> Result<ptr::NonNull<DartValueArg<Option<String>>>, Error>;
/// Returns native sample rate in `Hz` for the provided device.
pub fn sample_rate(info: Dart_Handle) -> Result<i64, Error>;
/// Returns number of channels for the provided device.
pub fn num_channels(info: Dart_Handle) -> Result<i64, Error>;
/// Indicates whether the last attempt to use the provided device
/// failed.
pub fn is_failed(info: Dart_Handle) -> Result<bool, Error>;
}
}
/// Representation of a [MediaDeviceInfo][0] ONLY for input devices.
///
/// [0]: https://w3.org/TR/mediacapture-streams#device-info
#[derive(Clone, Debug)]
pub struct MediaDeviceInfo {
/// Handle to the Dart side `MediaDeviceInfo`.
handle: DartHandle,
/// [`MediaDeviceKind`] of this [`MediaDeviceInfo`].
kind: MediaDeviceKind,
}
impl MediaDeviceInfo {
/// Returns a unique identifier of the device represented by this
/// [`MediaDeviceInfo`].
#[must_use]
pub fn device_id(&self) -> String {
let device_id =
unsafe { media_device_info::device_id(self.handle.get()) }.unwrap();
unsafe { dart_string_into_rust(device_id) }
}
/// Returns a kind of the device represented by this [`MediaDeviceInfo`].
#[must_use]
pub const fn kind(&self) -> MediaDeviceKind {
self.kind
}
/// Returns a label describing the device represented by this
/// [`MediaDeviceInfo`] (for example, "External USB Webcam").
///
/// If the device has no associated label, then returns an empty string.
#[must_use]
pub fn label(&self) -> String {
let label =
unsafe { media_device_info::label(self.handle.get()) }.unwrap();
unsafe { dart_string_into_rust(label) }
}
/// Returns a group identifier of the device represented by this
/// [`MediaDeviceInfo`].
///
/// Two devices have the same group identifier if they belong to the same
/// physical device. For example, the audio input and output devices
/// representing the speaker and microphone of the same headset have the
/// same [groupId][1].
///
/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediadeviceinfo-groupid
#[must_use]
pub fn group_id(&self) -> Option<String> {
let group_id =
unsafe { media_device_info::group_id(self.handle.get()) }.unwrap();
Option::try_from(unsafe { group_id.unbox() }).unwrap()
}
/// Native sample rate in Hz.
///
/// For audio devices only. [`None`] for video or if unavailable.
#[must_use]
pub fn sample_rate(&self) -> Option<u32> {
let sample_rate =
unsafe { media_device_info::sample_rate(self.handle.get()) }
.unwrap();
if sample_rate > 0 { u32::try_from(sample_rate).ok() } else { None }
}
/// Number of channels.
///
/// For audio devices only. [`None`] for video or if unavailable.
#[must_use]
pub fn num_channels(&self) -> Option<u16> {
let channels =
unsafe { media_device_info::num_channels(self.handle.get()) }
.unwrap();
if channels > 0 { u16::try_from(channels).ok() } else { None }
}
/// Indicates whether the last attempt to use this device failed.
#[must_use]
pub fn is_failed(&self) -> bool {
unsafe { media_device_info::is_failed(self.handle.get()) }.unwrap()
}
/// Returns an [`AudioDeviceKind`] of this [`MediaDeviceInfo`], if
/// applicable.
#[must_use]
pub fn audio_device_kind(&self) -> Option<AudioDeviceKind> {
let v =
unsafe { media_device_info::audio_device_kind(self.handle.get()) }
.unwrap();
AudioDeviceKind::try_from(v).ok()
}
}
impl TryFrom<DartHandle> for MediaDeviceInfo {
type Error = NotInput;
#[expect(clippy::unwrap_in_result, reason = "unrelated")]
fn try_from(value: DartHandle) -> Result<Self, Self::Error> {
#[expect(clippy::map_err_ignore, reason = "not useful")]
let kind = unsafe { media_device_info::kind(value.get()) }
.unwrap()
.try_into()
.map_err(|_| NotInput)?;
Ok(Self { handle: value, kind })
}
}
/// Error of a [MediaDeviceInfo][0] representing not an input device.
///
/// [0]: https://w3.org/TR/mediacapture-streams#device-info
#[derive(Clone, Copy, Debug)]
pub struct NotInput;