Skip to content

Commit 9e67c6f

Browse files
committed
MediaDevices: add support for MediaTrackConstraints.channelCount
Fixes #446 TODO: channelCount should be modeled as a ConstrainULong, not just u32. TODO: cubeb support
1 parent 5e4ba6e commit 9e67c6f

File tree

6 files changed

+51
-23
lines changed

6 files changed

+51
-23
lines changed

examples/microphone.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ fn main() {
7272

7373
let mut constraints = MediaTrackConstraints::default();
7474
constraints.device_id = source_id;
75+
// constraints.channel_count = Some(2);
7576
let stream_constraints = MediaStreamConstraints::AudioWithConstraints(constraints);
7677
let mic = media_devices::get_user_media_sync(stream_constraints);
7778

src/io/cpal.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,10 @@ impl AudioBackendManager for CpalBackend {
266266
}
267267
}
268268

269-
fn build_input(options: AudioContextOptions) -> (Self, Receiver<AudioBuffer>)
269+
fn build_input(
270+
options: AudioContextOptions,
271+
number_of_channels: Option<u32>,
272+
) -> (Self, Receiver<AudioBuffer>)
270273
where
271274
Self: Sized,
272275
{
@@ -297,6 +300,10 @@ impl AudioBackendManager for CpalBackend {
297300
// clone the config, we may need to fall back on it later
298301
let mut preferred: StreamConfig = supported.clone().into();
299302

303+
if let Some(number_of_channels) = number_of_channels {
304+
preferred.channels = number_of_channels as u16;
305+
}
306+
300307
// set specific sample rate if requested
301308
if let Some(sample_rate) = options.sample_rate {
302309
crate::assert_valid_sample_rate(sample_rate);
@@ -315,32 +322,40 @@ impl AudioBackendManager for CpalBackend {
315322
};
316323

317324
preferred.buffer_size = cpal::BufferSize::Fixed(clamped_buffer_size);
318-
319-
let mut number_of_channels = usize::from(preferred.channels);
320325
let mut sample_rate = preferred.sample_rate.0 as f32;
326+
let mut number_of_channels = preferred.channels as usize;
321327

322328
let smoothing = 3; // todo, use buffering to smooth frame drops
323329
let (sender, mut receiver) = crossbeam_channel::bounded(smoothing);
324330
let renderer = MicrophoneRender::new(number_of_channels, sample_rate, sender);
325331

326-
let maybe_stream =
327-
spawn_input_stream(&device, supported.sample_format(), &preferred, renderer);
332+
log::debug!(
333+
"Attempt input stream with preferred config: {:?}",
334+
&preferred
335+
);
336+
337+
let spawned = spawn_input_stream(&device, supported.sample_format(), &preferred, renderer);
328338

329339
// the required block size preferred config may not be supported, in that
330340
// case, fallback the supported config
331-
let stream = match maybe_stream {
332-
Ok(stream) => stream,
341+
let stream = match spawned {
342+
Ok(stream) => {
343+
log::debug!("Input stream set up successfully");
344+
stream
345+
}
333346
Err(e) => {
334-
log::warn!(
335-
"Output stream failed to build: {:?}, retry with default config {:?}",
336-
e,
337-
preferred
338-
);
347+
log::warn!("Output stream build failed with preferred config: {}", e);
339348

340349
let supported_config: StreamConfig = supported.clone().into();
350+
// fallback to device default sample rate and channel count
341351
number_of_channels = usize::from(supported_config.channels);
342352
sample_rate = supported_config.sample_rate.0 as f32;
343353

354+
log::debug!(
355+
"Attempt output stream with fallback config: {:?}",
356+
&supported_config
357+
);
358+
344359
// setup a new comms channel
345360
let (sender, receiver2) = crossbeam_channel::bounded(smoothing);
346361
receiver = receiver2; // overwrite earlier

src/io/cubeb.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,10 @@ impl AudioBackendManager for CubebBackend {
269269
backend
270270
}
271271

272-
fn build_input(options: AudioContextOptions) -> (Self, Receiver<AudioBuffer>)
272+
fn build_input(
273+
options: AudioContextOptions,
274+
_number_of_channels: Option<u32>,
275+
) -> (Self, Receiver<AudioBuffer>)
273276
where
274277
Self: Sized,
275278
{

src/io/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ pub(crate) fn build_output(
112112
}
113113

114114
/// Set up an input stream (microphone) bases on the selected features (cubeb/cpal/none)
115-
pub(crate) fn build_input(options: AudioContextOptions) -> MediaStream {
115+
pub(crate) fn build_input(
116+
options: AudioContextOptions,
117+
number_of_channels: Option<u32>,
118+
) -> MediaStream {
116119
#[cfg(all(not(feature = "cubeb"), not(feature = "cpal")))]
117120
{
118121
panic!("No audio backend available, enable the 'cpal' or 'cubeb' feature")
@@ -123,12 +126,12 @@ pub(crate) fn build_input(options: AudioContextOptions) -> MediaStream {
123126
let (backend, receiver) = {
124127
#[cfg(feature = "cubeb")]
125128
{
126-
cubeb::CubebBackend::build_input(options)
129+
cubeb::CubebBackend::build_input(options, number_of_channels)
127130
}
128131

129132
#[cfg(all(not(feature = "cubeb"), feature = "cpal"))]
130133
{
131-
cpal::CpalBackend::build_input(options)
134+
cpal::CpalBackend::build_input(options, number_of_channels)
132135
}
133136
};
134137

@@ -151,7 +154,10 @@ pub(crate) trait AudioBackendManager: Send + Sync + 'static {
151154
Self: Sized;
152155

153156
/// Setup a new input stream (microphone capture)
154-
fn build_input(options: AudioContextOptions) -> (Self, Receiver<AudioBuffer>)
157+
fn build_input(
158+
options: AudioContextOptions,
159+
number_of_channels: Option<u32>,
160+
) -> (Self, Receiver<AudioBuffer>)
155161
where
156162
Self: Sized;
157163

src/io/none.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ impl AudioBackendManager for NoneBackend {
110110
}
111111

112112
/// Setup a new input stream (microphone capture)
113-
fn build_input(_options: AudioContextOptions) -> (Self, Receiver<AudioBuffer>)
113+
fn build_input(
114+
_options: AudioContextOptions,
115+
_number_of_channels: Option<u32>,
116+
) -> (Self, Receiver<AudioBuffer>)
114117
where
115118
Self: Sized,
116119
{

src/media_devices/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ pub struct MediaTrackConstraints {
148148
// ConstrainBoolean autoGainControl;
149149
// ConstrainBoolean noiseSuppression;
150150
pub latency: Option<f64>,
151-
//ConstrainULong channelCount;
151+
pub channel_count: Option<u32>, // TODO model as ConstrainULong;
152152
pub device_id: Option<String>,
153153
// ConstrainDOMString groupId;
154154
}
@@ -218,15 +218,15 @@ fn is_valid_device_id(device_id: &str) -> bool {
218218
/// std::thread::sleep(std::time::Duration::from_secs(4));
219219
/// ```
220220
pub fn get_user_media_sync(constraints: MediaStreamConstraints) -> MediaStream {
221-
let mut options = match constraints {
222-
MediaStreamConstraints::Audio => AudioContextOptions::default(),
223-
MediaStreamConstraints::AudioWithConstraints(cs) => cs.into(),
221+
let (channel_count, mut options) = match constraints {
222+
MediaStreamConstraints::Audio => (None, AudioContextOptions::default()),
223+
MediaStreamConstraints::AudioWithConstraints(cs) => (cs.channel_count, cs.into()),
224224
};
225225

226226
if !is_valid_device_id(&options.sink_id) {
227227
log::error!("NotFoundError: invalid deviceId {:?}", options.sink_id);
228228
options.sink_id = String::from("");
229229
}
230230

231-
crate::io::build_input(options)
231+
crate::io::build_input(options, channel_count)
232232
}

0 commit comments

Comments
 (0)