зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1670633 - Update cubeb-coreaudio-rs to 964e1462. r=cubeb-reviewers,padenot
Differential Revision: https://phabricator.services.mozilla.com/D193900
This commit is contained in:
Родитель
6bb8230ea7
Коммит
fd20da1468
|
@ -65,9 +65,9 @@ git = "https://github.com/mozilla/audioipc"
|
|||
rev = "6be424d75f1367e70f2f5ddcacd6d0237e81a6a9"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=93b5c01a131f65c83c11aeb317f4583405c5eb79"]
|
||||
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=964e14628f285ad44522bdeeb566c1864ecd2fd8"]
|
||||
git = "https://github.com/mozilla/cubeb-coreaudio-rs"
|
||||
rev = "93b5c01a131f65c83c11aeb317f4583405c5eb79"
|
||||
rev = "964e14628f285ad44522bdeeb566c1864ecd2fd8"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=cf48897be5cbe147d051ebbbe1eaf5fd8fb6bbc9"]
|
||||
|
|
|
@ -895,7 +895,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "coreaudio-sys-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=93b5c01a131f65c83c11aeb317f4583405c5eb79#93b5c01a131f65c83c11aeb317f4583405c5eb79"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=964e14628f285ad44522bdeeb566c1864ecd2fd8#964e14628f285ad44522bdeeb566c1864ecd2fd8"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
|
@ -1106,11 +1106,11 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cubeb-coreaudio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=93b5c01a131f65c83c11aeb317f4583405c5eb79#93b5c01a131f65c83c11aeb317f4583405c5eb79"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=964e14628f285ad44522bdeeb566c1864ecd2fd8#964e14628f285ad44522bdeeb566c1864ecd2fd8"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"audio-mixer",
|
||||
"bitflags 1.999.999",
|
||||
"bitflags 2.4.0",
|
||||
"coreaudio-sys-utils",
|
||||
"cubeb-backend",
|
||||
"float-cmp",
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"d15a17e76eddf5088ad0b09544704479b3269ef1bc9060118bda7e87ed499eab","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"5447179330a862659a25bceedfdc5d29a1296f63490908d1c868c6b21c5f95a1","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"82ca429be8f930db730c7c571d6f2246e59e82ecb220b5290a3cf4a53e997053","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
||||
{"files":{"Cargo.toml":"87292d055a2fc0f070f54abd549a5f79ec8ac33611ecde80ba394f256b88294c","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"5447179330a862659a25bceedfdc5d29a1296f63490908d1c868c6b21c5f95a1","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"f6267fe587217c3d3ad5fe7f3a35955221c936103bf853c477a2e44eba5f1e46","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
|
@ -20,9 +20,10 @@ license = "ISC"
|
|||
version = "0.8"
|
||||
|
||||
[dependencies.coreaudio-sys]
|
||||
version = "0.2"
|
||||
version = "0.2.14"
|
||||
features = [
|
||||
"audio_unit",
|
||||
"core_audio",
|
||||
"io_kit_audio",
|
||||
]
|
||||
default-features = false
|
||||
|
|
|
@ -22,6 +22,16 @@ impl Queue {
|
|||
queue
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_assert_is_current(&self) {
|
||||
unsafe {
|
||||
dispatch_assert_queue(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn debug_assert_is_current(&self) {}
|
||||
|
||||
pub fn run_async<F>(&self, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".circleci/config.yml":"7f3dc865105ca8f33965a7958b1fe2e627ae2d5a703f3b2a4ab6e2e796018597",".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".github/workflows/test.yml":"aa1998a3b104ad131805ca3513832cef3f65300192824f8b1efc9a5a0cc108f6",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"b82f0d73e2afde65c328c9263c7e4ff3f53fe1671e5fc9a96ced50fb12698933","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":"cac9a7d588c2be0efa842c15d24c8cf0573bc9f9d73011662ba764dc9f6245e8","run_sanitizers.sh":"8828c73439dfd540cd21fc713c4a1011ec05baca6f282f285743f310ace39563","run_tests.sh":"916a7ae4a406d2274417d6eca939a878db5adcb6144e5680d9d148bf90178f1c","src/backend/aggregate_device.rs":"e822b4496e2ea5c6e1a288c6e92f57e598abf67cb0bdff813f859d57c63be5f9","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"314c3404f1a44f8c23856f96510779e7e64ebb7dde5abe444946c16d04af7c29","src/backend/device_property.rs":"2a63837b98b9b6bfb314b4aa6dce919412b71e13e18c7b8f3477aa1482f70b29","src/backend/mixer.rs":"ed299d3954e2a823060c870a8244673a7d4bca530830cb66b964d047a80ee3af","src/backend/mod.rs":"c1d4c753d30fc5e6f2aafcd5d34c3ae2393e0bae4aeb5bc477af396014ec1877","src/backend/resampler.rs":"8641c672d00f77be287d64f6cd4a2b2cd36f8630e2ded1eff6b39834b1b7ddc3","src/backend/tests/aggregate_device.rs":"7542cbb1c82398d00a5cba5b335842c517af2cda887946ebb5aaf512fd06b527","src/backend/tests/api.rs":"2cbae3a49f39fc78ec379c8c0c0266ad3e3552ef22b42168ac9d62d9d983a4a7","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"97555244a8081251f1447c6a677609d1e91f33c9c0f4fb3f8a4082005c68ae0c","src/backend/tests/device_property.rs":"373f76d3bee83b263db3f02be3b94b408bdf852d84e4b5153273fda34b11a374","src/backend/tests/interfaces.rs":"9ed857625147c3d13c1458e630db8a78362612e6ef679d918bfdafa6e927fe75","src/backend/tests/manual.rs":"8d485a6666a3f4518b03e39dab80bf2acfd760af2d2f43bad99023cb135b38ca","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"779cc14fc2a362bf7f26ce66ad70c0639501176175655a99b7fefb3c59d56c7a","src/backend/tests/utils.rs":"3c430e519c0e7a17dda6b57c110fab725a846da39606fd99274bad20a637cf35","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":"4d868f1f8698945fb84b9700926f5d6800d003d289d5ae773bcfaaaeb436b45e","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":"d6c3cde105ae0759e753cc31cab691eb417c4d0ad68f20da5e87fe0138a0d92f","run_tests.sh":"916a7ae4a406d2274417d6eca939a878db5adcb6144e5680d9d148bf90178f1c","src/backend/aggregate_device.rs":"43511107ba2a75a19340ac663c981362ca1b75b679b6c295d88b5035bd7e3619","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"314c3404f1a44f8c23856f96510779e7e64ebb7dde5abe444946c16d04af7c29","src/backend/device_property.rs":"a7622feaa41db1cd76fd35a85a022e44f4894e396a104a59008d5b8757d2ab4e","src/backend/mixer.rs":"ed299d3954e2a823060c870a8244673a7d4bca530830cb66b964d047a80ee3af","src/backend/mod.rs":"21ca892ccd925991e7f4877a5ac1b8e70a5f4785e7d498024965c46eabdd3033","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"e3f94e118e1dd47941fbba4417de40bddc4254d9f06b1e938f58d8f1aa566a5c","src/backend/tests/api.rs":"566546bec17220641960342374516da3baf6032a8843ac7a6c1f0b1cb8911594","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"b1b4f7f71de99d07406a8a38dc67e46a43b883d4a845daaf356e72fbe0d5a08b","src/backend/tests/device_property.rs":"ea0be5f8834be494cb33f854ce9d334b5763dc5287f949bcb4bd025d8a8b2d3b","src/backend/tests/interfaces.rs":"c8ca26d0fd098b8a230d9aa8d67a062da2ab3ac0f868739485c793618fc75700","src/backend/tests/manual.rs":"8d485a6666a3f4518b03e39dab80bf2acfd760af2d2f43bad99023cb135b38ca","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"504613b1b5fa4d67cbb2560cb8d8cef0a4e8929c28b31d9d4695ac5286969f38","src/backend/tests/tone.rs":"779cc14fc2a362bf7f26ce66ad70c0639501176175655a99b7fefb3c59d56c7a","src/backend/tests/utils.rs":"7d74298435260566838ece9a84ee42e4a02c16683f5434d7ba2671a9acf4424f","src/backend/utils.rs":"6c3ffbcd602e6cc9f56deb9ecb07b2eef2e6f074ef924178e466f380aae5c595","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"efc1f012eb9a331a040cad4ac03aa79307f25885f71b6fb38f3ad7af8d7d515c"},"package":null}
|
|
@ -28,7 +28,7 @@ crate-type = [
|
|||
[dependencies]
|
||||
atomic = "0.4"
|
||||
audio-mixer = "0.1"
|
||||
bitflags = "1.0"
|
||||
bitflags = "2"
|
||||
cubeb-backend = "0.10.3"
|
||||
float-cmp = "0.6"
|
||||
lazy_static = "1.2"
|
||||
|
@ -39,3 +39,6 @@ triple_buffer = "5.0.5"
|
|||
|
||||
[dependencies.coreaudio-sys-utils]
|
||||
path = "coreaudio-sys-utils"
|
||||
|
||||
[dev-dependencies]
|
||||
itertools = "0.11"
|
||||
|
|
|
@ -24,21 +24,17 @@ cargo test test_suspend_input_stream_by_unplugging_a_nondefault_input_device --
|
|||
cargo test test_destroy_input_stream_after_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
cargo test test_reinit_input_stream_by_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
|
||||
# FIXME: We don't monitor the alive-status for output device currently
|
||||
# cargo test test_destroy_output_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
# FIXME: We don't monitor the alive-status for output device currently
|
||||
# cargo test test_suspend_output_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_output_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_suspend_output_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
|
||||
cargo test test_destroy_output_stream_after_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_output_stream_by_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_suspend_duplex_stream_by_unplugging_a_nondefault_input_device
|
||||
cargo test test_suspend_duplex_stream_by_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
|
||||
# FIXME: We don't monitor the alive-status for output device currently
|
||||
# cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
# FIXME: We don't monitor the alive-status for output device currently
|
||||
# cargo test test_suspend_duplex_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_suspend_duplex_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
|
|
|
@ -15,8 +15,7 @@ fi
|
|||
# - `memory`: It doesn't works with target x86_64-apple-darwin
|
||||
# - `leak`: Get some errors that are out of our control. See:
|
||||
# https://github.com/mozilla/cubeb-coreaudio-rs/issues/45#issuecomment-591642931
|
||||
# - `thread`: It's blocked by #129
|
||||
sanitizers=("address")
|
||||
sanitizers=("address" "thread")
|
||||
for san in "${sanitizers[@]}"
|
||||
do
|
||||
San="$(tr '[:lower:]' '[:upper:]' <<< ${san:0:1})${san:1}"
|
||||
|
|
|
@ -180,7 +180,7 @@ impl AggregateDevice {
|
|||
let device = Self::create_blank_device(plugin_id)?;
|
||||
|
||||
// Wait until the aggregate is created.
|
||||
let &(ref lock, ref cvar) = &*condvar_pair;
|
||||
let (lock, cvar) = &*condvar_pair;
|
||||
let devices = lock.lock().unwrap();
|
||||
if !devices.contains(&device) {
|
||||
let (devs, timeout_res) = cvar.wait_timeout(devices, waiting_time).unwrap();
|
||||
|
@ -203,7 +203,7 @@ impl AggregateDevice {
|
|||
) -> OSStatus {
|
||||
assert_eq!(id, kAudioObjectSystemObject);
|
||||
let pair = unsafe { &mut *(data as *mut Arc<(Mutex<Vec<AudioObjectID>>, Condvar)>) };
|
||||
let &(ref lock, ref cvar) = &**pair;
|
||||
let (lock, cvar) = &**pair;
|
||||
let mut devices = lock.lock().unwrap();
|
||||
*devices = audiounit_get_devices();
|
||||
cvar.notify_one();
|
||||
|
@ -339,7 +339,7 @@ impl AggregateDevice {
|
|||
Self::set_sub_devices(device_id, input_id, output_id)?;
|
||||
|
||||
// Wait until the sub devices are added.
|
||||
let &(ref lock, ref cvar) = &*condvar_pair;
|
||||
let (lock, cvar) = &*condvar_pair;
|
||||
let device = lock.lock().unwrap();
|
||||
if *device != device_id {
|
||||
let (dev, timeout_res) = cvar.wait_timeout(device, waiting_time).unwrap();
|
||||
|
@ -370,7 +370,7 @@ impl AggregateDevice {
|
|||
data: *mut c_void,
|
||||
) -> OSStatus {
|
||||
let pair = unsafe { &mut *(data as *mut Arc<(Mutex<AudioObjectID>, Condvar)>) };
|
||||
let &(ref lock, ref cvar) = &**pair;
|
||||
let (lock, cvar) = &**pair;
|
||||
let mut device = lock.lock().unwrap();
|
||||
*device = id;
|
||||
cvar.notify_one();
|
||||
|
|
|
@ -223,58 +223,13 @@ pub fn get_ranges_of_device_sample_rate(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_device_stream_format(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<AudioStreamBasicDescription, OSStatus> {
|
||||
pub fn get_stream_latency(id: AudioStreamID) -> std::result::Result<u32, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceStreamFormat, devtype);
|
||||
let mut size = mem::size_of::<AudioStreamBasicDescription>();
|
||||
let mut format = AudioStreamBasicDescription::default();
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut format);
|
||||
if err == NO_ERR {
|
||||
Ok(format)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)] // Allow casting *mut u8 to *mut AudioBufferList
|
||||
pub fn get_device_stream_configuration(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<Vec<AudioBuffer>, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceStreamConfiguration, devtype);
|
||||
let mut size: usize = 0;
|
||||
let err = audio_object_get_property_data_size(id, &address, &mut size);
|
||||
if err != NO_ERR {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let mut data: Vec<u8> = allocate_array_by_size(size);
|
||||
let ptr = data.as_mut_ptr() as *mut AudioBufferList;
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, ptr);
|
||||
if err != NO_ERR {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let list = unsafe { &(*ptr) };
|
||||
let ptr = list.mBuffers.as_ptr() as *const AudioBuffer;
|
||||
let len = list.mNumberBuffers as usize;
|
||||
let buffers = unsafe { slice::from_raw_parts(ptr, len) };
|
||||
Ok(buffers.to_vec())
|
||||
}
|
||||
|
||||
pub fn get_stream_latency(
|
||||
id: AudioStreamID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<u32, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::StreamLatency, devtype);
|
||||
let address = get_property_address(
|
||||
Property::StreamLatency,
|
||||
DeviceType::INPUT | DeviceType::OUTPUT,
|
||||
);
|
||||
let mut size = mem::size_of::<u32>();
|
||||
let mut latency: u32 = 0;
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut latency);
|
||||
|
@ -285,6 +240,42 @@ pub fn get_stream_latency(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_stream_terminal_type(id: AudioStreamID) -> std::result::Result<u32, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
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> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(
|
||||
Property::StreamVirtualFormat,
|
||||
DeviceType::INPUT | DeviceType::OUTPUT,
|
||||
);
|
||||
let mut size = mem::size_of::<AudioStreamBasicDescription>();
|
||||
let mut format = AudioStreamBasicDescription::default();
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut format);
|
||||
if err == NO_ERR {
|
||||
Ok(format)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clock_domain(
|
||||
id: AudioStreamID,
|
||||
devtype: DeviceType,
|
||||
|
@ -312,8 +303,6 @@ pub enum Property {
|
|||
DeviceSampleRates,
|
||||
DeviceSource,
|
||||
DeviceSourceName,
|
||||
DeviceStreamConfiguration,
|
||||
DeviceStreamFormat,
|
||||
DeviceStreams,
|
||||
DeviceUID,
|
||||
HardwareDefaultInputDevice,
|
||||
|
@ -321,6 +310,8 @@ pub enum Property {
|
|||
HardwareDevices,
|
||||
ModelUID,
|
||||
StreamLatency,
|
||||
StreamTerminalType,
|
||||
StreamVirtualFormat,
|
||||
TransportType,
|
||||
ClockDomain,
|
||||
}
|
||||
|
@ -337,8 +328,6 @@ impl From<Property> for AudioObjectPropertySelector {
|
|||
Property::DeviceSampleRates => kAudioDevicePropertyAvailableNominalSampleRates,
|
||||
Property::DeviceSource => kAudioDevicePropertyDataSource,
|
||||
Property::DeviceSourceName => kAudioDevicePropertyDataSourceNameForIDCFString,
|
||||
Property::DeviceStreamConfiguration => kAudioDevicePropertyStreamConfiguration,
|
||||
Property::DeviceStreamFormat => kAudioDevicePropertyStreamFormat,
|
||||
Property::DeviceStreams => kAudioDevicePropertyStreams,
|
||||
Property::DeviceUID => kAudioDevicePropertyDeviceUID,
|
||||
Property::HardwareDefaultInputDevice => kAudioHardwarePropertyDefaultInputDevice,
|
||||
|
@ -346,6 +335,8 @@ impl From<Property> for AudioObjectPropertySelector {
|
|||
Property::HardwareDevices => kAudioHardwarePropertyDevices,
|
||||
Property::ModelUID => kAudioDevicePropertyModelUID,
|
||||
Property::StreamLatency => kAudioStreamPropertyLatency,
|
||||
Property::StreamTerminalType => kAudioStreamPropertyTerminalType,
|
||||
Property::StreamVirtualFormat => kAudioStreamPropertyVirtualFormat,
|
||||
Property::TransportType => kAudioDevicePropertyTransportType,
|
||||
Property::ClockDomain => kAudioDevicePropertyClockDomain,
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -19,15 +19,13 @@ impl Resampler {
|
|||
reclock: ffi::cubeb_resampler_reclock,
|
||||
) -> Self {
|
||||
let raw_resampler = unsafe {
|
||||
let in_params = if input_params.is_some() {
|
||||
input_params.as_mut().unwrap() as *mut ffi::cubeb_stream_params
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
let in_params = match &mut input_params {
|
||||
Some(p) => p,
|
||||
None => ptr::null_mut(),
|
||||
};
|
||||
let out_params = if output_params.is_some() {
|
||||
output_params.as_mut().unwrap() as *mut ffi::cubeb_stream_params
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
let out_params = match &mut output_params {
|
||||
Some(p) => p,
|
||||
None => ptr::null_mut(),
|
||||
};
|
||||
ffi::cubeb_resampler_create(
|
||||
stream,
|
||||
|
|
|
@ -42,7 +42,7 @@ fn test_aggregate_set_sub_devices_for_unknown_devices() {
|
|||
// application and print out the sub devices of them!
|
||||
#[test]
|
||||
fn test_aggregate_get_sub_devices() {
|
||||
let devices = test_get_all_devices(DeviceFilter::ExcludeCubebAggregate);
|
||||
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
|
||||
|
@ -108,7 +108,7 @@ fn test_aggregate_create_blank_device() {
|
|||
// 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();
|
||||
let devices = test_get_all_devices(DeviceFilter::IncludeCubebAggregate);
|
||||
let devices = test_get_all_devices(DeviceFilter::IncludeAll);
|
||||
let device = devices.into_iter().find(|dev| dev == &device).unwrap();
|
||||
let uid = get_device_global_uid(device).unwrap().into_string();
|
||||
assert!(uid.contains(PRIVATE_AGGREGATE_DEVICE_NAME));
|
||||
|
|
|
@ -45,8 +45,8 @@ fn test_increase_and_decrease_context_streams() {
|
|||
for i in 0..STREAMS {
|
||||
join_handles.push(thread::spawn(move || {
|
||||
let context = unsafe { &*(context_ptr_value as *const AudioUnitContext) };
|
||||
let global_latency = context.update_latency_by_adding_stream(i);
|
||||
global_latency
|
||||
|
||||
context.update_latency_by_adding_stream(i)
|
||||
}));
|
||||
}
|
||||
let mut latencies = vec![];
|
||||
|
@ -227,10 +227,11 @@ fn test_add_listener_unknown_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
assert_eq!(
|
||||
stream.add_device_listener(&listener),
|
||||
kAudioHardwareBadObjectError as OSStatus
|
||||
);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.add_device_listener(&listener));
|
||||
assert_eq!(res, kAudioHardwareBadObjectError as OSStatus);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -257,8 +258,15 @@ fn test_add_listener_then_remove_system_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
assert_eq!(stream.add_device_listener(&listener), NO_ERR);
|
||||
assert_eq!(stream.remove_device_listener(&listener), NO_ERR);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.add_device_listener(&listener));
|
||||
assert_eq!(res, NO_ERR);
|
||||
stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.remove_device_listener(&listener));
|
||||
assert_eq!(res, NO_ERR);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -283,7 +291,11 @@ fn test_remove_listener_without_adding_any_listener_before_system_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
assert_eq!(stream.remove_device_listener(&listener), NO_ERR);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.remove_device_listener(&listener));
|
||||
assert_eq!(res, NO_ERR);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -308,10 +320,11 @@ fn test_remove_listener_unknown_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
assert_eq!(
|
||||
stream.remove_device_listener(&listener),
|
||||
kAudioHardwareBadObjectError as OSStatus
|
||||
);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.remove_device_listener(&listener));
|
||||
assert_eq!(res, kAudioHardwareBadObjectError as OSStatus);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -700,7 +713,7 @@ fn test_convert_channel_layout() {
|
|||
}
|
||||
let layout_ref = unsafe { &(*(&layout as *const TestLayout as *const AudioChannelLayout)) };
|
||||
assert_eq!(
|
||||
&audiounit_convert_channel_layout(layout_ref),
|
||||
&audiounit_convert_channel_layout(layout_ref).unwrap(),
|
||||
expected_layout
|
||||
);
|
||||
}
|
||||
|
@ -711,7 +724,9 @@ fn test_convert_channel_layout() {
|
|||
#[test]
|
||||
fn test_get_preferred_channel_layout_output() {
|
||||
match test_get_default_audiounit(Scope::Output) {
|
||||
Some(unit) => assert!(!audiounit_get_preferred_channel_layout(unit.get_inner()).is_empty()),
|
||||
Some(unit) => assert!(!audiounit_get_preferred_channel_layout(unit.get_inner())
|
||||
.unwrap()
|
||||
.is_empty()),
|
||||
None => println!("No output audiounit for test."),
|
||||
}
|
||||
}
|
||||
|
@ -721,7 +736,9 @@ fn test_get_preferred_channel_layout_output() {
|
|||
#[test]
|
||||
fn test_get_current_channel_layout_output() {
|
||||
match test_get_default_audiounit(Scope::Output) {
|
||||
Some(unit) => assert!(!audiounit_get_current_channel_layout(unit.get_inner()).is_empty()),
|
||||
Some(unit) => assert!(!audiounit_get_current_channel_layout(unit.get_inner())
|
||||
.unwrap()
|
||||
.is_empty()),
|
||||
None => println!("No output audiounit for test."),
|
||||
}
|
||||
}
|
||||
|
@ -850,7 +867,7 @@ fn test_for_create_audiounit() {
|
|||
|
||||
// Check the output scope is enabled.
|
||||
if device.flags.contains(device_flags::DEV_OUTPUT) && default_output.is_some() {
|
||||
device.id = default_output.clone().unwrap();
|
||||
device.id = default_output.unwrap();
|
||||
let unit = create_audiounit(&device).unwrap();
|
||||
assert!(!unit.is_null());
|
||||
assert!(test_audiounit_scope_is_enabled(unit, Scope::Output));
|
||||
|
@ -864,7 +881,7 @@ fn test_for_create_audiounit() {
|
|||
|
||||
// Check the input scope is enabled.
|
||||
if device.flags.contains(device_flags::DEV_INPUT) && default_input.is_some() {
|
||||
let device_id = default_input.clone().unwrap();
|
||||
let device_id = default_input.unwrap();
|
||||
device.id = device_id;
|
||||
let unit = create_audiounit(&device).unwrap();
|
||||
assert!(!unit.is_null());
|
||||
|
@ -1047,8 +1064,12 @@ fn test_get_channel_count_of_inout_type() {
|
|||
fn test_channel_count(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
assert_eq!(
|
||||
get_channel_count(device, DeviceType::INPUT | DeviceType::OUTPUT).unwrap_err(),
|
||||
kAudioHardwareUnknownPropertyError as OSStatus
|
||||
get_channel_count(device, DeviceType::INPUT | DeviceType::OUTPUT),
|
||||
get_channel_count(device, DeviceType::INPUT).map(|c| c + get_channel_count(
|
||||
device,
|
||||
DeviceType::OUTPUT
|
||||
)
|
||||
.unwrap_or(0))
|
||||
);
|
||||
} else {
|
||||
println!("No device for {:?}.", scope);
|
||||
|
@ -1366,7 +1387,6 @@ fn test_create_device_info_by_unknown_device() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_create_device_info_with_unknown_type() {
|
||||
test_create_device_info_with_unknown_type_by_scope(Scope::Input);
|
||||
test_create_device_info_with_unknown_type_by_scope(Scope::Output);
|
||||
|
@ -1374,8 +1394,6 @@ fn test_create_device_info_with_unknown_type() {
|
|||
fn test_create_device_info_with_unknown_type_by_scope(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
assert!(create_cubeb_device_info(device, DeviceType::UNKNOWN).is_err());
|
||||
} else {
|
||||
panic!("Panic by default: No device for {:?}.", scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1416,23 +1434,6 @@ fn test_create_device_from_hwdev_with_inout_type() {
|
|||
}
|
||||
}
|
||||
|
||||
// is_aggregate_device
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_is_aggregate_device() {
|
||||
let mut aggregate_name = String::from(PRIVATE_AGGREGATE_DEVICE_NAME);
|
||||
aggregate_name.push_str("_something");
|
||||
let aggregate_name_cstring = CString::new(aggregate_name).unwrap();
|
||||
|
||||
let mut info = ffi::cubeb_device_info::default();
|
||||
info.friendly_name = aggregate_name_cstring.as_ptr();
|
||||
assert!(is_aggregate_device(&info));
|
||||
|
||||
let non_aggregate_name_cstring = CString::new("Hello World!").unwrap();
|
||||
info.friendly_name = non_aggregate_name_cstring.as_ptr();
|
||||
assert!(!is_aggregate_device(&info));
|
||||
}
|
||||
|
||||
// get_devices_of_type
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
|
@ -1443,7 +1444,7 @@ fn test_get_devices_of_type() {
|
|||
let input_devices = audiounit_get_devices_of_type(DeviceType::INPUT);
|
||||
let output_devices = audiounit_get_devices_of_type(DeviceType::OUTPUT);
|
||||
|
||||
let mut expected_all = test_get_all_devices(DeviceFilter::ExcludeCubebAggregate);
|
||||
let mut expected_all = test_get_all_devices(DeviceFilter::ExcludeCubebAggregateAndVPIO);
|
||||
expected_all.sort();
|
||||
assert_eq!(all_devices, expected_all);
|
||||
for device in all_devices.iter() {
|
||||
|
|
|
@ -21,8 +21,7 @@ use super::utils::{
|
|||
test_set_default_device, Scope, StreamType, TestDevicePlugger, TestDeviceSwitcher,
|
||||
};
|
||||
use super::*;
|
||||
use std::fmt::Debug;
|
||||
use std::thread;
|
||||
use std::sync::{LockResult, MutexGuard, WaitTimeoutResult};
|
||||
|
||||
// Switch default devices used by the active streams, to test stream reinitialization
|
||||
// ================================================================================================
|
||||
|
@ -48,22 +47,25 @@ fn test_switch_device_in_scope(scope: Scope) {
|
|||
|
||||
let mut device_switcher = TestDeviceSwitcher::new(scope.clone());
|
||||
|
||||
let count = Arc::new(Mutex::new(0));
|
||||
let also_count = Arc::clone(&count);
|
||||
let notifier = Arc::new(Notifier::new(0));
|
||||
let also_notifier = notifier.clone();
|
||||
let listener = test_create_device_change_listener(scope.clone(), move |_addresses| {
|
||||
let mut cnt = also_count.lock().unwrap();
|
||||
let mut cnt = notifier.lock().unwrap();
|
||||
*cnt += 1;
|
||||
notifier.notify(cnt);
|
||||
NO_ERR
|
||||
});
|
||||
listener.start();
|
||||
|
||||
let mut changed_watcher = Watcher::new(&count);
|
||||
let changed_watcher = Watcher::new(&also_notifier);
|
||||
test_get_started_stream_in_scope(scope.clone(), move |_stream| loop {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
changed_watcher.prepare();
|
||||
let mut guard = changed_watcher.lock().unwrap();
|
||||
let start_cnt = guard.clone();
|
||||
device_switcher.next();
|
||||
changed_watcher.wait_for_change();
|
||||
if changed_watcher.current_result() >= devices.len() {
|
||||
guard = changed_watcher
|
||||
.wait_while(guard, |cnt| *cnt == start_cnt)
|
||||
.unwrap();
|
||||
if *guard >= devices.len() {
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -204,77 +206,90 @@ fn test_plug_and_unplug_device_in_scope(scope: Scope) {
|
|||
let mut context = AudioUnitContext::new();
|
||||
|
||||
// Register the devices-changed callbacks.
|
||||
let input_count = Arc::new(Mutex::new(0u32));
|
||||
let also_input_count = Arc::clone(&input_count);
|
||||
let input_mtx_ptr = also_input_count.as_ref() as *const Mutex<u32>;
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct Counts {
|
||||
input: u32,
|
||||
output: u32,
|
||||
}
|
||||
impl Counts {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
input: 0,
|
||||
output: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
let counts = Arc::new(Notifier::new(Counts::new()));
|
||||
let counts_notifier_ptr = counts.as_ref() as *const Notifier<Counts>;
|
||||
|
||||
assert!(context
|
||||
.register_device_collection_changed(
|
||||
DeviceType::INPUT,
|
||||
Some(input_changed_callback),
|
||||
input_mtx_ptr as *mut c_void,
|
||||
counts_notifier_ptr as *mut c_void,
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
let output_count = Arc::new(Mutex::new(0u32));
|
||||
let also_output_count = Arc::clone(&output_count);
|
||||
let output_mtx_ptr = also_output_count.as_ref() as *const Mutex<u32>;
|
||||
|
||||
assert!(context
|
||||
.register_device_collection_changed(
|
||||
DeviceType::OUTPUT,
|
||||
Some(output_changed_callback),
|
||||
output_mtx_ptr as *mut c_void,
|
||||
counts_notifier_ptr as *mut c_void,
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
let mut input_watcher = Watcher::new(&input_count);
|
||||
let mut output_watcher = Watcher::new(&output_count);
|
||||
let counts_watcher = Watcher::new(&counts);
|
||||
|
||||
let mut device_plugger = TestDevicePlugger::new(scope).unwrap();
|
||||
|
||||
// Simulate adding devices and monitor the devices-changed callbacks.
|
||||
input_watcher.prepare();
|
||||
output_watcher.prepare();
|
||||
{
|
||||
// Simulate adding devices and monitor the devices-changed callbacks.
|
||||
let mut counts_guard = counts.lock().unwrap();
|
||||
let counts_start = counts_guard.clone();
|
||||
|
||||
assert!(device_plugger.plug().is_ok());
|
||||
assert!(device_plugger.plug().is_ok());
|
||||
|
||||
if is_input {
|
||||
input_watcher.wait_for_change();
|
||||
}
|
||||
if is_output {
|
||||
output_watcher.wait_for_change();
|
||||
counts_guard = counts_watcher
|
||||
.wait_while(counts_guard, |counts| {
|
||||
(is_input && counts.input == counts_start.input)
|
||||
|| (is_output && counts.output == counts_start.output)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Check changed count.
|
||||
assert_eq!(counts_guard.input, if is_input { 1 } else { 0 });
|
||||
assert_eq!(counts_guard.output, if is_output { 1 } else { 0 });
|
||||
}
|
||||
|
||||
// Check changed count.
|
||||
check_result(is_input, (1, 0), &input_watcher);
|
||||
check_result(is_output, (1, 0), &output_watcher);
|
||||
{
|
||||
// Simulate removing devices and monitor the devices-changed callbacks.
|
||||
let mut counts_guard = counts.lock().unwrap();
|
||||
let counts_start = counts_guard.clone();
|
||||
|
||||
// Simulate removing devices and monitor the devices-changed callbacks.
|
||||
input_watcher.prepare();
|
||||
output_watcher.prepare();
|
||||
assert!(device_plugger.unplug().is_ok());
|
||||
|
||||
assert!(device_plugger.unplug().is_ok());
|
||||
counts_guard = counts_watcher
|
||||
.wait_while(counts_guard, |counts| {
|
||||
(is_input && counts.input == counts_start.input)
|
||||
|| (is_output && counts.output == counts_start.output)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if is_input {
|
||||
input_watcher.wait_for_change();
|
||||
// Check changed count.
|
||||
assert_eq!(counts_guard.input, if is_input { 2 } else { 0 });
|
||||
assert_eq!(counts_guard.output, if is_output { 2 } else { 0 });
|
||||
}
|
||||
if is_output {
|
||||
output_watcher.wait_for_change();
|
||||
}
|
||||
|
||||
check_result(is_input, (2, 0), &input_watcher);
|
||||
check_result(is_output, (2, 0), &output_watcher);
|
||||
|
||||
extern "C" fn input_changed_callback(context: *mut ffi::cubeb, data: *mut c_void) {
|
||||
println!(
|
||||
"Input device collection @ {:p} is changed. Data @ {:p}",
|
||||
context, data
|
||||
);
|
||||
let count = unsafe { &*(data as *const Mutex<u32>) };
|
||||
let notifier = unsafe { &*(data as *const Notifier<Counts>) };
|
||||
{
|
||||
let mut guard = count.lock().unwrap();
|
||||
*guard += 1;
|
||||
let mut counts = notifier.lock().unwrap();
|
||||
counts.input += 1;
|
||||
notifier.notify(counts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,23 +298,13 @@ fn test_plug_and_unplug_device_in_scope(scope: Scope) {
|
|||
"output device collection @ {:p} is changed. Data @ {:p}",
|
||||
context, data
|
||||
);
|
||||
let count = unsafe { &*(data as *const Mutex<u32>) };
|
||||
let notifier = unsafe { &*(data as *const Notifier<Counts>) };
|
||||
{
|
||||
let mut guard = count.lock().unwrap();
|
||||
*guard += 1;
|
||||
let mut counts = notifier.lock().unwrap();
|
||||
counts.output += 1;
|
||||
notifier.notify(counts);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_result<T: Clone + Debug + PartialEq>(
|
||||
in_scope: bool,
|
||||
expected: (T, T),
|
||||
watcher: &Watcher<T>,
|
||||
) {
|
||||
assert_eq!(
|
||||
watcher.current_result(),
|
||||
if in_scope { expected.0 } else { expected.1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Switch default devices used by the active streams, to test device changed callback
|
||||
|
@ -352,16 +357,15 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
|
|||
return;
|
||||
}
|
||||
|
||||
let changed_count = Arc::new(Mutex::new(0u32));
|
||||
let also_changed_count = Arc::clone(&changed_count);
|
||||
let mtx_ptr = also_changed_count.as_ref() as *const Mutex<u32>;
|
||||
let changed_count = Arc::new(Notifier::new(0u32));
|
||||
let notifier_ptr = changed_count.as_ref() as *const Notifier<u32>;
|
||||
|
||||
test_get_stream_with_device_changed_callback(
|
||||
"stream: test callback for default device changed",
|
||||
stm_type,
|
||||
None, // Use default input device.
|
||||
None, // Use default output device.
|
||||
mtx_ptr as *mut c_void,
|
||||
notifier_ptr as *mut c_void,
|
||||
state_callback,
|
||||
device_changed_callback,
|
||||
|stream| {
|
||||
|
@ -371,17 +375,22 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
|
|||
// be assigned to the default device, since the device list for setting
|
||||
// default device is cached upon {input, output}_device_switcher is initialized.
|
||||
|
||||
let mut changed_watcher = Watcher::new(&changed_count);
|
||||
let changed_watcher = Watcher::new(&changed_count);
|
||||
|
||||
if let Some(devices) = inputs {
|
||||
let mut device_switcher = TestDeviceSwitcher::new(Scope::Input);
|
||||
for _ in 0..devices {
|
||||
// While the stream is re-initializing for the default device switch,
|
||||
// switching for the default device again will be ignored.
|
||||
while stream.switching_device.load(atomic::Ordering::SeqCst) {}
|
||||
changed_watcher.prepare();
|
||||
while stream.switching_device.load(atomic::Ordering::SeqCst) {
|
||||
std::hint::spin_loop()
|
||||
}
|
||||
let guard = changed_watcher.lock().unwrap();
|
||||
let start_cnt = guard.clone();
|
||||
device_switcher.next();
|
||||
changed_watcher.wait_for_change();
|
||||
changed_watcher
|
||||
.wait_while(guard, |cnt| *cnt == start_cnt)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,10 +399,15 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
|
|||
for _ in 0..devices {
|
||||
// While the stream is re-initializing for the default device switch,
|
||||
// switching for the default device again will be ignored.
|
||||
while stream.switching_device.load(atomic::Ordering::SeqCst) {}
|
||||
changed_watcher.prepare();
|
||||
while stream.switching_device.load(atomic::Ordering::SeqCst) {
|
||||
std::hint::spin_loop()
|
||||
}
|
||||
let guard = changed_watcher.lock().unwrap();
|
||||
let start_cnt = guard.clone();
|
||||
device_switcher.next();
|
||||
changed_watcher.wait_for_change();
|
||||
changed_watcher
|
||||
.wait_while(guard, |cnt| *cnt == start_cnt)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -410,11 +424,10 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
|
|||
|
||||
extern "C" fn device_changed_callback(data: *mut c_void) {
|
||||
println!("Device change callback. data @ {:p}", data);
|
||||
let count = unsafe { &*(data as *const Mutex<u32>) };
|
||||
{
|
||||
let mut guard = count.lock().unwrap();
|
||||
*guard += 1;
|
||||
}
|
||||
let notifier = unsafe { &*(data as *const Notifier<u32>) };
|
||||
let mut count_guard = notifier.lock().unwrap();
|
||||
*count_guard += 1;
|
||||
notifier.notify(count_guard);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,7 +453,7 @@ fn test_destroy_input_stream_after_unplugging_a_nondefault_input_device() {
|
|||
#[test]
|
||||
fn test_suspend_input_stream_by_unplugging_a_nondefault_input_device() {
|
||||
// Expect to get an error state callback by device-changed event handler
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::INPUT, Scope::Input, false, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::INPUT, Scope::Input, false, 2000);
|
||||
}
|
||||
|
||||
// Unplug the default input device for an input stream
|
||||
|
@ -459,7 +472,7 @@ fn test_destroy_input_stream_after_unplugging_a_default_input_device() {
|
|||
fn test_reinit_input_stream_by_unplugging_a_default_input_device() {
|
||||
// Expect to get an device-changed callback by device-changed event handler,
|
||||
// which will reinitialize the stream behind the scenes
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::INPUT, Scope::Input, true, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::INPUT, Scope::Input, true, 2000);
|
||||
}
|
||||
|
||||
// Output-only stream
|
||||
|
@ -467,18 +480,16 @@ fn test_reinit_input_stream_by_unplugging_a_default_input_device() {
|
|||
|
||||
// Unplug the non-default output device for an output stream
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// FIXME: We don't monitor the alive-status for output device currently
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_destroy_output_stream_after_unplugging_a_nondefault_output_device() {
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::OUTPUT, Scope::Output, false, 0);
|
||||
}
|
||||
|
||||
// FIXME: We don't monitor the alive-status for output device currently
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_suspend_output_stream_by_unplugging_a_nondefault_output_device() {
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::OUTPUT, Scope::Output, false, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::OUTPUT, Scope::Output, false, 2000);
|
||||
}
|
||||
|
||||
// Unplug the default output device for an output stream
|
||||
|
@ -498,7 +509,7 @@ fn test_destroy_output_stream_after_unplugging_a_default_output_device() {
|
|||
fn test_reinit_output_stream_by_unplugging_a_default_output_device() {
|
||||
// Expect to get an device-changed callback by device-changed event handler,
|
||||
// which will reinitialize the stream behind the scenes
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::OUTPUT, Scope::Output, true, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::OUTPUT, Scope::Output, true, 2000);
|
||||
}
|
||||
|
||||
// Duplex stream
|
||||
|
@ -518,24 +529,22 @@ fn test_destroy_duplex_stream_after_unplugging_a_nondefault_input_device() {
|
|||
#[test]
|
||||
fn test_suspend_duplex_stream_by_unplugging_a_nondefault_input_device() {
|
||||
// Expect to get an error state callback by device-changed event handler
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Input, false, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Input, false, 2000);
|
||||
}
|
||||
|
||||
// Unplug the non-default output device for a duplex stream
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// FIXME: We don't monitor the alive-status for output device currently
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_destroy_duplex_stream_after_unplugging_a_nondefault_output_device() {
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Output, false, 0);
|
||||
}
|
||||
|
||||
// FIXME: We don't monitor the alive-status for output device currently
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_suspend_duplex_stream_by_unplugging_a_nondefault_output_device() {
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Output, false, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Output, false, 2000);
|
||||
}
|
||||
|
||||
// Unplug the non-default in-out device for a duplex stream
|
||||
|
@ -559,7 +568,7 @@ fn test_destroy_duplex_stream_after_unplugging_a_default_input_device() {
|
|||
fn test_reinit_duplex_stream_by_unplugging_a_default_input_device() {
|
||||
// Expect to get an device-changed callback by device-changed event handler,
|
||||
// which will reinitialize the stream behind the scenes
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Input, true, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Input, true, 2000);
|
||||
}
|
||||
|
||||
// Unplug the default ouput device for a duplex stream
|
||||
|
@ -579,14 +588,14 @@ fn test_destroy_duplex_stream_after_unplugging_a_default_output_device() {
|
|||
fn test_reinit_duplex_stream_by_unplugging_a_default_output_device() {
|
||||
// Expect to get an device-changed callback by device-changed event handler,
|
||||
// which will reinitialize the stream behind the scenes
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Output, true, 500);
|
||||
test_unplug_a_device_on_an_active_stream(StreamType::DUPLEX, Scope::Output, true, 2000);
|
||||
}
|
||||
|
||||
fn test_unplug_a_device_on_an_active_stream(
|
||||
stream_type: StreamType,
|
||||
device_scope: Scope,
|
||||
set_device_to_default: bool,
|
||||
sleep: u64,
|
||||
wait_up_to_ms: u64,
|
||||
) {
|
||||
let has_input = test_get_default_device(Scope::Input).is_some();
|
||||
let has_output = test_get_default_device(Scope::Output).is_some();
|
||||
|
@ -670,62 +679,72 @@ fn test_unplug_a_device_on_an_active_stream(
|
|||
),
|
||||
};
|
||||
|
||||
struct SharedData {
|
||||
changed_count: Arc<Mutex<u32>>,
|
||||
states: Arc<Mutex<Vec<ffi::cubeb_state>>>,
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct Data {
|
||||
changed_count: u32,
|
||||
states: Vec<ffi::cubeb_state>,
|
||||
}
|
||||
|
||||
let mut shared_data = SharedData {
|
||||
changed_count: Arc::new(Mutex::new(0u32)),
|
||||
states: Arc::new(Mutex::new(vec![])),
|
||||
};
|
||||
impl Data {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
changed_count: 0,
|
||||
states: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let notifier = Arc::new(Notifier::new(Data::new()));
|
||||
let notifier_ptr = notifier.as_ref() as *const Notifier<Data>;
|
||||
|
||||
test_get_stream_with_device_changed_callback(
|
||||
"stream: test stream reinit/destroy after unplugging a device",
|
||||
stream_type,
|
||||
input_device,
|
||||
output_device,
|
||||
&mut shared_data as *mut SharedData as *mut c_void,
|
||||
notifier_ptr as *mut c_void,
|
||||
state_callback,
|
||||
device_changed_callback,
|
||||
|stream| {
|
||||
let mut changed_watcher = Watcher::new(&shared_data.changed_count);
|
||||
changed_watcher.prepare();
|
||||
stream.start();
|
||||
// Wait for stream data callback.
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
let changed_watcher = Watcher::new(¬ifier);
|
||||
let mut data_guard = notifier.lock().unwrap();
|
||||
assert_eq!(data_guard.states.last().unwrap(), &ffi::CUBEB_STATE_STARTED);
|
||||
|
||||
println!(
|
||||
"Stream runs on the device {} for {:?}",
|
||||
plugger.get_device_id(),
|
||||
device_scope
|
||||
);
|
||||
|
||||
let dev = plugger.get_device_id();
|
||||
let start_changed_count = data_guard.changed_count.clone();
|
||||
|
||||
assert!(plugger.unplug().is_ok());
|
||||
|
||||
if set_device_to_default {
|
||||
// The stream will be reinitialized if it follows the default input or output device.
|
||||
changed_watcher.wait_for_change();
|
||||
}
|
||||
|
||||
if sleep > 0 {
|
||||
println!(
|
||||
"Wait {} ms for stream re-initialization, or state callback",
|
||||
sleep
|
||||
);
|
||||
thread::sleep(Duration::from_millis(sleep));
|
||||
|
||||
if !set_device_to_default {
|
||||
// stream can be dropped immediately before device-changed callback
|
||||
// so we only check the states if we wait for it explicitly.
|
||||
let guard = shared_data.states.lock().unwrap();
|
||||
assert!(guard.last().is_some());
|
||||
assert_eq!(guard.last().unwrap(), &ffi::CUBEB_STATE_ERROR);
|
||||
}
|
||||
} else {
|
||||
println!("Destroy the stream immediately");
|
||||
if set_device_to_default {
|
||||
println!("Stream re-initialization may run at the same time when stream is being destroyed");
|
||||
}
|
||||
println!("Waiting for default device to change and reinit");
|
||||
data_guard = changed_watcher
|
||||
.wait_while(data_guard, |data| {
|
||||
data.changed_count == start_changed_count
|
||||
|| data.states.last().unwrap_or(&ffi::CUBEB_STATE_ERROR)
|
||||
!= &ffi::CUBEB_STATE_STARTED
|
||||
})
|
||||
.unwrap();
|
||||
} else if wait_up_to_ms > 0 {
|
||||
// stream can be dropped immediately before device-changed callback
|
||||
// so we only check the states if we wait for it explicitly.
|
||||
println!("Waiting for non-default device to enter error state");
|
||||
let (new_guard, timeout_res) = changed_watcher
|
||||
.wait_timeout_while(data_guard, Duration::from_millis(wait_up_to_ms), |data| {
|
||||
data.states.last().unwrap_or(&ffi::CUBEB_STATE_STARTED)
|
||||
!= &ffi::CUBEB_STATE_ERROR
|
||||
})
|
||||
.unwrap();
|
||||
assert!(!timeout_res.timed_out());
|
||||
data_guard = new_guard;
|
||||
}
|
||||
|
||||
println!(
|
||||
|
@ -745,6 +764,7 @@ fn test_unplug_a_device_on_an_active_stream(
|
|||
user_ptr: *mut c_void,
|
||||
state: ffi::cubeb_state,
|
||||
) {
|
||||
println!("Device change callback. user_ptr @ {:p}", user_ptr);
|
||||
assert!(!stream.is_null());
|
||||
println!(
|
||||
"state: {}",
|
||||
|
@ -756,52 +776,79 @@ fn test_unplug_a_device_on_an_active_stream(
|
|||
_ => "unknown",
|
||||
}
|
||||
);
|
||||
let shared_data = unsafe { &mut *(user_ptr as *mut SharedData) };
|
||||
{
|
||||
let mut guard = shared_data.states.lock().unwrap();
|
||||
guard.push(state);
|
||||
let notifier = unsafe { &mut *(user_ptr as *mut Notifier<Data>) };
|
||||
let mut data_guard = notifier.lock().unwrap();
|
||||
data_guard.states.push(state);
|
||||
notifier.notify(data_guard);
|
||||
}
|
||||
|
||||
extern "C" fn device_changed_callback(user_ptr: *mut c_void) {
|
||||
println!("Device change callback. user_ptr @ {:p}", user_ptr);
|
||||
let notifier = unsafe { &mut *(user_ptr as *mut Notifier<Data>) };
|
||||
let mut data_guard = notifier.lock().unwrap();
|
||||
data_guard.changed_count += 1;
|
||||
notifier.notify(data_guard);
|
||||
}
|
||||
}
|
||||
|
||||
struct Notifier<T> {
|
||||
value: Mutex<T>,
|
||||
cvar: Condvar,
|
||||
}
|
||||
|
||||
impl<T> Notifier<T> {
|
||||
fn new(value: T) -> Self {
|
||||
Self {
|
||||
value: Mutex::new(value),
|
||||
cvar: Condvar::new(),
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn device_changed_callback(data: *mut c_void) {
|
||||
println!("Device change callback. data @ {:p}", data);
|
||||
let shared_data = unsafe { &mut *(data as *mut SharedData) };
|
||||
{
|
||||
let mut guard = shared_data.changed_count.lock().unwrap();
|
||||
*guard += 1;
|
||||
}
|
||||
fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
|
||||
self.value.lock()
|
||||
}
|
||||
|
||||
fn notify(&self, _guard: MutexGuard<'_, T>) {
|
||||
self.cvar.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
struct Watcher<T: Clone + PartialEq> {
|
||||
watching: Arc<Mutex<T>>,
|
||||
current: Option<T>,
|
||||
notifier: Arc<Notifier<T>>,
|
||||
}
|
||||
|
||||
impl<T: Clone + PartialEq> Watcher<T> {
|
||||
fn new(value: &Arc<Mutex<T>>) -> Self {
|
||||
fn new(value: &Arc<Notifier<T>>) -> Self {
|
||||
Self {
|
||||
watching: Arc::clone(value),
|
||||
current: None,
|
||||
notifier: Arc::clone(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(&mut self) {
|
||||
self.current = Some(self.current_result());
|
||||
fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
|
||||
self.notifier.lock()
|
||||
}
|
||||
|
||||
fn wait_for_change(&self) {
|
||||
loop {
|
||||
if self.current_result() != self.current.clone().unwrap() {
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
fn wait_while<'a, F>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
condition: F,
|
||||
) -> LockResult<MutexGuard<'a, T>>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
self.notifier.cvar.wait_while(guard, condition)
|
||||
}
|
||||
|
||||
fn current_result(&self) -> T {
|
||||
let guard = self.watching.lock().unwrap();
|
||||
guard.clone()
|
||||
fn wait_timeout_while<'a, F>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
dur: Duration,
|
||||
condition: F,
|
||||
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
self.notifier.cvar.wait_timeout_while(guard, dur, condition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -282,6 +282,7 @@ fn test_get_device_streams() {
|
|||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
println!("streams on the input device: {:?}", streams);
|
||||
assert!(!streams.is_empty());
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
@ -289,6 +290,7 @@ fn test_get_device_streams() {
|
|||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
println!("streams on the output device: {:?}", streams);
|
||||
assert!(!streams.is_empty());
|
||||
} else {
|
||||
println!("No output device.");
|
||||
}
|
||||
|
@ -350,56 +352,6 @@ fn test_get_ranges_of_device_sample_rate_by_unknown_device() {
|
|||
assert!(get_ranges_of_device_sample_rate(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
}
|
||||
|
||||
// get_device_stream_format
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_get_device_stream_format() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let format = get_device_stream_format(device, DeviceType::INPUT).unwrap();
|
||||
println!("input stream format: {:?}", format);
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let format = get_device_stream_format(device, DeviceType::OUTPUT).unwrap();
|
||||
println!("output stream format: {:?}", format);
|
||||
} else {
|
||||
println!("No output device.");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_stream_format_by_unknown_device() {
|
||||
assert!(get_device_stream_format(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
}
|
||||
|
||||
// get_device_stream_configuration
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_get_device_stream_configuration() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let buffers = get_device_stream_configuration(device, DeviceType::INPUT).unwrap();
|
||||
println!("input stream config: {:?}", buffers);
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let buffers = get_device_stream_configuration(device, DeviceType::OUTPUT).unwrap();
|
||||
println!("output stream config: {:?}", buffers);
|
||||
} else {
|
||||
println!("No output device.");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_stream_configuration_by_unknown_device() {
|
||||
assert!(get_device_stream_configuration(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
}
|
||||
|
||||
// get_stream_latency
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
|
@ -407,7 +359,7 @@ fn test_get_stream_latency() {
|
|||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
for stream in streams {
|
||||
let latency = get_stream_latency(stream, DeviceType::INPUT).unwrap();
|
||||
let latency = get_stream_latency(stream).unwrap();
|
||||
println!("latency of the input stream {} is {}", stream, latency);
|
||||
}
|
||||
} else {
|
||||
|
@ -417,7 +369,7 @@ fn test_get_stream_latency() {
|
|||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
for stream in streams {
|
||||
let latency = get_stream_latency(stream, DeviceType::OUTPUT).unwrap();
|
||||
let latency = get_stream_latency(stream).unwrap();
|
||||
println!("latency of the output stream {} is {}", stream, latency);
|
||||
}
|
||||
} else {
|
||||
|
@ -428,5 +380,94 @@ fn test_get_stream_latency() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_stream_latency_by_unknown_device() {
|
||||
assert!(get_stream_latency(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(get_stream_latency(kAudioObjectUnknown).is_err());
|
||||
}
|
||||
|
||||
// get_stream_virtual_format
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_get_stream_virtual_format() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
let formats = streams
|
||||
.iter()
|
||||
.map(|s| get_stream_virtual_format(*s))
|
||||
.collect::<Vec<std::result::Result<AudioStreamBasicDescription, OSStatus>>>();
|
||||
println!("input stream formats: {:?}", formats);
|
||||
assert!(!formats.is_empty());
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
let formats = streams
|
||||
.iter()
|
||||
.map(|s| get_stream_virtual_format(*s))
|
||||
.collect::<Vec<std::result::Result<AudioStreamBasicDescription, OSStatus>>>();
|
||||
println!("output stream formats: {:?}", formats);
|
||||
assert!(!formats.is_empty());
|
||||
} else {
|
||||
println!("No output device.");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_stream_virtual_format_by_unknown_stream() {
|
||||
assert!(get_stream_virtual_format(kAudioObjectUnknown).is_err());
|
||||
}
|
||||
|
||||
// get_stream_terminal_type
|
||||
// ------------------------------------
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
for stream in streams {
|
||||
assert_eq!(
|
||||
terminal_type_to_device_type(get_stream_terminal_type(stream).unwrap()),
|
||||
Some(DeviceType::INPUT)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
for stream in streams {
|
||||
assert_eq!(
|
||||
terminal_type_to_device_type(get_stream_terminal_type(stream).unwrap()),
|
||||
Some(DeviceType::OUTPUT)
|
||||
);
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
extern crate itertools;
|
||||
|
||||
use self::itertools::iproduct;
|
||||
use super::utils::{
|
||||
test_get_default_device, test_ops_context_operation, test_ops_stream_operation, Scope,
|
||||
noop_data_callback, test_get_default_device, test_ops_context_operation,
|
||||
test_ops_stream_operation, Scope,
|
||||
};
|
||||
use super::*;
|
||||
|
||||
|
@ -449,6 +453,192 @@ fn test_ops_context_register_device_collection_changed_manual() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_context_stream_init_no_stream_params() {
|
||||
let name = "context: stream_init with no stream params";
|
||||
test_ops_context_operation(name, |context_ptr| {
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
ptr::null_mut(), // Use default input device.
|
||||
ptr::null_mut(), // No input parameters.
|
||||
ptr::null_mut(), // Use default output device.
|
||||
ptr::null_mut(), // No output parameters.
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_ERROR_INVALID_PARAMETER
|
||||
);
|
||||
assert!(stream.is_null());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_context_stream_init_no_input_stream_params() {
|
||||
let name = "context: stream_init with no input stream params";
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
if input_device.is_none() {
|
||||
println!("No input device to perform input tests for \"{}\".", name);
|
||||
return;
|
||||
}
|
||||
test_ops_context_operation(name, |context_ptr| {
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
input_device.unwrap() as ffi::cubeb_devid,
|
||||
ptr::null_mut(), // No input parameters.
|
||||
ptr::null_mut(), // Use default output device.
|
||||
ptr::null_mut(), // No output parameters.
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_ERROR_INVALID_PARAMETER
|
||||
);
|
||||
assert!(stream.is_null());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_context_stream_init_no_output_stream_params() {
|
||||
let name = "context: stream_init with no output stream params";
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
if output_device.is_none() {
|
||||
println!("No output device to perform output tests for \"{}\".", name);
|
||||
return;
|
||||
}
|
||||
test_ops_context_operation(name, |context_ptr| {
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
ptr::null_mut(), // Use default input device.
|
||||
ptr::null_mut(), // No input parameters.
|
||||
output_device.unwrap() as ffi::cubeb_devid,
|
||||
ptr::null_mut(), // No output parameters.
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_ERROR_INVALID_PARAMETER
|
||||
);
|
||||
assert!(stream.is_null());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_context_stream_init_no_data_callback() {
|
||||
let name = "context: stream_init with no data callback";
|
||||
test_ops_context_operation(name, |context_ptr| {
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
|
||||
let mut output_params = ffi::cubeb_stream_params::default();
|
||||
output_params.format = ffi::CUBEB_SAMPLE_FLOAT32NE;
|
||||
output_params.rate = 44100;
|
||||
output_params.channels = 2;
|
||||
output_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
ptr::null_mut(), // Use default input device.
|
||||
ptr::null_mut(), // No input parameters.
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
None, // No data callback.
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_ERROR_INVALID_PARAMETER
|
||||
);
|
||||
assert!(stream.is_null());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_context_stream_init_channel_rate_combinations() {
|
||||
let name = "context: stream_init with various channels and rates";
|
||||
test_ops_context_operation(name, |context_ptr| {
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
|
||||
const MAX_NUM_CHANNELS: u32 = 32;
|
||||
let channel_values: Vec<u32> = vec![1, 2, 3, 4, 6];
|
||||
let freq_values: Vec<u32> = vec![16000, 24000, 44100, 48000];
|
||||
let is_float_values: Vec<bool> = vec![false, true];
|
||||
|
||||
for (channels, freq, is_float) in iproduct!(channel_values, freq_values, is_float_values) {
|
||||
assert!(channels < MAX_NUM_CHANNELS);
|
||||
println!("--------------------------");
|
||||
println!(
|
||||
"Testing {} channel(s), {} Hz, {}\n",
|
||||
channels,
|
||||
freq,
|
||||
if is_float { "float" } else { "short" }
|
||||
);
|
||||
|
||||
let mut output_params = ffi::cubeb_stream_params::default();
|
||||
output_params.format = if is_float {
|
||||
ffi::CUBEB_SAMPLE_FLOAT32NE
|
||||
} else {
|
||||
ffi::CUBEB_SAMPLE_S16NE
|
||||
};
|
||||
output_params.rate = freq;
|
||||
output_params.channels = channels;
|
||||
output_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
ptr::null_mut(), // Use default input device.
|
||||
ptr::null_mut(), // No input parameters.
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback), // No data callback.
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
assert!(!stream.is_null());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Stream Operations
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
fn test_default_output_stream_operation<F>(name: &'static str, operation: F)
|
||||
|
@ -470,8 +660,8 @@ where
|
|||
ptr::null_mut(), // No input parameters.
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
None, // No data callback.
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
|
@ -504,8 +694,8 @@ where
|
|||
&mut input_params,
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
None, // No data callback.
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::utils::{
|
||||
test_audiounit_get_buffer_frame_size, test_get_default_audiounit, test_get_default_device,
|
||||
test_ops_context_operation, PropertyScope, Scope,
|
||||
noop_data_callback, test_audiounit_get_buffer_frame_size, test_get_default_audiounit,
|
||||
test_get_default_device, test_ops_context_operation, PropertyScope, Scope,
|
||||
};
|
||||
use super::*;
|
||||
use std::thread;
|
||||
|
@ -232,7 +232,7 @@ fn create_streams_by_ops_in_parallel_with_different_latency<F>(
|
|||
ptr::null_mut()
|
||||
},
|
||||
latency_frames,
|
||||
None, // No data callback.
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
|
@ -468,7 +468,7 @@ fn create_streams_in_parallel_with_different_latency<F>(
|
|||
None
|
||||
},
|
||||
latency_frames,
|
||||
None, // No data callback.
|
||||
Some(noop_data_callback),
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
)
|
||||
|
|
|
@ -2,6 +2,29 @@ use super::*;
|
|||
|
||||
// Common Utils
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
pub extern "C" fn noop_data_callback(
|
||||
stream: *mut ffi::cubeb_stream,
|
||||
_user_ptr: *mut c_void,
|
||||
_input_buffer: *const c_void,
|
||||
output_buffer: *mut c_void,
|
||||
nframes: i64,
|
||||
) -> i64 {
|
||||
assert!(!stream.is_null());
|
||||
|
||||
// Feed silence data to output buffer
|
||||
if !output_buffer.is_null() {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
let channels = stm.core_stream_data.output_stream_params.channels();
|
||||
let samples = nframes as usize * channels as usize;
|
||||
let sample_size = cubeb_sample_size(stm.core_stream_data.output_stream_params.format());
|
||||
unsafe {
|
||||
ptr::write_bytes(output_buffer, 0, samples * sample_size);
|
||||
}
|
||||
}
|
||||
|
||||
nframes
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Scope {
|
||||
Input,
|
||||
|
@ -189,11 +212,7 @@ fn test_enable_audiounit_in_scope(
|
|||
}
|
||||
|
||||
pub fn test_get_source_name(device: AudioObjectID, scope: Scope) -> Option<String> {
|
||||
if let Some(source) = test_get_source_data(device, scope) {
|
||||
Some(u32_to_string(source))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
test_get_source_data(device, scope).map(u32_to_string)
|
||||
}
|
||||
|
||||
pub fn test_get_source_data(device: AudioObjectID, scope: Scope) -> Option<u32> {
|
||||
|
@ -238,8 +257,8 @@ fn u32_to_string(data: u32) -> String {
|
|||
}
|
||||
|
||||
pub enum DeviceFilter {
|
||||
ExcludeCubebAggregate,
|
||||
IncludeCubebAggregate,
|
||||
ExcludeCubebAggregateAndVPIO,
|
||||
IncludeAll,
|
||||
}
|
||||
pub fn test_get_all_devices(filter: DeviceFilter) -> Vec<AudioObjectID> {
|
||||
let mut devices = Vec::new();
|
||||
|
@ -284,11 +303,12 @@ pub fn test_get_all_devices(filter: DeviceFilter) -> Vec<AudioObjectID> {
|
|||
}
|
||||
|
||||
match filter {
|
||||
DeviceFilter::ExcludeCubebAggregate => {
|
||||
DeviceFilter::ExcludeCubebAggregateAndVPIO => {
|
||||
devices.retain(|&device| {
|
||||
if let Ok(uid) = get_device_global_uid(device) {
|
||||
let uid = uid.into_string();
|
||||
!uid.contains(PRIVATE_AGGREGATE_DEVICE_NAME)
|
||||
&& !uid.contains(VOICEPROCESSING_AGGREGATE_DEVICE_NAME)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
@ -301,7 +321,7 @@ pub fn test_get_all_devices(filter: DeviceFilter) -> Vec<AudioObjectID> {
|
|||
}
|
||||
|
||||
pub fn test_get_devices_in_scope(scope: Scope) -> Vec<AudioObjectID> {
|
||||
let mut devices = test_get_all_devices(DeviceFilter::ExcludeCubebAggregate);
|
||||
let mut devices = test_get_all_devices(DeviceFilter::ExcludeCubebAggregateAndVPIO);
|
||||
devices.retain(|device| test_device_in_scope(*device, scope.clone()));
|
||||
devices
|
||||
}
|
||||
|
@ -323,7 +343,7 @@ pub fn get_devices_info_in_scope(scope: Scope) -> Vec<TestDeviceInfo> {
|
|||
infos.push(TestDeviceInfo::new(device, scope.clone()));
|
||||
print_info(infos.last().unwrap());
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
|
||||
infos
|
||||
}
|
||||
|
@ -605,7 +625,10 @@ pub fn test_set_default_device(
|
|||
&device as *const AudioObjectID as *const c_void,
|
||||
)
|
||||
};
|
||||
if status == NO_ERR {
|
||||
let new_default = test_get_default_device(scope.clone()).unwrap();
|
||||
if new_default == default {
|
||||
Err(-1)
|
||||
} else if status == NO_ERR {
|
||||
Ok(default)
|
||||
} else {
|
||||
Err(status)
|
||||
|
@ -628,8 +651,8 @@ impl TestDeviceSwitcher {
|
|||
.position(|device| *device == current)
|
||||
.unwrap();
|
||||
Self {
|
||||
scope: scope,
|
||||
devices: devices,
|
||||
scope,
|
||||
devices,
|
||||
current_device_index: index,
|
||||
}
|
||||
}
|
||||
|
@ -642,9 +665,19 @@ impl TestDeviceSwitcher {
|
|||
"Switch device for {:?}: {} -> {}",
|
||||
self.scope, current, next
|
||||
);
|
||||
let prev = self.set_device(next).unwrap();
|
||||
assert_eq!(prev, current);
|
||||
self.current_device_index = next_index;
|
||||
match self.set_device(next) {
|
||||
Ok(prev) => {
|
||||
assert_eq!(prev, current);
|
||||
self.current_device_index = next_index;
|
||||
}
|
||||
_ => {
|
||||
self.devices.remove(next_index);
|
||||
if next_index < self.current_device_index {
|
||||
self.current_device_index -= 1;
|
||||
}
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_device(&self, device: AudioObjectID) -> std::result::Result<AudioObjectID, OSStatus> {
|
||||
|
@ -888,8 +921,9 @@ impl TestDevicePlugger {
|
|||
);
|
||||
CFRelease(device_uid as *const c_void);
|
||||
|
||||
// This device is private to the process creating it.
|
||||
let private_value: i32 = 1;
|
||||
// Make this device NOT private to the process creating it.
|
||||
// On MacOS 14 devicechange events are not triggered when it is private.
|
||||
let private_value: i32 = 0;
|
||||
let device_private_key = CFNumberCreate(
|
||||
kCFAllocatorDefault,
|
||||
i64::from(kCFNumberIntType),
|
||||
|
@ -1003,9 +1037,7 @@ impl TestDevicePlugger {
|
|||
// AggregateDevice::get_sub_devices and audiounit_set_aggregate_sub_device_list.
|
||||
fn get_sub_devices(scope: Scope) -> Option<CFArrayRef> {
|
||||
let device = test_get_default_device(scope);
|
||||
if device.is_none() {
|
||||
return None;
|
||||
}
|
||||
device?;
|
||||
let device = device.unwrap();
|
||||
let uid = get_device_global_uid(device);
|
||||
if uid.is_err() {
|
||||
|
@ -1208,9 +1240,9 @@ pub fn test_get_stream_with_default_data_callback_by_type<F>(
|
|||
|
||||
bitflags! {
|
||||
pub struct StreamType: u8 {
|
||||
const INPUT = 0b01;
|
||||
const OUTPUT = 0b10;
|
||||
const DUPLEX = Self::INPUT.bits | Self::OUTPUT.bits;
|
||||
const INPUT = 0x01;
|
||||
const OUTPUT = 0x02;
|
||||
const DUPLEX = 0x03;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1252,32 +1284,9 @@ fn test_ops_stream_operation_with_default_data_callback<F>(
|
|||
output_device,
|
||||
output_stream_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(data_callback),
|
||||
Some(noop_data_callback),
|
||||
Some(state_callback),
|
||||
data,
|
||||
operation,
|
||||
);
|
||||
|
||||
extern "C" fn data_callback(
|
||||
stream: *mut ffi::cubeb_stream,
|
||||
_user_ptr: *mut c_void,
|
||||
_input_buffer: *const c_void,
|
||||
output_buffer: *mut c_void,
|
||||
nframes: i64,
|
||||
) -> i64 {
|
||||
assert!(!stream.is_null());
|
||||
|
||||
// Feed silence data to output buffer
|
||||
if !output_buffer.is_null() {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
let channels = stm.core_stream_data.output_stream_params.channels();
|
||||
let samples = nframes as usize * channels as usize;
|
||||
let sample_size = cubeb_sample_size(stm.core_stream_data.output_stream_params.format());
|
||||
unsafe {
|
||||
ptr::write_bytes(output_buffer, 0, samples * sample_size);
|
||||
}
|
||||
}
|
||||
|
||||
nframes
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "93b5c01a131f65c83c11aeb317f4583405c5eb79", optional = true }
|
||||
cubeb-coreaudio = { git = "https://github.com/mozilla/cubeb-coreaudio-rs", rev = "964e14628f285ad44522bdeeb566c1864ecd2fd8", optional = true }
|
||||
cubeb-pulse = { git = "https://github.com/mozilla/cubeb-pulse-rs", rev="cf48897be5cbe147d051ebbbe1eaf5fd8fb6bbc9", optional = true, features=["pulse-dlopen"] }
|
||||
cubeb-sys = { version = "0.10.3", optional = true, features=["gecko-in-tree"] }
|
||||
audioipc2-client = { git = "https://github.com/mozilla/audioipc", rev = "6be424d75f1367e70f2f5ddcacd6d0237e81a6a9", optional = true }
|
||||
|
|
Загрузка…
Ссылка в новой задаче