Don't account for channels on Tap input streams
With VPIO, output devices may get a Tap stream, basically adding an input AudioStream to the output device. To avoid enumerating output-only devices as input devices, we ignore these channels.
This commit is contained in:
Родитель
8d83a4385f
Коммит
dc4dac9ce6
|
@ -10,5 +10,5 @@ core-foundation-sys = { version = "0.8" }
|
|||
|
||||
[dependencies.coreaudio-sys]
|
||||
default-features = false
|
||||
features = ["audio_unit", "core_audio"]
|
||||
version = "0.2"
|
||||
features = ["audio_unit", "core_audio", "io_kit_audio"]
|
||||
version = "0.2.14"
|
||||
|
|
|
@ -240,6 +240,23 @@ pub fn get_stream_latency(id: AudioStreamID) -> std::result::Result<u32, OSStatu
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_stream_terminal_type(id: AudioStreamID) -> std::result::Result<u32, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
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> {
|
||||
|
@ -293,6 +310,7 @@ pub enum Property {
|
|||
HardwareDevices,
|
||||
ModelUID,
|
||||
StreamLatency,
|
||||
StreamTerminalType,
|
||||
StreamVirtualFormat,
|
||||
TransportType,
|
||||
ClockDomain,
|
||||
|
@ -317,6 +335,7 @@ 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,
|
||||
|
|
|
@ -1353,7 +1353,43 @@ fn get_channel_count(
|
|||
) -> std::result::Result<u32, OSStatus> {
|
||||
assert_ne!(devid, kAudioObjectUnknown);
|
||||
|
||||
let streams = get_device_streams(devid, devtype)?;
|
||||
let mut streams = get_device_streams(devid, devtype)?;
|
||||
|
||||
if devtype == DeviceType::INPUT {
|
||||
// With VPIO, output devices will/may get a Tap that appears as input channels on the
|
||||
// output device id. One could check for whether the output device has a tap enabled,
|
||||
// but it is impossible to distinguish an output-only device from an input+output
|
||||
// device. There have also been corner cases observed, where the device does NOT have
|
||||
// a Tap enabled, but it still has the extra input channels from the Tap.
|
||||
// We can check the terminal type of the input stream instead -- if it is
|
||||
// INPUT_UNDEFINED (reported to be the VPIO type according to Chromium) or any
|
||||
// non-input-only terminal type, we ignore it when counting channels (and thereby
|
||||
// determining whether we are dealing with an input device).
|
||||
streams.retain(|stream| {
|
||||
let terminal_type = get_stream_terminal_type(*stream);
|
||||
if terminal_type.is_err() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
match terminal_type.unwrap() {
|
||||
kAudioStreamTerminalTypeMicrophone
|
||||
| kAudioStreamTerminalTypeHeadsetMicrophone
|
||||
| kAudioStreamTerminalTypeReceiverMicrophone => true,
|
||||
t if t > INPUT_UNDEFINED && t < OUTPUT_UNDEFINED => true,
|
||||
t if t > BIDIRECTIONAL_UNDEFINED && t < TELEPHONY_UNDEFINED => true,
|
||||
t if t > TELEPHONY_UNDEFINED && t < EXTERNAL_UNDEFINED => true,
|
||||
t => {
|
||||
cubeb_log!(
|
||||
"Unexpected TerminalType {:06X} for input stream. Ignoring its channels.",
|
||||
t
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
for stream in streams {
|
||||
if let Ok(format) = get_stream_virtual_format(stream) {
|
||||
|
|
|
@ -417,3 +417,57 @@ fn test_get_stream_virtual_format() {
|
|||
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());
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче