The audio backend of Firefox on Mac OS X.
Перейти к файлу
Chun-Min Chang 7da41f97e5 Prevent any padding from being added in the beginning of the AudioUnitStream 2019-05-10 17:53:43 -07:00
coreaudio-sys-utils Add a struct PropertySelector to wrap AudioObjectPropertySelector 2019-05-01 13:52:37 -07:00
src Prevent any padding from being added in the beginning of the AudioUnitStream 2019-05-10 17:53:43 -07:00
.editorconfig Add editorconfig 2018-09-10 11:23:29 -07:00
.gitignore create a empty cargo library crate 2018-09-10 11:23:03 -07:00
.travis.yml Move tests for aggregate device to an independent module 2019-04-03 12:56:24 -07:00
Cargo.toml Replace coreaudio-sys by coreaudio-sys-utils 2019-03-28 16:06:33 -07:00
LICENSE Add Readme and License 2018-09-10 11:18:00 -07:00
README.md Status update 2019-04-29 14:10:36 -07:00
mutex-disposal.md fix typo 2019-05-10 17:53:43 -07:00
run_tests.sh Revise parallel tests 2019-05-02 16:18:03 -07:00

README.md

cubeb-coreaudio-rs

Build Status

Rust implementation of Cubeb on the MacOS platform.

Current Goals

  • Rewrite the C code into Rust on a line-by-line basis
  • Create some tests for later refactoring
  • Defuse the OwnedCriticalSection. See proposal here.

Status

All the lines in cubeb_audiounit.cpp are translated.

By applying the patch to integrate within Cubeb, it can pass all the tests under cubeb/test and it's able to switch devices when the stream is working (we are unable to test this automatically yet).

Now the draft version can pass all the tests within gecko on mozilla try-server. The project can be tracked on bugzilla 1530715.

The plain translation version from the C code is in plain-translation-from-c branch.

Test

Please run sh run_tests.sh.

Some tests cannot be run in parallel. They may operate the same device at the same time, or indirectly fire some system events that are listened by some tests.

The tests that may affect others are marked #[ignore]. They will be run by cargo test ... -- --ignored ... after finishing normal tests. Most of the tests are executed in run_tests.sh. Only those tests commented with FIXIT are left.

Manual Test

  • Output devices switching
    • $ cargo test test_switch_output_device -- --ignored --nocapture
    • Enter s to switch output devices
    • Enter q to finish test
  • Device change events listener
    • $ cargo test test_add_then_remove_listeners -- --ignored --nocapture
    • Plug/Unplug devices or switch input/output devices to see events log.
  • Device collection change
    • cargo test test_device_collection_change -- --ignored --nocapture
    • Plug/Unplug devices to see events log.

TODO

  • Maybe it's better to move all fn some_func(stm: &AudioUnitStream, ...) functions into impl AudioUnitStream to avoid useless references to AudioUnitStream.
  • Remove #[allow(non_camel_case_types)], #![allow(unused_assignments)], #![allow(unused_must_use)] and apply rust coding styles
  • Use Atomic{I64, U32, U64} instead of Atomic<{i64, u32, u64}>, once they are stable.
  • Tests
    • Rewrite some tests under cubeb/test/* in Rust as part of the integration tests
      • Add tests for capturing/recording, output, duplex streams
    • Tests cleaned up: Only tests under aggregate_device.rs left now.
  • Some of bugs are found when adding tests. Search FIXIT to find them.
  • cubeb-rs
    • Implement to_owned in StreamParamsRef
    • Check the passed parameters like what cubeb.c does!
      • Check the input StreamParams parameters properly, or we will set a invalid format into AudioUnit. In fact, we should check all the parameters properly so we can make sure we don't mess up the streams/devices settings!
  • Find a efficient way to catch memory leaks
    • Instrument on OSX

Issues

  • See discussion here
  • Mutex: Find a replacement for owned_critical_section
    • A dummy mutex like Mutex<()> should work (see test_dummy_mutex_multithread) as what owned_critical_section does in C version, but it doens't has equivalent API for assert_current_thread_owns.
    • We implement a OwnedCriticalSection around pthread_mutex_t like what we do in C version for now.
    • It's hard to debug with the variables using OwnedCriticalSection. Within a test with a variable using OwnedCriticalSection, if the OwnedCriticalSection used in the test isn't be dropped in a correct order, then the test will get a crash in OwnedCriticalSection. The examples are test_stream_drop_mutex_(in)correct. The tests must be created very carefully.
  • Atomic:
    • The stable atomic types only support bool, usize, isize, and ptr, but we need u64, i64, and f32.
    • Using atomic-rs instead.
    • Rust-Nightly supports AtomicU32 and AtomicU64 so we use that.
  • Unworkable API: dispatch_async and dispatch_sync
  • Borrowing Issues
    1. Pass AudioUnitContext across threads. In C version, we pass the pointer to cubeb context across threads, but it's forbidden in Rust. A workarounds are
      1. Cast the pointer to a usize value so the value can be copied to another thread.
      2. Or Unsafely implements Send and Sync traits so the compiler ignores the checks.
    2. We have a mutex in AudioUnitContext, and we have a reference to AudioUnitContext in AudioUnitStream. To sync what we do in C version, we need to lock the mutex in AudioUnitContext then pass a reference to AudioUnitContext to AudioUnitStream::new(...). To lock the mutex in AudioUnitContext, we call AutoLock::new(&mut AudioUnitContext.mutex). That is, we will borrow a reference to AudioUnitContext as a mutable first then borrow it again. It's forbidden in Rust. Some workarounds are
      1. Replace AutoLock by calling mutex.lock() and mutex.unlock() explicitly.
      2. Save the pointer to mutex first, then call AutoLock::new(unsafe { &mut (*mutex_ptr) }).
      3. Cast immutable reference to a *const then to a *mut: pthread_mutex_lock(&self.mutex as *const pthread_mutex_t as *mut pthread_mutex_t)

Test issues

  • Complexity of creating unit tests
    • We have lots of dependent APIs, so it's hard to test one API only, specially for those APIs using mutex(OwnedCriticalSection actually)
    • It's better to split them into several APIs so it's easier to test them
  • Fail to run test_create_blank_aggregate_device with test_add_device_listeners_dont_affect_other_scopes_with_* at the same time
    • I guess audiounit_create_blank_aggregate_device will fire the callbacks in test_add_device_listeners_dont_affect_other_scopes_with_*
  • Fail to run test_configure_{input, output}_with_zero_latency_frames and test_configure_{input, output} at the same time.
    • The APIs depending on audiounit_set_buffer_size cannot be called in parallel
      • kAudioDevicePropertyBufferFrameSize cannot be set when another stream using the same device with smaller buffer size is active. See here for reference.
      • The buffer frame size within same device may be overwritten (For those AudioUnits using same device ?)
  • Fail to run test_ops_context_register_device_collection_changed_twice_* on my MacBook Air and Travis CI.
    • A panic in capi_register_device_collection_changed causes EXC_BAD_INSTRUCTION.
    • Works fine if replacing register_device_collection_changed: Option<unsafe extern "C" fn(..,) -> c_int> to register_device_collection_changed: unsafe extern "C" fn(..,) -> c_int
    • Test them in AudioUnitContext directly instead of calling them via OPS for now.
  • TestDeviceSwitcher cannot work when there is an alive full-duplex stream
    • An aggregate device will be created for a duplex stream when its input and output devices are different.
    • TestDeviceSwitcher will cached the available devices, upon it's created, as the candidates for default device
    • Hence the created aggregate device may be cached in TestDeviceSwitcher
    • If the aggregate device is destroyed (when the destroying the duplex stream created it) but the TestDeviceSwitcher is still working, it will set a destroyed device as the default device
    • See details in device_change.rs