зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1530715 - P21: Replace set_buffer_size by set_buffer_size_sync and its friends. r=padenot
By moving out this API from the AudioUnitStream, we can avoid calling it by borrowing the AudioUnitStream variable as a mutable. This will help to avoid the potential borrowing-twice issues in the later mutex replacement In addition, the change applies an idiomatic way to wait for the system callback event without consuming CPU time. Differential Revision: https://phabricator.services.mozilla.com/D34054 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
9b1165df4e
Коммит
b7dc2b7dae
|
@ -3,4 +3,4 @@ git repository using the update.sh script.
|
|||
|
||||
The cubeb-coreaudio-rs git repository is: https://github.com/ChunMinChang/cubeb-coreaudio-rs
|
||||
|
||||
The git commit ID used was 275ec997750ddec5d35f891bed9d932fb6e12c9d (2019-06-21 14:40:50 -0700)
|
||||
The git commit ID used was 3c14e6a17f8b931950941326eecc8a1132f13cae (2019-06-21 15:51:40 -0700)
|
||||
|
|
|
@ -68,7 +68,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum io_side {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
|
@ -1707,61 +1707,161 @@ fn create_audiounit_by_description(desc: AudioComponentDescription) -> Result<Au
|
|||
}
|
||||
}
|
||||
|
||||
// Change buffer size is prone to deadlock thus we change it following the steps:
|
||||
// - register a listener for the buffer size property
|
||||
// - change the property
|
||||
// - wait until the listener is executed
|
||||
// - property has changed, remove the listener
|
||||
extern "C" fn buffer_size_changed_callback(
|
||||
in_client_data: *mut c_void,
|
||||
in_unit: AudioUnit,
|
||||
in_property_id: AudioUnitPropertyID,
|
||||
in_scope: AudioUnitScope,
|
||||
in_element: AudioUnitElement,
|
||||
) {
|
||||
use self::coreaudio_sys_utils::sys;
|
||||
|
||||
let stm = unsafe { &mut *(in_client_data as *mut AudioUnitStream) };
|
||||
|
||||
let au = in_unit;
|
||||
let au_element = in_element;
|
||||
|
||||
let (au_scope, au_type) = if AU_IN_BUS == in_element {
|
||||
(kAudioUnitScope_Output, "input")
|
||||
} else {
|
||||
(kAudioUnitScope_Input, "output")
|
||||
fn get_buffer_size(unit: AudioUnit, side: io_side) -> std::result::Result<u32, OSStatus> {
|
||||
assert!(!unit.is_null());
|
||||
let (scope, element) = match side {
|
||||
io_side::INPUT => (kAudioUnitScope_Output, AU_IN_BUS),
|
||||
io_side::OUTPUT => (kAudioUnitScope_Input, AU_OUT_BUS),
|
||||
};
|
||||
|
||||
match in_property_id {
|
||||
// Using coreaudio_sys as prefix so kAudioDevicePropertyBufferFrameSize
|
||||
// won't be treated as a new variable introduced in the match arm.
|
||||
sys::kAudioDevicePropertyBufferFrameSize => {
|
||||
if in_scope != au_scope {
|
||||
// filter out the callback for global scope
|
||||
return;
|
||||
}
|
||||
let mut new_buffer_size: u32 = 0;
|
||||
let mut out_size = mem::size_of::<u32>();
|
||||
let r = audio_unit_get_property(
|
||||
au,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
au_scope,
|
||||
au_element,
|
||||
&mut new_buffer_size,
|
||||
&mut out_size,
|
||||
);
|
||||
if r != NO_ERR {
|
||||
cubeb_log!("({:p}) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current buffer size", stm as *const AudioUnitStream);
|
||||
} else {
|
||||
cubeb_log!("({:p}) Event: kAudioDevicePropertyBufferFrameSize: New {} buffer size = {} for scope {}", stm as *const AudioUnitStream,
|
||||
au_type, new_buffer_size, in_scope);
|
||||
}
|
||||
*stm.buffer_size_change_state.get_mut() = true;
|
||||
}
|
||||
_ => {}
|
||||
let mut frames: u32 = 0;
|
||||
let mut size = mem::size_of::<u32>();
|
||||
let status = audio_unit_get_property(
|
||||
unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
scope,
|
||||
element,
|
||||
&mut frames,
|
||||
&mut size,
|
||||
);
|
||||
if status == NO_ERR {
|
||||
assert_ne!(frames, 0);
|
||||
Ok(frames)
|
||||
} else {
|
||||
Err(status)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_buffer_size(
|
||||
unit: AudioUnit,
|
||||
side: io_side,
|
||||
frames: u32,
|
||||
) -> std::result::Result<(), OSStatus> {
|
||||
assert!(!unit.is_null());
|
||||
let (scope, element) = match side {
|
||||
io_side::INPUT => (kAudioUnitScope_Output, AU_IN_BUS),
|
||||
io_side::OUTPUT => (kAudioUnitScope_Input, AU_OUT_BUS),
|
||||
};
|
||||
let status = audio_unit_set_property(
|
||||
unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
scope,
|
||||
element,
|
||||
&frames,
|
||||
mem::size_of_val(&frames),
|
||||
);
|
||||
if status == NO_ERR {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(status)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_buffer_size_sync(unit: AudioUnit, side: io_side, frames: u32) -> Result<()> {
|
||||
let current_frames = get_buffer_size(unit, side.clone()).map_err(|r| {
|
||||
cubeb_log!(
|
||||
"AudioUnitGetProperty/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
Error::error()
|
||||
})?;
|
||||
if frames == current_frames {
|
||||
cubeb_log!(
|
||||
"The size of {} buffer frames is already {}",
|
||||
side.to_string(),
|
||||
frames
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let waiting_time = Duration::from_millis(100);
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let mut pair2 = pair.clone();
|
||||
let pair_ptr = &mut pair2;
|
||||
|
||||
assert_eq!(
|
||||
audio_unit_add_property_listener(
|
||||
unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
buffer_size_changed_callback,
|
||||
pair_ptr,
|
||||
),
|
||||
NO_ERR
|
||||
);
|
||||
|
||||
let _teardown = finally(|| {
|
||||
assert_eq!(
|
||||
audio_unit_remove_property_listener_with_user_data(
|
||||
unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
buffer_size_changed_callback,
|
||||
pair_ptr,
|
||||
),
|
||||
NO_ERR
|
||||
);
|
||||
});
|
||||
|
||||
set_buffer_size(unit, side.clone(), frames).map_err(|r| {
|
||||
cubeb_log!(
|
||||
"AudioUnitSetProperty/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
Error::error()
|
||||
})?;
|
||||
|
||||
let &(ref lock, ref cvar) = &*pair;
|
||||
let changed = lock.lock().unwrap();
|
||||
if !*changed {
|
||||
let (chg, timeout_res) = cvar.wait_timeout(changed, waiting_time).unwrap();
|
||||
if timeout_res.timed_out() {
|
||||
cubeb_log!(
|
||||
"Time out for waiting the {} buffer size setting!",
|
||||
side.to_string()
|
||||
);
|
||||
}
|
||||
if !*chg {
|
||||
return Err(Error::error());
|
||||
}
|
||||
}
|
||||
|
||||
let new_frames = get_buffer_size(unit, side.clone()).map_err(|r| {
|
||||
cubeb_log!(
|
||||
"Cannot get new {} buffer size. Error: {}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
Error::error()
|
||||
})?;
|
||||
cubeb_log!(
|
||||
"The new size of {} buffer frames is {}",
|
||||
side.to_string(),
|
||||
new_frames
|
||||
);
|
||||
|
||||
extern "C" fn buffer_size_changed_callback(
|
||||
in_client_data: *mut c_void,
|
||||
_in_unit: AudioUnit,
|
||||
in_property_id: AudioUnitPropertyID,
|
||||
in_scope: AudioUnitScope,
|
||||
in_element: AudioUnitElement,
|
||||
) {
|
||||
if in_scope == 0 {
|
||||
// filter out the callback for global scope.
|
||||
return;
|
||||
}
|
||||
assert!(in_element == AU_IN_BUS || in_element == AU_OUT_BUS);
|
||||
assert_eq!(in_property_id, kAudioDevicePropertyBufferFrameSize);
|
||||
let pair = unsafe { &mut *(in_client_data as *mut Arc<(Mutex<bool>, Condvar)>) };
|
||||
let &(ref lock, ref cvar) = &**pair;
|
||||
let mut changed = lock.lock().unwrap();
|
||||
*changed = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_uint32_into_string(data: u32) -> CString {
|
||||
let empty = CString::default();
|
||||
if data == 0 {
|
||||
|
@ -2862,7 +2962,6 @@ struct AudioUnitStream<'ctx> {
|
|||
resampler: AutoRelease<ffi::cubeb_resampler>,
|
||||
// This is true if a device change callback is currently running.
|
||||
switching_device: AtomicBool,
|
||||
buffer_size_change_state: AtomicBool,
|
||||
aggregate_device_id: AudioDeviceID, // the aggregate device id
|
||||
plugin_id: AudioObjectID, // used to create aggregate device
|
||||
// Mixer interface
|
||||
|
@ -2931,7 +3030,6 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
panning: atomic::Atomic::new(0.0_f32),
|
||||
resampler: AutoRelease::new(ptr::null_mut(), ffi::cubeb_resampler_destroy),
|
||||
switching_device: AtomicBool::new(false),
|
||||
buffer_size_change_state: AtomicBool::new(false),
|
||||
aggregate_device_id: kAudioObjectUnknown,
|
||||
plugin_id: kAudioObjectUnknown,
|
||||
mixer: AutoRelease::new(ptr::null_mut(), ffi::cubeb_mixer_destroy),
|
||||
|
@ -3601,142 +3699,6 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_buffer_size(&mut self, new_size_frames: u32, side: io_side) -> Result<()> {
|
||||
use std::thread;
|
||||
|
||||
assert_ne!(new_size_frames, 0);
|
||||
let (au, au_scope, au_element) = if side == io_side::INPUT {
|
||||
(self.input_unit, kAudioUnitScope_Output, AU_IN_BUS)
|
||||
} else {
|
||||
(self.output_unit, kAudioUnitScope_Input, AU_OUT_BUS)
|
||||
};
|
||||
assert!(!au.is_null());
|
||||
|
||||
let mut buffer_frames: u32 = 0;
|
||||
let mut size = mem::size_of_val(&buffer_frames);
|
||||
let mut r = audio_unit_get_property(
|
||||
au,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
au_scope,
|
||||
au_element,
|
||||
&mut buffer_frames,
|
||||
&mut size,
|
||||
);
|
||||
if r != NO_ERR {
|
||||
cubeb_log!(
|
||||
"AudioUnitGetProperty/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
assert_ne!(buffer_frames, 0);
|
||||
|
||||
if new_size_frames == buffer_frames {
|
||||
cubeb_log!(
|
||||
"({:p}) No need to update {} buffer size already {} frames",
|
||||
self as *const AudioUnitStream,
|
||||
side.to_string(),
|
||||
buffer_frames
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
r = audio_unit_add_property_listener(
|
||||
au,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
buffer_size_changed_callback,
|
||||
self as *mut AudioUnitStream as *mut c_void,
|
||||
);
|
||||
if r != NO_ERR {
|
||||
cubeb_log!(
|
||||
"AudioUnitAddPropertyListener/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
*self.buffer_size_change_state.get_mut() = false;
|
||||
|
||||
r = audio_unit_set_property(
|
||||
au,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
au_scope,
|
||||
au_element,
|
||||
&new_size_frames,
|
||||
mem::size_of_val(&new_size_frames),
|
||||
);
|
||||
if r != NO_ERR {
|
||||
cubeb_log!(
|
||||
"AudioUnitSetProperty/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
|
||||
r = audio_unit_remove_property_listener_with_user_data(
|
||||
au,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
buffer_size_changed_callback,
|
||||
self as *mut AudioUnitStream as *mut c_void,
|
||||
);
|
||||
if r != NO_ERR {
|
||||
cubeb_log!(
|
||||
"AudioUnitAddPropertyListener/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
}
|
||||
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
let mut count: u32 = 0;
|
||||
let duration = Duration::from_millis(100); // 0.1 sec
|
||||
|
||||
while !self.buffer_size_change_state.load(Ordering::SeqCst) && count < 30 {
|
||||
count += 1;
|
||||
thread::sleep(duration);
|
||||
cubeb_log!(
|
||||
"({:p}) audiounit_set_buffer_size : wait count = {}",
|
||||
self as *const AudioUnitStream,
|
||||
count
|
||||
);
|
||||
}
|
||||
|
||||
r = audio_unit_remove_property_listener_with_user_data(
|
||||
au,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
buffer_size_changed_callback,
|
||||
self as *mut AudioUnitStream as *mut c_void,
|
||||
);
|
||||
if r != NO_ERR {
|
||||
cubeb_log!(
|
||||
"AudioUnitAddPropertyListener/{}/kAudioDevicePropertyBufferFrameSize rv={}",
|
||||
side.to_string(),
|
||||
r
|
||||
);
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
if !self.buffer_size_change_state.load(Ordering::SeqCst) && count >= 30 {
|
||||
cubeb_log!(
|
||||
"({:p}) Error, did not get buffer size change callback ...",
|
||||
self as *const AudioUnitStream
|
||||
);
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
cubeb_log!(
|
||||
"({:p}) {} buffer size changed to {} frames.",
|
||||
self as *const AudioUnitStream,
|
||||
side.to_string(),
|
||||
new_size_frames
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_input(&mut self) -> Result<()> {
|
||||
assert!(!self.input_unit.is_null());
|
||||
|
||||
|
@ -3792,7 +3754,7 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
// Use latency to set buffer size
|
||||
assert_ne!(self.latency_frames, 0);
|
||||
let latency_frames = self.latency_frames;
|
||||
if let Err(r) = self.set_buffer_size(latency_frames, io_side::INPUT) {
|
||||
if let Err(r) = set_buffer_size_sync(self.input_unit, io_side::INPUT, latency_frames) {
|
||||
cubeb_log!(
|
||||
"({:p}) Error in change input buffer size.",
|
||||
self as *const AudioUnitStream
|
||||
|
@ -3975,7 +3937,7 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
// Use latency to set buffer size
|
||||
assert_ne!(self.latency_frames, 0);
|
||||
let latency_frames = self.latency_frames;
|
||||
if let Err(r) = self.set_buffer_size(latency_frames, io_side::OUTPUT) {
|
||||
if let Err(r) = set_buffer_size_sync(self.output_unit, io_side::OUTPUT, latency_frames) {
|
||||
cubeb_log!(
|
||||
"({:p}) Error in change output buffer size.",
|
||||
self as *const AudioUnitStream
|
||||
|
|
Загрузка…
Ссылка в новой задаче