Bug 1874789 - Update cubeb-coreaudio-rs to d23ab55eab. r=cubeb-reviewers,supply-chain-reviewers,padenot

This update introduces a blocklist function for using VPIO, and
makes the platform AGC settable by param.

Differential Revision: https://phabricator.services.mozilla.com/D202404
This commit is contained in:
Andreas Pehrson 2024-02-23 14:22:18 +00:00
Родитель 787dc24b69
Коммит e5f94817e6
7 изменённых файлов: 301 добавлений и 91 удалений

Просмотреть файл

@ -65,9 +65,9 @@ git = "https://github.com/mozilla/audioipc"
rev = "596bdb7fbb5745ea415726e16bd497e6c850a540"
replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=4ba39ca14bbb5bb0274843701cd958717a0fe293"]
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=d23ab55eab684b46f46e1da177c8814f6103a009"]
git = "https://github.com/mozilla/cubeb-coreaudio-rs"
rev = "4ba39ca14bbb5bb0274843701cd958717a0fe293"
rev = "d23ab55eab684b46f46e1da177c8814f6103a009"
replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=8ff972c8e2ec1782ff262ac4071c0415e69b1367"]

4
Cargo.lock сгенерированный
Просмотреть файл

@ -899,7 +899,7 @@ dependencies = [
[[package]]
name = "coreaudio-sys-utils"
version = "0.1.0"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=4ba39ca14bbb5bb0274843701cd958717a0fe293#4ba39ca14bbb5bb0274843701cd958717a0fe293"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=d23ab55eab684b46f46e1da177c8814f6103a009#d23ab55eab684b46f46e1da177c8814f6103a009"
dependencies = [
"core-foundation-sys",
"coreaudio-sys",
@ -1110,7 +1110,7 @@ dependencies = [
[[package]]
name = "cubeb-coreaudio"
version = "0.1.0"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=4ba39ca14bbb5bb0274843701cd958717a0fe293#4ba39ca14bbb5bb0274843701cd958717a0fe293"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=d23ab55eab684b46f46e1da177c8814f6103a009#d23ab55eab684b46f46e1da177c8814f6103a009"
dependencies = [
"atomic",
"audio-mixer",

Просмотреть файл

@ -1 +1 @@
{"files":{".circleci/config.yml":"7f3dc865105ca8f33965a7958b1fe2e627ae2d5a703f3b2a4ab6e2e796018597",".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".github/workflows/test.yml":"aa1998a3b104ad131805ca3513832cef3f65300192824f8b1efc9a5a0cc108f6",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"d7e757e664c23fae52028f1dfc5917f92523c08702e3a1f95e1fd38ed714416c","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"0007782a05a5330f739ad789c19c82562c82e32386b0447000fc72c0d48405bc","build-audiounit-rust-in-cubeb.sh":"d228a05985dcd02ec1ecac66a2b64dae5a530804a25a7054ccc95905aedfb7ef","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"d717e598c96e4911d9494b18382d6bd3a8d5038b7d68d3166ad4336e237a97d8","run_sanitizers.sh":"84e93a0da137803018f37403511e8c92760be730426bf6cea34419d93d1a7ff8","run_tests.sh":"916a7ae4a406d2274417d6eca939a878db5adcb6144e5680d9d148bf90178f1c","src/backend/aggregate_device.rs":"43511107ba2a75a19340ac663c981362ca1b75b679b6c295d88b5035bd7e3619","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"e9bcf964347daa8952f98caa2746e34a31ea8908375204896593f56e4b6147ca","src/backend/device_property.rs":"a7622feaa41db1cd76fd35a85a022e44f4894e396a104a59008d5b8757d2ab4e","src/backend/mixer.rs":"ed299d3954e2a823060c870a8244673a7d4bca530830cb66b964d047a80ee3af","src/backend/mod.rs":"4f07743a46a4b908c9e23b31d0bc8f3a0983b943e0bb824b9154272b2673ae90","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"e3f94e118e1dd47941fbba4417de40bddc4254d9f06b1e938f58d8f1aa566a5c","src/backend/tests/api.rs":"cd7e7551e2e82b19da883621a494d2a6779c373f3ff2d12ee52fae8efec1e7b8","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"f68c2eaa55c3ec2a58894832fbca1e2a2e79e740b145f76a0f45452af465a934","src/backend/tests/device_property.rs":"ea0be5f8834be494cb33f854ce9d334b5763dc5287f949bcb4bd025d8a8b2d3b","src/backend/tests/interfaces.rs":"2f192d08a5b2cbf678e0fea38b7061b1f0e83aa6b8f443bd92b70fee67c35516","src/backend/tests/manual.rs":"0c1bbd9b6137bbc484974c4bf7ea965872b8f52f86055f07916b85a3d715e0a6","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"59632744e70616ab7037facb0787db339b96800c8cc397d203241548c5cfb7f5","src/backend/tests/tone.rs":"779cc14fc2a362bf7f26ce66ad70c0639501176175655a99b7fefb3c59d56c7a","src/backend/tests/utils.rs":"efb8b3709aff7ed5e2923566084de3e0709f3bd9c18a04f3310d7a3b86fa4b71","src/backend/utils.rs":"6c3ffbcd602e6cc9f56deb9ecb07b2eef2e6f074ef924178e466f380aae5c595","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"efc1f012eb9a331a040cad4ac03aa79307f25885f71b6fb38f3ad7af8d7d515c"},"package":null}
{"files":{".circleci/config.yml":"7f3dc865105ca8f33965a7958b1fe2e627ae2d5a703f3b2a4ab6e2e796018597",".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".github/workflows/test.yml":"aa1998a3b104ad131805ca3513832cef3f65300192824f8b1efc9a5a0cc108f6",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"d7e757e664c23fae52028f1dfc5917f92523c08702e3a1f95e1fd38ed714416c","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"0007782a05a5330f739ad789c19c82562c82e32386b0447000fc72c0d48405bc","build-audiounit-rust-in-cubeb.sh":"d228a05985dcd02ec1ecac66a2b64dae5a530804a25a7054ccc95905aedfb7ef","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"d717e598c96e4911d9494b18382d6bd3a8d5038b7d68d3166ad4336e237a97d8","run_sanitizers.sh":"84e93a0da137803018f37403511e8c92760be730426bf6cea34419d93d1a7ff8","run_tests.sh":"916a7ae4a406d2274417d6eca939a878db5adcb6144e5680d9d148bf90178f1c","src/backend/aggregate_device.rs":"43511107ba2a75a19340ac663c981362ca1b75b679b6c295d88b5035bd7e3619","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"e9bcf964347daa8952f98caa2746e34a31ea8908375204896593f56e4b6147ca","src/backend/device_property.rs":"a7622feaa41db1cd76fd35a85a022e44f4894e396a104a59008d5b8757d2ab4e","src/backend/mixer.rs":"ed299d3954e2a823060c870a8244673a7d4bca530830cb66b964d047a80ee3af","src/backend/mod.rs":"1591669c30a3d07754bfb39c9cb042cdd101f0ab89be13f6cdf74d376e441cf8","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"e3f94e118e1dd47941fbba4417de40bddc4254d9f06b1e938f58d8f1aa566a5c","src/backend/tests/api.rs":"cd7e7551e2e82b19da883621a494d2a6779c373f3ff2d12ee52fae8efec1e7b8","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"f68c2eaa55c3ec2a58894832fbca1e2a2e79e740b145f76a0f45452af465a934","src/backend/tests/device_property.rs":"ea0be5f8834be494cb33f854ce9d334b5763dc5287f949bcb4bd025d8a8b2d3b","src/backend/tests/interfaces.rs":"af8e3fdeb58226621699b29f1a90621b2260e3f17292dac54860cd05fe4eec71","src/backend/tests/manual.rs":"4a1634e86beb145d2703722a8be057a762953241329c82ee09acf7dc0f0d9d0c","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"59632744e70616ab7037facb0787db339b96800c8cc397d203241548c5cfb7f5","src/backend/tests/tone.rs":"779cc14fc2a362bf7f26ce66ad70c0639501176175655a99b7fefb3c59d56c7a","src/backend/tests/utils.rs":"efb8b3709aff7ed5e2923566084de3e0709f3bd9c18a04f3310d7a3b86fa4b71","src/backend/utils.rs":"6c3ffbcd602e6cc9f56deb9ecb07b2eef2e6f074ef924178e466f380aae5c595","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"efc1f012eb9a331a040cad4ac03aa79307f25885f71b6fb38f3ad7af8d7d515c"},"package":null}

Просмотреть файл

@ -32,12 +32,11 @@ use self::device_property::*;
use self::mixer::*;
use self::resampler::*;
use self::utils::*;
use atomic;
use backend::ringbuf::RingBuffer;
use cubeb_backend::{
ffi, Context, ContextOps, DeviceCollectionRef, DeviceId, DeviceRef, DeviceType, Error,
InputProcessingParams, Ops, Result, SampleFormat, State, Stream, StreamOps, StreamParams,
StreamParamsRef, StreamPrefs,
ffi, ChannelLayout, Context, ContextOps, DeviceCollectionRef, DeviceId, DeviceRef, DeviceType,
Error, InputProcessingParams, Ops, Result, SampleFormat, State, Stream, StreamOps,
StreamParams, StreamParamsRef, StreamPrefs,
};
use mach::mach_time::{mach_absolute_time, mach_timebase_info};
use std::cmp;
@ -59,6 +58,8 @@ const DISPATCH_QUEUE_LABEL: &str = "org.mozilla.cubeb";
const PRIVATE_AGGREGATE_DEVICE_NAME: &str = "CubebAggregateDevice";
const VOICEPROCESSING_AGGREGATE_DEVICE_NAME: &str = "VPAUAggregateAudioDevice";
const APPLE_STUDIO_DISPLAY_USB_ID: &str = "05AC:1114";
// Testing empirically, some headsets report a minimal latency that is very low,
// but this does not work in practice. Lie and say the minimum is 128 frames.
const SAFE_MIN_LATENCY_FRAMES: u32 = 128;
@ -286,7 +287,26 @@ fn get_volume(unit: AudioUnit) -> Result<f32> {
fn set_input_mute(unit: AudioUnit, mute: bool) -> Result<()> {
assert!(!unit.is_null());
let mute: UInt32 = mute.into();
let mute: u32 = mute.into();
let mut old_mute: u32 = 0;
let r = audio_unit_get_property(
unit,
kAUVoiceIOProperty_MuteOutput,
kAudioUnitScope_Global,
AU_IN_BUS,
&mut old_mute,
&mut mem::size_of::<u32>(),
);
if r != NO_ERR {
cubeb_log!(
"AudioUnitGetProperty/kAUVoiceIOProperty_MuteOutput rv={}",
r
);
return Err(Error::error());
}
if old_mute == mute {
return Ok(());
}
let r = audio_unit_set_property(
unit,
kAUVoiceIOProperty_MuteOutput,
@ -310,52 +330,85 @@ fn set_input_processing_params(unit: AudioUnit, params: InputProcessingParams) -
assert!(!unit.is_null());
let aec = params.contains(InputProcessingParams::ECHO_CANCELLATION);
let ns = params.contains(InputProcessingParams::NOISE_SUPPRESSION);
// We don't use AGC, but keep it here for reference.
// See the comment in supported_input_processing_params.
let agc = params.contains(InputProcessingParams::AUTOMATIC_GAIN_CONTROL);
assert!(!agc);
// AEC and NS are active as soon as VPIO is not bypassed.
// Therefore the only modes we can explicitly support are {} and {aec, ns}.
// AEC and NS are active as soon as VPIO is not bypassed, therefore the only combinations
// of those we can explicitly support are {} and {aec, ns}.
if aec != ns {
// No control to turn on AEC without NS or vice versa.
return Err(Error::error());
}
let agc = u32::from(agc);
let r = audio_unit_set_property(
let mut old_agc: u32 = 0;
let r = audio_unit_get_property(
unit,
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
kAudioUnitScope_Global,
AU_IN_BUS,
&agc,
mem::size_of::<u32>(),
&mut old_agc,
&mut mem::size_of::<u32>(),
);
if r != NO_ERR {
cubeb_log!(
"AudioUnitSetProperty/kAUVoiceIOProperty_VoiceProcessingEnableAGC rv={}",
"AudioUnitGetProperty/kAUVoiceIOProperty_VoiceProcessingEnableAGC rv={}",
r
);
return Err(Error::error());
}
if (old_agc == 1) != agc {
let agc = u32::from(agc);
let r = audio_unit_set_property(
unit,
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
kAudioUnitScope_Global,
AU_IN_BUS,
&agc,
mem::size_of::<u32>(),
);
if r != NO_ERR {
cubeb_log!(
"AudioUnitSetProperty/kAUVoiceIOProperty_VoiceProcessingEnableAGC rv={}",
r
);
return Err(Error::error());
}
}
let mut old_bypass: u32 = 0;
let r = audio_unit_get_property(
unit,
kAUVoiceIOProperty_BypassVoiceProcessing,
kAudioUnitScope_Global,
AU_IN_BUS,
&mut old_bypass,
&mut mem::size_of::<u32>(),
);
if r != NO_ERR {
cubeb_log!(
"AudioUnitGetProperty/kAUVoiceIOProperty_BypassVoiceProcessing rv={}",
r
);
return Err(Error::error());
}
let bypass = u32::from(!aec);
let r = audio_unit_set_property(
unit,
kAUVoiceIOProperty_BypassVoiceProcessing,
kAudioUnitScope_Global,
AU_IN_BUS,
&bypass,
mem::size_of::<u32>(),
);
if r != NO_ERR {
cubeb_log!(
"AudioUnitSetProperty/kAUVoiceIOProperty_BypassVoiceProcessing rv={}",
r
if old_bypass != bypass {
let r = audio_unit_set_property(
unit,
kAUVoiceIOProperty_BypassVoiceProcessing,
kAudioUnitScope_Global,
AU_IN_BUS,
&bypass,
mem::size_of::<u32>(),
);
return Err(Error::error());
if r != NO_ERR {
cubeb_log!(
"AudioUnitSetProperty/kAUVoiceIOProperty_BypassVoiceProcessing rv={}",
r
);
return Err(Error::error());
}
}
Ok(())
@ -1418,6 +1471,8 @@ fn get_channel_count(
assert_ne!(devid, kAudioObjectUnknown);
let mut streams = get_device_streams(devid, devtype)?;
let model_uid =
get_device_model_uid(devid, devtype).map_or_else(|_| String::new(), |s| s.into_string());
if devtype == DeviceType::INPUT {
// With VPIO, output devices will/may get a Tap that appears as input channels on the
@ -1462,6 +1517,13 @@ fn get_channel_count(
);
false
}
// The input tap stream on the Studio Display Speakers has a terminal type that
// is not clearly output-specific. We special-case it here.
EXTERNAL_DIGITAL_AUDIO_INTERFACE
if model_uid.contains(APPLE_STUDIO_DISPLAY_USB_ID) =>
{
false
}
// Note INPUT_UNDEFINED is 0x200 and INPUT_MICROPHONE is 0x201
t if (INPUT_MICROPHONE..OUTPUT_UNDEFINED).contains(&t) => true,
t if (OUTPUT_UNDEFINED..BIDIRECTIONAL_UNDEFINED).contains(&t) => false,
@ -2237,11 +2299,9 @@ impl ContextOps for AudioUnitContext {
Ok(rate as u32)
}
fn supported_input_processing_params(&mut self) -> Result<InputProcessingParams> {
// The VoiceProcessingIO AudioUnit has the
// kAUVoiceIOProperty_VoiceProcessingEnableAGC property to enable AGC on
// the input signal, but some simple manual tests on MacOS 14.0 suggest
// it doesn't have any effect.
Ok(InputProcessingParams::ECHO_CANCELLATION | InputProcessingParams::NOISE_SUPPRESSION)
Ok(InputProcessingParams::ECHO_CANCELLATION
| InputProcessingParams::NOISE_SUPPRESSION
| InputProcessingParams::AUTOMATIC_GAIN_CONTROL)
}
fn enumerate_devices(
&mut self,
@ -2505,6 +2565,8 @@ struct CoreStreamData<'ctx> {
// Info of the I/O devices.
input_device: device_info,
output_device: device_info,
input_processing_params: InputProcessingParams,
input_mute: bool,
input_buffer_manager: Option<BufferManager>,
// Listeners indicating what system events are monitored.
default_input_listener: Option<device_property_listener>,
@ -2543,6 +2605,8 @@ impl<'ctx> Default for CoreStreamData<'ctx> {
output_unit: ptr::null_mut(),
input_device: device_info::default(),
output_device: device_info::default(),
input_processing_params: InputProcessingParams::NONE,
input_mute: false,
input_buffer_manager: None,
default_input_listener: None,
default_output_listener: None,
@ -2587,6 +2651,8 @@ impl<'ctx> CoreStreamData<'ctx> {
output_unit: ptr::null_mut(),
input_device: in_dev,
output_device: out_dev,
input_processing_params: InputProcessingParams::NONE,
input_mute: false,
input_buffer_manager: None,
default_input_listener: None,
default_output_listener: None,
@ -2678,6 +2744,50 @@ impl<'ctx> CoreStreamData<'ctx> {
input_domain == output_domain
}
fn should_block_vpio_for_device_pair(
&self,
in_device: &device_info,
out_device: &device_info,
) -> bool {
self.debug_assert_is_on_stream_queue();
cubeb_log!("Evaluating device pair against VPIO block list");
let log_device = |id, devtype| -> std::result::Result<(), OSStatus> {
cubeb_log!("{} uid=\"{}\", model_uid=\"{}\", transport_type={:?}, source={:?}, source_name=\"{}\", name=\"{}\", manufacturer=\"{}\"",
if devtype == DeviceType::INPUT {
"Input"
} else {
debug_assert_eq!(devtype, DeviceType::OUTPUT);
"Output"
},
get_device_uid(id, devtype).map(|s| s.into_string()).unwrap_or_default(),
get_device_model_uid(id, devtype).map(|s| s.into_string()).unwrap_or_default(),
convert_uint32_into_string(get_device_transport_type(id, devtype).unwrap_or(0)),
convert_uint32_into_string(get_device_source(id, devtype).unwrap_or(0)),
get_device_source_name(id, devtype).map(|s| s.into_string()).unwrap_or_default(),
get_device_name(id, devtype).map(|s| s.into_string()).unwrap_or_default(),
get_device_manufacturer(id, devtype).map(|s| s.into_string()).unwrap_or_default());
Ok(())
};
log_device(in_device.id, DeviceType::INPUT);
log_device(out_device.id, DeviceType::OUTPUT);
match (
get_device_model_uid(in_device.id, DeviceType::INPUT).map(|s| s.to_string()),
get_device_model_uid(out_device.id, DeviceType::OUTPUT).map(|s| s.to_string()),
) {
(Ok(in_model_uid), Ok(out_model_uid))
if in_model_uid.contains(APPLE_STUDIO_DISPLAY_USB_ID)
&& out_model_uid.contains(APPLE_STUDIO_DISPLAY_USB_ID) =>
{
cubeb_log!("Both input and output device is an Apple Studio Display. BLOCKED");
true
}
_ => {
cubeb_log!("Device pair is not blocked");
false
}
}
}
fn create_audiounits(&mut self) -> Result<(device_info, device_info)> {
self.debug_assert_is_on_stream_queue();
let should_use_voice_processing_unit = self.has_input()
@ -2685,7 +2795,8 @@ impl<'ctx> CoreStreamData<'ctx> {
&& self
.input_stream_params
.prefs()
.contains(StreamPrefs::VOICE);
.contains(StreamPrefs::VOICE)
&& !self.should_block_vpio_for_device_pair(&self.input_device, &self.output_device);
let should_use_aggregate_device = {
// It's impossible to create an aggregate device from an aggregate device, and it's
@ -2715,7 +2826,7 @@ impl<'ctx> CoreStreamData<'ctx> {
}
cubeb_log!(
"Output device ID: {} (aggregate: {:?})",
self.input_device.id,
self.output_device.id,
output_is_aggregate
);
}
@ -2935,8 +3046,7 @@ impl<'ctx> CoreStreamData<'ctx> {
let params = unsafe {
let mut p = *self.input_stream_params.as_ptr();
p.channels = if using_voice_processing_unit {
// With VPIO, stereo input devices configured for stereo have been observed to
// spit out only a single mono channel.
// VPIO is always MONO.
1
} else {
input_hw_desc.mChannelsPerFrame
@ -3054,9 +3164,6 @@ impl<'ctx> CoreStreamData<'ctx> {
out_dev_info
);
let device_channel_count =
get_channel_count(self.output_device.id, DeviceType::OUTPUT).unwrap_or(0);
cubeb_log!(
"({:p}) Opening output side: rate {}, channels {}, format {:?}, layout {:?}, prefs {:?}, latency in frames {}, voice processing {}.",
self.stm_ptr,
@ -3093,9 +3200,10 @@ impl<'ctx> CoreStreamData<'ctx> {
output_hw_desc
);
// In some cases with VPIO the stream format's mChannelsPerFrame is higher than
// expected. Use get_channel_count as source of truth.
output_hw_desc.mChannelsPerFrame = device_channel_count;
// In some cases with (other streams using) VPIO the stream format's mChannelsPerFrame
// is higher than expected. Use get_channel_count as source of truth.
output_hw_desc.mChannelsPerFrame =
get_channel_count(self.output_device.id, DeviceType::OUTPUT).unwrap_or(0);
// This has been observed in the wild.
if output_hw_desc.mChannelsPerFrame == 0 {
@ -3112,7 +3220,12 @@ impl<'ctx> CoreStreamData<'ctx> {
// channels will be appended at the end of the raw data given by the output callback.
let params = unsafe {
let mut p = *self.output_stream_params.as_ptr();
p.channels = output_hw_desc.mChannelsPerFrame;
p.channels = if using_voice_processing_unit {
// VPIO is always MONO.
1
} else {
output_hw_desc.mChannelsPerFrame
};
if using_voice_processing_unit {
// VPIO will always use the sample rate of the input hw for both input and output,
// as reported to us. (We can override it but we cannot improve quality this way).
@ -3360,12 +3473,26 @@ impl<'ctx> CoreStreamData<'ctx> {
);
}
// Always initiate to not use input processing.
// Always try to remember the applied input mute state. If it cannot be applied
// to the new device pair, we notify the client of an error and it will have to
// open a new stream.
if let Err(r) = set_input_mute(self.input_unit, self.input_mute) {
cubeb_log!(
"({:p}) Failed to set mute state of voiceprocessing. Error: {}",
self.stm_ptr,
r
);
return Err(r);
}
// Always try to remember the applied input processing params. If they cannot
// be applied in the new device pair, we notify the client of an error and it
// will have to open a new stream.
if let Err(r) =
set_input_processing_params(self.input_unit, InputProcessingParams::NONE)
set_input_processing_params(self.input_unit, self.input_processing_params)
{
cubeb_log!(
"({:p}) Failed to enable bypass of voiceprocessing. Error: {}",
"({:p}) Failed to set params of voiceprocessing. Error: {}",
self.stm_ptr,
r
);
@ -3719,23 +3846,10 @@ impl<'ctx> CoreStreamData<'ctx> {
fn get_output_channel_layout(&self) -> Result<Vec<mixer::Channel>> {
self.debug_assert_is_on_stream_queue();
assert!(!self.output_unit.is_null());
if !self.using_voice_processing_unit() {
return get_channel_layout(self.output_unit);
if self.using_voice_processing_unit() {
return Ok(get_channel_order(ChannelLayout::MONO));
}
// The VoiceProcessingIO unit (as tried on MacOS 14) is known to not support
// kAudioUnitProperty_AudioChannelLayout queries, and to lie about
// kAudioDevicePropertyPreferredChannelLayout. If we're using
// VoiceProcessingIO, try standing up a regular AudioUnit and query that.
cubeb_log!(
"({:p}) get_output_channel_layout with a VoiceProcessingIO output unit. Trying a dedicated unit.",
self.stm_ptr
);
let mut dedicated_unit = create_audiounit(&self.output_device)?;
let res = get_channel_layout(dedicated_unit);
dispose_audio_unit(dedicated_unit);
dedicated_unit = ptr::null_mut();
res
get_channel_layout(self.output_unit)
}
}
@ -4217,6 +4331,7 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
self as *const AudioUnitStream,
mute
);
self.core_stream_data.input_mute = mute;
Ok(())
}
fn set_input_processing_params(&mut self, params: InputProcessingParams) -> Result<()> {
@ -4257,6 +4372,7 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
self as *const AudioUnitStream,
params
);
self.core_stream_data.input_processing_params = params;
Ok(())
}
#[cfg(target_os = "ios")]

Просмотреть файл

@ -90,6 +90,7 @@ fn test_ops_context_supported_input_processing_params() {
params,
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL
);
},
);
@ -1017,6 +1018,54 @@ fn test_ops_duplex_voice_stream_set_input_mute() {
});
}
#[test]
fn test_ops_duplex_voice_stream_set_input_mute_before_start() {
test_default_duplex_voice_stream_operation(
"duplex voice stream: mute before start",
|stream| {
assert_eq!(
unsafe { OPS.stream_set_input_mute.unwrap()(stream, 1) },
ffi::CUBEB_OK
);
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
},
);
}
#[test]
fn test_ops_duplex_voice_stream_set_input_mute_before_start_with_reinit() {
test_default_duplex_voice_stream_operation(
"duplex voice stream: mute before start with reinit",
|stream| {
assert_eq!(
unsafe { OPS.stream_set_input_mute.unwrap()(stream, 1) },
ffi::CUBEB_OK
);
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
// Hacky cast, but testing this here was simplest for now.
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
stm.reinit_async();
let queue = stm.queue.clone();
let mut mute_after_reinit = false;
queue.run_sync(|| {
let mut mute: u32 = 0;
let r = audio_unit_get_property(
stm.core_stream_data.input_unit,
kAUVoiceIOProperty_MuteOutput,
kAudioUnitScope_Global,
AU_IN_BUS,
&mut mute,
&mut mem::size_of::<u32>(),
);
assert_eq!(r, NO_ERR);
mute_after_reinit = mute == 1;
});
assert_eq!(mute_after_reinit, true);
},
);
}
#[test]
fn test_ops_duplex_voice_stream_set_input_mute_after_start() {
test_default_duplex_voice_stream_operation("duplex voice stream: mute after start", |stream| {
@ -1033,7 +1082,8 @@ fn test_ops_duplex_voice_stream_set_input_processing_params() {
test_default_duplex_voice_stream_operation("duplex voice stream: processing", |stream| {
let params: ffi::cubeb_input_processing_params =
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION;
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
assert_eq!(
unsafe { OPS.stream_set_input_processing_params.unwrap()(stream, params) },
ffi::CUBEB_OK
@ -1048,7 +1098,8 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_before_start() {
|stream| {
let params: ffi::cubeb_input_processing_params =
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION;
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
assert_eq!(
unsafe { OPS.stream_set_input_processing_params.unwrap()(stream, params) },
ffi::CUBEB_OK
@ -1058,6 +1109,65 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_before_start() {
);
}
#[test]
fn test_ops_duplex_voice_stream_set_input_processing_params_before_start_with_reinit() {
test_default_duplex_voice_stream_operation(
"duplex voice stream: processing before start with reinit",
|stream| {
let params: ffi::cubeb_input_processing_params =
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
assert_eq!(
unsafe { OPS.stream_set_input_processing_params.unwrap()(stream, params) },
ffi::CUBEB_OK
);
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
// Hacky cast, but testing this here was simplest for now.
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
stm.reinit_async();
let queue = stm.queue.clone();
let mut params_after_reinit: ffi::cubeb_input_processing_params =
ffi::CUBEB_INPUT_PROCESSING_PARAM_NONE;
queue.run_sync(|| {
let mut params: ffi::cubeb_input_processing_params =
ffi::CUBEB_INPUT_PROCESSING_PARAM_NONE;
let mut agc: u32 = 0;
let r = audio_unit_get_property(
stm.core_stream_data.input_unit,
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
kAudioUnitScope_Global,
AU_IN_BUS,
&mut agc,
&mut mem::size_of::<u32>(),
);
assert_eq!(r, NO_ERR);
if agc == 1 {
params = params | ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
}
let mut bypass: u32 = 0;
let r = audio_unit_get_property(
stm.core_stream_data.input_unit,
kAUVoiceIOProperty_BypassVoiceProcessing,
kAudioUnitScope_Global,
AU_IN_BUS,
&mut bypass,
&mut mem::size_of::<u32>(),
);
assert_eq!(r, NO_ERR);
if bypass == 0 {
params = params
| ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION;
}
params_after_reinit = params;
});
assert_eq!(params, params_after_reinit);
},
);
}
#[test]
fn test_ops_duplex_voice_stream_set_input_processing_params_after_start() {
test_default_duplex_voice_stream_operation(
@ -1066,7 +1176,8 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_after_start() {
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
let params: ffi::cubeb_input_processing_params =
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION;
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION
| ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
assert_eq!(
unsafe { OPS.stream_set_input_processing_params.unwrap()(stream, params) },
ffi::CUBEB_OK

Просмотреть файл

@ -338,23 +338,6 @@ fn test_stream_tester() {
params.set(InputProcessingParams::ECHO_CANCELLATION, true);
params.set(InputProcessingParams::NOISE_SUPPRESSION, true);
}
let mut agc = u32::from(false);
let mut size: usize = mem::size_of::<u32>();
assert_eq!(
audio_unit_get_property(
stm.core_stream_data.input_unit,
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
kAudioUnitScope_Global,
AU_IN_BUS,
&mut agc,
&mut size,
),
NO_ERR
);
assert_eq!(size, mem::size_of::<u32>());
if agc == 1 {
params.set(InputProcessingParams::AUTOMATIC_GAIN_CONTROL, true);
}
}
let mut done = false;
while !done {

Просмотреть файл

@ -22,7 +22,7 @@ static_prefs = { path = "../../../../modules/libpref/init/static_prefs" }
profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true }
mozurl = { path = "../../../../netwerk/base/mozurl" }
webrender_bindings = { path = "../../../../gfx/webrender_bindings" }
cubeb-coreaudio = { git = "https://github.com/mozilla/cubeb-coreaudio-rs", rev = "4ba39ca14bbb5bb0274843701cd958717a0fe293", optional = true }
cubeb-coreaudio = { git = "https://github.com/mozilla/cubeb-coreaudio-rs", rev = "d23ab55eab684b46f46e1da177c8814f6103a009", optional = true }
cubeb-pulse = { git = "https://github.com/mozilla/cubeb-pulse-rs", rev="8ff972c8e2ec1782ff262ac4071c0415e69b1367", optional = true, features=["pulse-dlopen"] }
cubeb-sys = { version = "0.12.0", optional = true, features=["gecko-in-tree"] }
audioipc2-client = { git = "https://github.com/mozilla/audioipc", rev = "596bdb7fbb5745ea415726e16bd497e6c850a540", optional = true }