Bug 1890186: Update cubeb-coreaudio-rs to 1796ace5bd. r=cubeb-reviewers,padenot

Differential Revision: https://phabricator.services.mozilla.com/D212311
This commit is contained in:
Andreas Pehrson 2024-06-03 10:03:39 +00:00
parent e18f2fd79f
commit a303b96371
12 changed files with 354 additions and 233 deletions

View File

@ -70,9 +70,9 @@ git = "https://github.com/mozilla/audioipc"
rev = "3495905752a4263827f5d43737f9ca3ed0243ce0"
replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=4cc24c7dc74a4801455ba0bbe20ea67a49f28514"]
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=1796ace5bdd08ec8baa56bbf7170a08d760c984b"]
git = "https://github.com/mozilla/cubeb-coreaudio-rs"
rev = "4cc24c7dc74a4801455ba0bbe20ea67a49f28514"
rev = "1796ace5bdd08ec8baa56bbf7170a08d760c984b"
replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=8678dcab1c287de79c4c184ccc2e065bc62b70e2"]

4
Cargo.lock generated
View File

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

View File

@ -1 +1 @@
{"files":{".circleci/config.yml":"7f3dc865105ca8f33965a7958b1fe2e627ae2d5a703f3b2a4ab6e2e796018597",".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".github/workflows/test.yml":"cf6ebe6d41b022897360866b526d19ba8843aa82ae99a1d28393985576b6a782",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"2698cf87581d8d551ed3ac5875564720ed23d7b788e8d145d4281c8026203cd2","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":"90c2542fa3ff8a35fed894fae3a1aa0157117b7f0e28df14b8e6f7b1f1f43797","run_sanitizers.sh":"84e93a0da137803018f37403511e8c92760be730426bf6cea34419d93d1a7ff8","run_tests.sh":"bae82f66dd47a060b6fdcc238520084aec1079d5b1b1d66d103baa1ffaa8773d","src/backend/aggregate_device.rs":"6e94c36c09081a728b1ab748b460fe8f538cf5f50bc62fd47171a393fe2d609a","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"e9bcf964347daa8952f98caa2746e34a31ea8908375204896593f56e4b6147ca","src/backend/device_property.rs":"0714b90c3187b0b1709f5e4b7757e1b434659276e00db48a3f3270fbfd429640","src/backend/mixer.rs":"c4d09291598cbffb2217b551770ec590f34b6dd6b461dd99b019d5bb70f0eef3","src/backend/mod.rs":"48380d5a273fc3ff8b597e5974fc00361c66b512b1d95b6c15a6cea332a11a57","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"48e291b355a7c0c643fc58e9d238ed00234b4f1ac0f4c26737cc74862d4f2ac8","src/backend/tests/api.rs":"ef3babcd3410394b8d5bcdaf0ea526486b14d8e42f33211997aafe179430bf4a","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"babf50326fb38db24fe80f24f546e1b6ad04319ae8835bb372d893fc9b3038a2","src/backend/tests/device_property.rs":"73c25f579a995e8a59c9b7d391813afb75f739b5e2f825480cba04499a1d46e8","src/backend/tests/interfaces.rs":"cd58614435574444d8a1f039dc201cf371cccacd58efbae8ed8fbff919550d0a","src/backend/tests/manual.rs":"f72625c05110534775c4608ccc45472ea108286657ffc1f029844a13d0b883bf","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"a7ebd579339c40ca64c0757cc9da6baec641e670f226e1b2ec5049894700bd7a","src/backend/tests/tone.rs":"b028c67777b6453a26190b6a49785dfe28556adcbe179cb10862ce0d47ee8509","src/backend/tests/utils.rs":"21c8e7f6f18da0f8d33733ad0fc981041b43586db6a637c3f7aec7e7b3936aed","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":"cf6ebe6d41b022897360866b526d19ba8843aa82ae99a1d28393985576b6a782",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"2698cf87581d8d551ed3ac5875564720ed23d7b788e8d145d4281c8026203cd2","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":"1403232694fabeae004179be8399d1fe2a1b100d60cd90db37d8860eddbaf2ae","run_sanitizers.sh":"84e93a0da137803018f37403511e8c92760be730426bf6cea34419d93d1a7ff8","run_tests.sh":"bae82f66dd47a060b6fdcc238520084aec1079d5b1b1d66d103baa1ffaa8773d","src/backend/aggregate_device.rs":"a910b9d596b1971cb4fee34f5030809ade584f41eb5cbad73a09abe7352ebd15","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"e9bcf964347daa8952f98caa2746e34a31ea8908375204896593f56e4b6147ca","src/backend/device_property.rs":"4719226a5fb3b5697d4624ccf1b15f0d522ddbc3f64a98fba47d9c55c5aeee36","src/backend/mixer.rs":"c4d09291598cbffb2217b551770ec590f34b6dd6b461dd99b019d5bb70f0eef3","src/backend/mod.rs":"c13d293223402bdb53451e6587a3de8a22be36d6e944baba88bda3977d2cebd9","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"afbdf1da1fcaddcad2986bd3146bf93ca75c24b3362f5f23a09517a926290ca2","src/backend/tests/api.rs":"3b0936810b3afa84cb80428c471e1097701fd790460d00c0a5715fd8026d0a4d","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"babf50326fb38db24fe80f24f546e1b6ad04319ae8835bb372d893fc9b3038a2","src/backend/tests/device_property.rs":"4ef3ab625809fe95e944c19cc5dc1cc79f473520a4314d123b1f80c6b7e11411","src/backend/tests/interfaces.rs":"cd58614435574444d8a1f039dc201cf371cccacd58efbae8ed8fbff919550d0a","src/backend/tests/manual.rs":"f72625c05110534775c4608ccc45472ea108286657ffc1f029844a13d0b883bf","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"a7ebd579339c40ca64c0757cc9da6baec641e670f226e1b2ec5049894700bd7a","src/backend/tests/tone.rs":"b028c67777b6453a26190b6a49785dfe28556adcbe179cb10862ce0d47ee8509","src/backend/tests/utils.rs":"3e435569798b883db8342137098832b88837a387008852005363f74e5e6ff18e","src/backend/utils.rs":"6c3ffbcd602e6cc9f56deb9ecb07b2eef2e6f074ef924178e466f380aae5c595","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"efc1f012eb9a331a040cad4ac03aa79307f25885f71b6fb38f3ad7af8d7d515c"},"package":null}

View File

@ -8,10 +8,15 @@ if [[ -z "${RUST_BACKTRACE}" ]]; then
fi
echo "RUST_BACKTRACE is set to ${RUST_BACKTRACE}\n"
cargo test test_aggregate -- --ignored --nocapture
cargo test test_switch_device -- --ignored --nocapture
cargo test test_plug_and_unplug_device -- --ignored --nocapture
cargo test test_get_channel_count_of_input_devices_with_vpio -- --ignored --nocapture
cargo test test_get_channel_count_of_input_devices_with_aggregate_device_and_vpio -- --ignored --nocapture
cargo test test_register_device_changed_callback_to_check_default_device_changed_input -- --ignored --nocapture
cargo test test_register_device_changed_callback_to_check_default_device_changed_output -- --ignored --nocapture
cargo test test_register_device_changed_callback_to_check_default_device_changed_duplex -- --ignored --nocapture

View File

@ -185,9 +185,7 @@ impl AggregateDevice {
let (lock, cvar) = &*condvar_pair;
let guard = lock.lock().unwrap();
let (_guard, timeout_res) = cvar
.wait_timeout_while(guard, waiting_time, |()| {
!audiounit_get_devices().contains(&device)
})
.wait_timeout_while(guard, waiting_time, |()| !get_devices().contains(&device))
.unwrap();
if timeout_res.timed_out() {
cubeb_log!(
@ -396,8 +394,8 @@ impl AggregateDevice {
assert_ne!(input_id, output_id);
debug_assert_running_serially();
let output_sub_devices = Self::get_sub_devices(output_id)?;
let input_sub_devices = Self::get_sub_devices(input_id)?;
let output_sub_devices = Self::get_sub_devices_or_self(output_id)?;
let input_sub_devices = Self::get_sub_devices_or_self(input_id)?;
unsafe {
let sub_devices = CFArrayCreateMutable(ptr::null(), 0, &kCFTypeArrayCallBacks);
@ -445,16 +443,10 @@ impl AggregateDevice {
let mut size: usize = 0;
let status = audio_object_get_property_data_size(device_id, &address, &mut size);
if status == kAudioHardwareUnknownPropertyError as OSStatus {
// Return a vector containing the device itself if the device has no sub devices.
sub_devices.push(device_id);
return Ok(sub_devices);
} else if status != NO_ERR {
if status != NO_ERR {
return Err(Error::from(status));
}
assert_ne!(size, 0);
let count = size / mem::size_of::<AudioObjectID>();
sub_devices = allocate_array(count);
let status = audio_object_get_property_data(
@ -471,6 +463,17 @@ impl AggregateDevice {
}
}
pub fn get_sub_devices_or_self(
device_id: AudioDeviceID,
) -> std::result::Result<Vec<AudioObjectID>, Error> {
AggregateDevice::get_sub_devices(device_id).or_else(|e| match e {
Error::OS(status) if status == kAudioHardwareUnknownPropertyError as OSStatus => {
Ok(vec![device_id])
}
_ => Err(e),
})
}
pub fn get_master_device_uid(device_id: AudioDeviceID) -> std::result::Result<String, Error> {
debug_assert_running_serially();
let address = AudioObjectPropertyAddress {
@ -515,7 +518,7 @@ impl AggregateDevice {
};
// The master device will be the 1st sub device of the primary device.
let output_sub_devices = Self::get_sub_devices(primary_id)?;
let output_sub_devices = Self::get_sub_devices_or_self(primary_id)?;
assert!(!output_sub_devices.is_empty());
let master_sub_device_uid = get_device_global_uid(output_sub_devices[0]).unwrap();
let master_sub_device = master_sub_device_uid.get_raw();

View File

@ -18,6 +18,32 @@ pub fn get_device_uid(
}
}
pub fn get_devices() -> Vec<AudioObjectID> {
debug_assert_running_serially();
let mut size: usize = 0;
let address = get_property_address(
Property::HardwareDevices,
DeviceType::INPUT | DeviceType::OUTPUT,
);
let mut ret =
audio_object_get_property_data_size(kAudioObjectSystemObject, &address, &mut size);
if ret != NO_ERR {
return Vec::new();
}
// Total number of input and output devices.
let mut devices: Vec<AudioObjectID> = allocate_array_by_size(size);
ret = audio_object_get_property_data(
kAudioObjectSystemObject,
&address,
&mut size,
devices.as_mut_ptr(),
);
if ret != NO_ERR {
return Vec::new();
}
devices
}
pub fn get_device_model_uid(
id: AudioDeviceID,
devtype: DeviceType,
@ -169,10 +195,15 @@ pub fn get_device_latency(
}
}
#[derive(Debug)]
pub struct DeviceStream {
pub device: AudioDeviceID,
pub stream: AudioStreamID,
}
pub fn get_device_streams(
id: AudioDeviceID,
devtype: DeviceType,
) -> std::result::Result<Vec<AudioStreamID>, OSStatus> {
) -> std::result::Result<Vec<DeviceStream>, OSStatus> {
assert_ne!(id, kAudioObjectUnknown);
debug_assert_running_serially();
@ -184,13 +215,77 @@ pub fn get_device_streams(
return Err(err);
}
let mut streams: Vec<AudioObjectID> = allocate_array_by_size(size);
let mut streams = vec![AudioObjectID::default(); size / mem::size_of::<AudioObjectID>()];
let err = audio_object_get_property_data(id, &address, &mut size, streams.as_mut_ptr());
if err == NO_ERR {
Ok(streams)
} else {
Err(err)
if err != NO_ERR {
return Err(err);
}
let mut device_streams = streams
.into_iter()
.map(|stream| DeviceStream { device: id, stream })
.collect::<Vec<_>>();
if devtype.contains(DeviceType::INPUT) {
// With VPIO, output devices will/may get a Tap that appears as an input stream on the
// output device id. It is unclear what kind of Tap this is as it cannot be enumerated
// as a Tap through the public APIs. There is no property on the stream itself that
// can consistently identify it as originating from another device's output either.
// TerminalType gets close but is often kAudioStreamTerminalTypeUnknown, and there are
// cases reported where real input streams have that TerminalType, too.
// See Firefox bug 1890186.
// We rely on AudioObjectID order instead. AudioDeviceID and AudioStreamID (and more)
// are all AudioObjectIDs underneath, and they're all distinct. The Tap streams
// mentioned above are created when VPIO is created, and their AudioObjectIDs are higher
// than the VPIO device's AudioObjectID, but lower than the next *real* device's
// AudioObjectID.
// Simplified, a device's native streams have AudioObjectIDs higher than their device's
// AudioObjectID but lower than the next device's AudioObjectID.
// We use this to filter streams, and hope that it holds across macOS versions.
// Note that for aggregate devices this does not hold, as their stream IDs seem to be
// repurposed by VPIO. We sum up the result of the above algorithm for each of their sub
// devices instead, as that seems to hold.
let mut devices = get_devices();
let sub_devices = AggregateDevice::get_sub_devices(id);
if let Ok(sub_device_ids) = sub_devices {
cubeb_log!(
"Getting input device streams for aggregate device {}. Summing over sub devices {:?}.",
id,
sub_device_ids
);
return Ok(sub_device_ids
.into_iter()
.filter_map(|sub_id| get_device_streams(sub_id, devtype).ok())
.flatten()
.collect());
}
debug_assert!(devices.contains(&id));
devices.sort();
let next_id = devices.into_iter().skip_while(|&i| i != id).nth(1);
cubeb_log!(
"Filtering input streams {:?} for device {}. Next device is {:?}.",
device_streams
.iter()
.map(|ds| ds.stream)
.collect::<Vec<_>>(),
id,
next_id
);
if let Some(next_id) = next_id {
device_streams.retain(|ds| ds.stream > id && ds.stream < next_id);
} else {
device_streams.retain(|ds| ds.stream > id);
}
cubeb_log!(
"Input stream filtering for device {} retained {:?}.",
id,
device_streams
.iter()
.map(|ds| ds.stream)
.collect::<Vec<_>>()
);
}
Ok(device_streams)
}
pub fn get_device_sample_rate(
@ -253,24 +348,6 @@ pub fn get_stream_latency(id: AudioStreamID) -> std::result::Result<u32, OSStatu
}
}
pub fn get_stream_terminal_type(id: AudioStreamID) -> std::result::Result<u32, OSStatus> {
assert_ne!(id, kAudioObjectUnknown);
debug_assert_running_serially();
let address = get_property_address(
Property::StreamTerminalType,
DeviceType::INPUT | DeviceType::OUTPUT,
);
let mut size = mem::size_of::<u32>();
let mut terminal_type: u32 = 0;
let err = audio_object_get_property_data(id, &address, &mut size, &mut terminal_type);
if err == NO_ERR {
Ok(terminal_type)
} else {
Err(err)
}
}
pub fn get_stream_virtual_format(
id: AudioStreamID,
) -> std::result::Result<AudioStreamBasicDescription, OSStatus> {

View File

@ -1620,77 +1620,17 @@ fn get_channel_count(
assert_ne!(devid, kAudioObjectUnknown);
debug_assert_running_serially();
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
// output device id. One could check for whether the output device has a tap enabled,
// but it is impossible to distinguish an output-only device from an input+output
// device. There have also been corner cases observed, where the device does NOT have
// a Tap enabled, but it still has the extra input channels from the Tap.
// We can check the terminal type of the input stream instead, the VPIO type is
// INPUT_UNDEFINED or an output type, we explicitly ignore those and keep all other cases.
streams.retain(|stream| {
let terminal_type = get_stream_terminal_type(*stream);
if terminal_type.is_err() {
return true;
}
#[allow(non_upper_case_globals)]
match terminal_type.unwrap() {
kAudioStreamTerminalTypeMicrophone
| kAudioStreamTerminalTypeHeadsetMicrophone
| kAudioStreamTerminalTypeReceiverMicrophone => true,
kAudioStreamTerminalTypeUnknown => {
cubeb_log!("Unknown TerminalType for input stream. Ignoring its channels.");
false
}
t if [
kAudioStreamTerminalTypeSpeaker,
kAudioStreamTerminalTypeHeadphones,
kAudioStreamTerminalTypeLFESpeaker,
kAudioStreamTerminalTypeReceiverSpeaker,
]
.contains(&t) =>
{
cubeb_log!(
"Output TerminalType {:#06X} for input stream. Ignoring its channels.",
t
);
false
}
INPUT_UNDEFINED => {
cubeb_log!(
"INPUT_UNDEFINED TerminalType for input stream. Ignoring its channels."
);
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,
t if (BIDIRECTIONAL_UNDEFINED..TELEPHONY_UNDEFINED).contains(&t) => true,
t if (TELEPHONY_UNDEFINED..EXTERNAL_UNDEFINED).contains(&t) => true,
t => {
cubeb_log!("Unknown TerminalType {:#06X} for input stream.", t);
true
}
}
});
}
let mut count = 0;
for stream in streams {
if let Ok(format) = get_stream_virtual_format(stream) {
count += format.mChannelsPerFrame;
let devstreams = get_device_streams(devid, devtype)?;
let mut count: u32 = 0;
for ds in devstreams {
if devtype == DeviceType::INPUT
&& CoreStreamData::should_force_vpio_for_input_device(ds.device)
{
count += 1;
} else {
count += get_stream_virtual_format(ds.stream)
.map(|f| f.mChannelsPerFrame)
.unwrap_or(0);
}
}
Ok(count)
@ -1736,8 +1676,8 @@ fn get_fixed_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
}
};
let stream_latency = get_device_streams(devid, devtype).and_then(|streams| {
if streams.is_empty() {
let stream_latency = get_device_streams(devid, devtype).and_then(|devstreams| {
if devstreams.is_empty() {
cubeb_log!(
"No stream on device {} in {:?} scope!",
devid,
@ -1745,7 +1685,7 @@ fn get_fixed_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
);
Ok(0) // default stream latency
} else {
get_stream_latency(streams[0])
get_stream_latency(devstreams[0].stream)
}
}).map_err(|e| {
cubeb_log!(
@ -2031,37 +1971,11 @@ fn destroy_cubeb_device_info(device: &mut ffi::cubeb_device_info) {
}
}
fn audiounit_get_devices() -> Vec<AudioObjectID> {
debug_assert_running_serially();
let mut size: usize = 0;
let address = get_property_address(
Property::HardwareDevices,
DeviceType::INPUT | DeviceType::OUTPUT,
);
let mut ret =
audio_object_get_property_data_size(kAudioObjectSystemObject, &address, &mut size);
if ret != NO_ERR {
return Vec::new();
}
// Total number of input and output devices.
let mut devices: Vec<AudioObjectID> = allocate_array_by_size(size);
ret = audio_object_get_property_data(
kAudioObjectSystemObject,
&address,
&mut size,
devices.as_mut_ptr(),
);
if ret != NO_ERR {
return Vec::new();
}
devices
}
fn audiounit_get_devices_of_type(devtype: DeviceType) -> Vec<AudioObjectID> {
assert!(devtype.intersects(DeviceType::INPUT | DeviceType::OUTPUT));
debug_assert_running_serially();
let mut devices = audiounit_get_devices();
let mut devices = get_devices();
// Remove the aggregate device from the list of devices (if any).
devices.retain(|&device| {
@ -3304,14 +3218,15 @@ impl<'ctx> CoreStreamData<'ctx> {
}
#[allow(non_upper_case_globals)]
fn should_force_vpio_for_input_device(&self, in_device: &device_info) -> bool {
assert!(in_device.id != kAudioObjectUnknown);
self.debug_assert_is_on_stream_queue();
match get_device_transport_type(in_device.id, DeviceType::INPUT) {
fn should_force_vpio_for_input_device(id: AudioDeviceID) -> bool {
assert!(id != kAudioObjectUnknown);
debug_assert_running_serially();
match get_device_transport_type(id, DeviceType::INPUT) {
Ok(kAudioDeviceTransportTypeBuiltIn) => {
cubeb_log!(
"Forcing VPIO because input device is built in, and its volume \
is known to be very low without VPIO whenever VPIO is hooked up to it elsewhere."
"Input device {} is on the VPIO force list because it is built in, \
and its volume is known to be very low without VPIO whenever VPIO \
is hooked up to it elsewhere."
);
true
}
@ -3388,7 +3303,7 @@ impl<'ctx> CoreStreamData<'ctx> {
.input_stream_params
.prefs()
.contains(StreamPrefs::VOICE)
|| self.should_force_vpio_for_input_device(&self.input_device))
|| CoreStreamData::should_force_vpio_for_input_device(self.input_device.id))
&& !self.should_block_vpio_for_device_pair(&self.input_device, &self.output_device)
&& macos_kernel_major_version() != Ok(MACOS_KERNEL_MAJOR_VERSION_MONTEREY);

View File

@ -1,8 +1,11 @@
extern crate itertools;
use super::utils::{
test_get_all_devices, test_get_all_onwed_devices, test_get_default_device,
test_get_drift_compensations, test_get_master_device, DeviceFilter, Scope,
};
use super::*;
use std::collections::HashSet;
use std::iter::zip;
use std::panic;
@ -46,25 +49,73 @@ fn test_aggregate_set_sub_devices_for_unknown_devices() {
// AggregateDevice::get_sub_devices
// ------------------------------------
// You can check this by creating an aggregate device in `Audio MIDI Setup`
// application and print out the sub devices of them!
#[test]
#[ignore]
fn test_aggregate_get_sub_devices() {
let devices = test_get_all_devices(DeviceFilter::ExcludeCubebAggregateAndVPIO);
for device in devices {
// `AggregateDevice::get_sub_devices(device)` will return a single-element vector
// containing `device` itself if it's not an aggregate device. This test assumes devices
// is not an empty aggregate device (Test will panic when calling get_sub_devices with
// an empty aggregate device).
println!(
"get_sub_devices({}={})",
device,
run_serially_forward_panics(|| get_device_uid(device))
fn diff(lhs: Vec<u32>, rhs: Vec<u32>) -> Vec<u32> {
let left: HashSet<u32> = lhs.into_iter().collect();
let right: HashSet<u32> = rhs.into_iter().collect();
left.symmetric_difference(&right)
.map(|&i| i.clone())
.collect()
}
// Run in a large block so other test cases cannot add or remove devices while this runs.
let input_device = test_get_default_device(Scope::Input);
let output_device = test_get_default_device(Scope::Output);
if input_device.is_none() || output_device.is_none() || input_device == output_device {
println!("No input and output device to create an aggregate device.");
return;
}
let devices_base = test_get_all_devices(DeviceFilter::ExcludeVPIO);
run_serially_forward_panics(|| {
assert!(
devices_base
.clone()
.into_iter()
.map(AggregateDevice::get_sub_devices)
.any(|r| r.is_err()),
"There should be some device that is not an aggregate."
)
});
{
// Test get_sub_devices on an empty aggregate device.
let plugin_id = AggregateDevice::get_system_plugin_id().unwrap();
let aggr = run_serially_forward_panics(|| AggregateDevice::create_blank_device(plugin_id))
.unwrap();
let new = diff(
devices_base.clone(),
test_get_all_devices(DeviceFilter::ExcludeVPIO),
);
let sub_devices =
run_serially_forward_panics(|| AggregateDevice::get_sub_devices(device).unwrap());
// TODO: If the device is a blank aggregate device, then the assertion fails!
assert!(!sub_devices.is_empty());
assert_eq!(new.len(), 1);
let new_subs = run_serially_forward_panics(|| AggregateDevice::get_sub_devices(new[0]));
assert!(new_subs.is_ok());
assert_eq!(new_subs.unwrap().len(), 0);
assert!(
run_serially_forward_panics(|| AggregateDevice::destroy_device(plugin_id, aggr))
.is_ok()
);
}
{
// Test get_sub_devices on an aggregate device with two sub devices.
let aggr = run_serially_forward_panics(|| {
let input_device = input_device.unwrap();
let output_device = output_device.unwrap();
AggregateDevice::new(input_device, output_device)
})
.unwrap();
let new = diff(
devices_base.clone(),
test_get_all_devices(DeviceFilter::ExcludeVPIO),
);
assert_eq!(new.len(), 1);
let new_subs = run_serially_forward_panics(|| AggregateDevice::get_sub_devices(new[0]));
assert!(new_subs.is_ok());
assert_eq!(new_subs.unwrap().len(), 2);
run_serially_forward_panics(|| drop(aggr));
}
}
@ -72,8 +123,7 @@ fn test_aggregate_get_sub_devices() {
#[should_panic]
fn test_aggregate_get_sub_devices_for_a_unknown_device() {
run_serially_forward_panics(|| {
let devices = AggregateDevice::get_sub_devices(kAudioObjectUnknown).unwrap();
assert!(devices.is_empty());
AggregateDevice::get_sub_devices(kAudioObjectUnknown);
});
}
@ -135,15 +185,11 @@ fn test_aggregate_create_blank_device() {
// AggregateDevice::get_sub_devices
// ------------------------------------
#[test]
#[should_panic]
fn test_aggregate_get_sub_devices_for_blank_aggregate_devices() {
run_serially_forward_panics(|| {
// TODO: Test this when there is no available devices.
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
// There is no sub device in a blank aggregate device!
// AggregateDevice::get_sub_devices guarantees returning a non-empty devices vector, so
// the following call will panic!
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
assert!(sub_devices.is_empty());
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
@ -173,11 +219,11 @@ fn test_aggregate_set_sub_devices() {
))
.is_ok());
let sub_devices = run_serially(|| AggregateDevice::get_sub_devices(device)).unwrap();
let sub_devices = run_serially(|| AggregateDevice::get_sub_devices_or_self(device)).unwrap();
let input_sub_devices =
run_serially(|| AggregateDevice::get_sub_devices(input_device)).unwrap();
run_serially(|| AggregateDevice::get_sub_devices_or_self(input_device)).unwrap();
let output_sub_devices =
run_serially(|| AggregateDevice::get_sub_devices(output_device)).unwrap();
run_serially(|| AggregateDevice::get_sub_devices_or_self(output_device)).unwrap();
// TODO: There may be overlapping devices between input_sub_devices and output_sub_devices,
// but now AggregateDevice::set_sub_devices will add them directly.
@ -280,7 +326,7 @@ fn test_aggregate_set_master_device() {
assert!(run_serially(|| AggregateDevice::set_master_device(device, output_device)).is_ok());
let output_sub_devices =
run_serially(|| AggregateDevice::get_sub_devices(output_device)).unwrap();
run_serially(|| AggregateDevice::get_sub_devices_or_self(output_device)).unwrap();
let first_output_sub_device_uid = run_serially(|| get_device_uid(output_sub_devices[0]));
// Check that the first sub device of the output device is set as master device.
@ -391,10 +437,12 @@ fn test_aggregate_activate_clock_drift_compensation_for_an_aggregate_device_with
// The master device is by default the first sub device in the list.
// This happens to be the first sub device of the input device, see implementation of
// AggregateDevice::set_sub_devices.
let first_input_sub_device_uid =
run_serially(|| get_device_uid(AggregateDevice::get_sub_devices(input_device).unwrap()[0]));
let first_sub_device_uid =
run_serially(|| get_device_uid(AggregateDevice::get_sub_devices(device).unwrap()[0]));
let first_input_sub_device_uid = run_serially(|| {
get_device_uid(AggregateDevice::get_sub_devices_or_self(input_device).unwrap()[0])
});
let first_sub_device_uid = run_serially(|| {
get_device_uid(AggregateDevice::get_sub_devices_or_self(device).unwrap()[0])
});
assert_eq!(first_input_sub_device_uid, first_sub_device_uid);
let master_device_uid = run_serially(|| test_get_master_device(device));
assert_eq!(first_sub_device_uid, master_device_uid);
@ -422,7 +470,7 @@ fn test_aggregate_activate_clock_drift_compensation_for_a_blank_aggregate_device
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
let sub_devices = AggregateDevice::get_sub_devices_or_self(device).unwrap();
assert!(sub_devices.is_empty());
let onwed_devices = test_get_all_onwed_devices(device);
assert!(onwed_devices.is_empty());
@ -475,7 +523,7 @@ fn test_aggregate_new() {
let aggr = AggregateDevice::new(input_device, output_device).unwrap();
// Check main device
let output_sub_devices = AggregateDevice::get_sub_devices(output_device).unwrap();
let output_sub_devices = AggregateDevice::get_sub_devices_or_self(output_device).unwrap();
let first_output_sub_device_uid = get_device_uid(output_sub_devices[0]);
let master_device_uid = test_get_master_device(aggr.get_device_id());
assert_eq!(first_output_sub_device_uid, master_device_uid);

View File

@ -1134,6 +1134,98 @@ fn test_get_channel_count_of_unknwon_type() {
}
}
fn is_vpio(id: AudioObjectID) -> bool {
debug_assert_running_serially();
get_device_global_uid(id)
.map(|uid| uid.into_string())
.map(|uid| uid.contains(VOICEPROCESSING_AGGREGATE_DEVICE_NAME))
.unwrap_or(false)
}
fn get_nonvpio_input_channel_counts() -> Vec<u32> {
debug_assert_running_serially();
get_devices()
.into_iter()
.filter(|&id| !is_vpio(id))
.filter_map(|id| get_channel_count(id, DeviceType::INPUT).ok())
.collect()
}
#[test]
#[ignore]
fn test_get_channel_count_of_input_devices_with_vpio() {
let non_vpio_channel_counts =
run_serially_forward_panics(|| get_nonvpio_input_channel_counts());
let queue = Queue::new_with_target(
"test_get_channel_count_of_input_devices_with_vpio",
get_serial_queue_singleton(),
);
let mut shared = SharedVoiceProcessingUnitManager::new(queue.clone());
let _vpio = queue.run_sync(|| shared.take_or_create()).unwrap().unwrap();
let vpio_channel_counts = run_serially_forward_panics(|| get_nonvpio_input_channel_counts());
assert_eq!(non_vpio_channel_counts, vpio_channel_counts);
}
#[test]
#[ignore]
fn test_get_channel_count_of_input_devices_with_aggregate_device_and_vpio() {
let input_device = test_get_default_device(Scope::Input);
let output_device = test_get_default_device(Scope::Output);
if input_device.is_none() || output_device.is_none() || input_device == output_device {
println!("No input or output device to create an aggregate device.");
return;
}
#[derive(Default)]
struct State {
aggr: Option<AggregateDevice>,
vpio_mgr: Option<SharedVoiceProcessingUnitManager>,
vpio: Option<OwningHandle<VoiceProcessingUnit>>,
}
impl Drop for State {
fn drop(&mut self) {
let mut aggr = self.aggr.take();
let mut vpio = self.vpio.take();
run_serially_forward_panics(move || {
aggr.take();
vpio.take();
});
}
}
let state = Arc::new(Mutex::new(State::default()));
// Set up an AggregateDevice with input and output.
let initial_channel_counts = run_serially_forward_panics(|| get_nonvpio_input_channel_counts());
let s1 = state.clone();
let aggr_channel_counts = run_serially_forward_panics(|| {
let mut state = s1.lock().unwrap();
state.aggr =
Some(AggregateDevice::new(input_device.unwrap(), output_device.unwrap()).unwrap());
get_nonvpio_input_channel_counts()
});
assert_eq!(initial_channel_counts.len() + 1, aggr_channel_counts.len());
let queue = Queue::new_with_target(
"test_get_channel_count_of_input_devices_with_aggregate_device_and_vpio",
get_serial_queue_singleton(),
);
{
let mut s = state.lock().unwrap();
s.vpio_mgr = Some(SharedVoiceProcessingUnitManager::new(queue.clone()));
}
let s2 = state.clone();
let aggr_vpio_channel_counts = run_serially_forward_panics(|| {
let mut state = s2.lock().unwrap();
state.vpio = state.vpio_mgr.as_mut().map(|m| m.take_or_create().unwrap());
get_nonvpio_input_channel_counts()
});
assert_eq!(aggr_channel_counts, aggr_vpio_channel_counts);
}
// get_range_of_sample_rates
// ------------------------------------
#[test]

View File

@ -413,20 +413,20 @@ fn test_get_ranges_of_device_sample_rate_by_unknown_device() {
#[test]
fn test_get_stream_latency() {
if let Some(device) = test_get_default_device(Scope::Input) {
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
for stream in streams {
let latency = run_serially(|| get_stream_latency(stream)).unwrap();
println!("latency of the input stream {} is {}", stream, latency);
let devstreams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
for ds in devstreams {
let latency = run_serially(|| get_stream_latency(ds.stream)).unwrap();
println!("latency of the input stream {} is {}", ds.stream, latency);
}
} else {
println!("No input device.");
}
if let Some(device) = test_get_default_device(Scope::Output) {
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
for stream in streams {
let latency = run_serially(|| get_stream_latency(stream)).unwrap();
println!("latency of the output stream {} is {}", stream, latency);
let devstreams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
for ds in devstreams {
let latency = run_serially(|| get_stream_latency(ds.stream)).unwrap();
println!("latency of the output stream {} is {}", ds.stream, latency);
}
} else {
println!("No output device.");
@ -444,10 +444,10 @@ fn test_get_stream_latency_by_unknown_device() {
#[test]
fn test_get_stream_virtual_format() {
if let Some(device) = test_get_default_device(Scope::Input) {
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
let formats = streams
let devstreams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
let formats = devstreams
.iter()
.map(|s| run_serially(|| get_stream_virtual_format(*s)))
.map(|ds| run_serially(|| get_stream_virtual_format(ds.stream)))
.collect::<Vec<std::result::Result<AudioStreamBasicDescription, OSStatus>>>();
println!("input stream formats: {:?}", formats);
assert!(!formats.is_empty());
@ -456,10 +456,10 @@ fn test_get_stream_virtual_format() {
}
if let Some(device) = test_get_default_device(Scope::Output) {
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
let formats = streams
let devstreams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
let formats = devstreams
.iter()
.map(|s| run_serially(|| get_stream_virtual_format(*s)))
.map(|ds| run_serially(|| get_stream_virtual_format(ds.stream)))
.collect::<Vec<std::result::Result<AudioStreamBasicDescription, OSStatus>>>();
println!("output stream formats: {:?}", formats);
assert!(!formats.is_empty());
@ -476,52 +476,22 @@ fn test_get_stream_virtual_format_by_unknown_stream() {
);
}
// get_stream_terminal_type
// get_devices
// ------------------------------------
#[test]
fn test_get_stream_terminal_type() {
fn terminal_type_to_device_type(terminal_type: u32) -> Option<DeviceType> {
#[allow(non_upper_case_globals)]
match terminal_type {
kAudioStreamTerminalTypeMicrophone
| kAudioStreamTerminalTypeHeadsetMicrophone
| kAudioStreamTerminalTypeReceiverMicrophone => Some(DeviceType::INPUT),
kAudioStreamTerminalTypeSpeaker
| kAudioStreamTerminalTypeHeadphones
| kAudioStreamTerminalTypeLFESpeaker
| kAudioStreamTerminalTypeReceiverSpeaker => Some(DeviceType::OUTPUT),
t if t > INPUT_UNDEFINED && t < OUTPUT_UNDEFINED => Some(DeviceType::INPUT),
t if t > OUTPUT_UNDEFINED && t < BIDIRECTIONAL_UNDEFINED => Some(DeviceType::OUTPUT),
t => {
println!("UNKNOWN TerminalType {:#06x}", t);
None
}
}
}
fn test_get_devices() {
if let Some(device) = test_get_default_device(Scope::Input) {
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
assert!(streams.iter().any(|&s| {
terminal_type_to_device_type(run_serially(|| get_stream_terminal_type(s)).unwrap())
== Some(DeviceType::INPUT)
}));
let devices = run_serially(|| get_devices());
assert!(devices.contains(&device));
} else {
println!("No input device.");
}
if let Some(device) = test_get_default_device(Scope::Output) {
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
assert!(streams.iter().any(|&s| {
terminal_type_to_device_type(run_serially(|| get_stream_terminal_type(s)).unwrap())
== Some(DeviceType::OUTPUT)
}));
let devices = run_serially(|| get_devices());
assert!(devices.contains(&device));
} else {
println!("No output device.");
}
}
#[test]
#[should_panic]
fn test_get_stream_terminal_type_by_unknown_stream() {
assert!(get_stream_terminal_type(kAudioObjectUnknown).is_err());
}

View File

@ -293,6 +293,7 @@ fn test_enable_audiounit_in_scope(
pub enum DeviceFilter {
ExcludeCubebAggregateAndVPIO,
ExcludeVPIO,
IncludeAll,
}
pub fn test_get_all_devices(filter: DeviceFilter) -> Vec<AudioObjectID> {
@ -354,6 +355,16 @@ pub fn test_get_all_devices(filter: DeviceFilter) -> Vec<AudioObjectID> {
}
});
}
DeviceFilter::ExcludeVPIO => {
devices.retain(|&device| {
if let Ok(uid) = get_device_global_uid(device) {
let uid = uid.into_string();
!uid.contains(VOICEPROCESSING_AGGREGATE_DEVICE_NAME)
} else {
true
}
});
}
_ => {}
}

View File

@ -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 = "4cc24c7dc74a4801455ba0bbe20ea67a49f28514", optional = true }
cubeb-coreaudio = { git = "https://github.com/mozilla/cubeb-coreaudio-rs", rev = "1796ace5bdd08ec8baa56bbf7170a08d760c984b", optional = true }
cubeb-pulse = { git = "https://github.com/mozilla/cubeb-pulse-rs", rev="8678dcab1c287de79c4c184ccc2e065bc62b70e2", optional = true, features=["pulse-dlopen"] }
cubeb-sys = { version = "0.13", optional = true, features=["gecko-in-tree"] }
audioipc2-client = { git = "https://github.com/mozilla/audioipc", rev = "3495905752a4263827f5d43737f9ca3ed0243ce0", optional = true }