use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use crate::context::{AudioContextLatencyCategory, AudioContextOptions};
use crate::media_streams::MediaStream;
pub fn enumerate_devices_sync() -> Vec<MediaDeviceInfo> {
crate::io::enumerate_devices_sync()
}
#[derive(Hash)]
pub(crate) struct DeviceId {
kind: MediaDeviceInfoKind,
host: String,
device_name: String,
num_channels: u16,
index: u8,
}
impl DeviceId {
pub(crate) fn as_string(
kind: MediaDeviceInfoKind,
host: String,
device_name: String,
num_channels: u16,
index: u8,
) -> String {
let device_info = Self {
kind,
host,
device_name,
num_channels,
index,
};
let mut hasher = DefaultHasher::new();
device_info.hash(&mut hasher);
format!("{}", hasher.finish())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum MediaDeviceInfoKind {
VideoInput,
AudioInput,
AudioOutput,
}
#[derive(Debug)]
pub struct MediaDeviceInfo {
device_id: String,
group_id: Option<String>,
kind: MediaDeviceInfoKind,
label: String,
device: Box<dyn std::any::Any>,
}
impl MediaDeviceInfo {
pub(crate) fn new(
device_id: String,
group_id: Option<String>,
kind: MediaDeviceInfoKind,
label: String,
device: Box<dyn std::any::Any>,
) -> Self {
Self {
device_id,
group_id,
kind,
label,
device,
}
}
pub fn device_id(&self) -> &str {
&self.device_id
}
pub fn group_id(&self) -> Option<&str> {
self.group_id.as_deref()
}
pub fn kind(&self) -> MediaDeviceInfoKind {
self.kind
}
pub fn label(&self) -> &str {
&self.label
}
pub(crate) fn device(self) -> Box<dyn std::any::Any> {
self.device
}
}
#[derive(Clone, Debug)]
pub enum MediaStreamConstraints {
Audio,
AudioWithConstraints(MediaTrackConstraints),
}
#[derive(Default, Debug, Clone)]
#[non_exhaustive]
pub struct MediaTrackConstraints {
pub sample_rate: Option<f32>,
pub latency: Option<f64>,
pub channel_count: Option<u32>, pub device_id: Option<String>,
}
impl From<MediaTrackConstraints> for AudioContextOptions {
fn from(value: MediaTrackConstraints) -> Self {
let latency_hint = match value.latency {
Some(v) => AudioContextLatencyCategory::Custom(v),
None => AudioContextLatencyCategory::Interactive,
};
let sink_id = value.device_id.unwrap_or(String::from(""));
AudioContextOptions {
latency_hint,
sample_rate: value.sample_rate,
sink_id,
render_size_hint: Default::default(),
}
}
}
fn is_valid_device_id(device_id: &str) -> bool {
if device_id.is_empty() {
true
} else {
enumerate_devices_sync()
.into_iter()
.filter(|d| d.kind == MediaDeviceInfoKind::AudioInput)
.any(|d| d.device_id() == device_id)
}
}
pub fn get_user_media_sync(constraints: MediaStreamConstraints) -> MediaStream {
let (channel_count, mut options) = match constraints {
MediaStreamConstraints::Audio => (None, AudioContextOptions::default()),
MediaStreamConstraints::AudioWithConstraints(cs) => (cs.channel_count, cs.into()),
};
if !is_valid_device_id(&options.sink_id) {
log::error!("NotFoundError: invalid deviceId {:?}", options.sink_id);
options.sink_id = String::from("");
}
crate::io::build_input(options, channel_count)
}