Add new `DeviceNameError` type

The coreaudio and wasapi backends may both potentially fail to produce
the name associated with a device. This changes the API to allow for
returning the errors in these cases.
This commit is contained in:
mitchmindtree 2019-06-21 00:53:11 +02:00
parent 78a7cb9e79
commit 105086a108
6 changed files with 54 additions and 19 deletions

View File

@ -7,6 +7,7 @@ use ChannelCount;
use BackendSpecificError; use BackendSpecificError;
use BuildStreamError; use BuildStreamError;
use DefaultFormatError; use DefaultFormatError;
use DeviceNameError;
use Format; use Format;
use SupportedFormatsError; use SupportedFormatsError;
use SampleFormat; use SampleFormat;
@ -74,8 +75,8 @@ pub struct Device(String);
impl Device { impl Device {
#[inline] #[inline]
pub fn name(&self) -> String { pub fn name(&self) -> Result<String, DeviceNameError> {
self.0.clone() Ok(self.0.clone())
} }
unsafe fn supported_formats( unsafe fn supported_formats(

View File

@ -5,6 +5,7 @@ use ChannelCount;
use BackendSpecificError; use BackendSpecificError;
use BuildStreamError; use BuildStreamError;
use DefaultFormatError; use DefaultFormatError;
use DeviceNameError;
use Format; use Format;
use SupportedFormatsError; use SupportedFormatsError;
use Sample; use Sample;
@ -76,7 +77,7 @@ pub struct Device {
} }
impl Device { impl Device {
pub fn name(&self) -> String { pub fn name(&self) -> Result<String, DeviceNameError> {
let property_address = AudioObjectPropertyAddress { let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceNameCFString, mSelector: kAudioDevicePropertyDeviceNameCFString,
mScope: kAudioDevicePropertyScopeOutput, mScope: kAudioDevicePropertyScopeOutput,
@ -93,16 +94,17 @@ impl Device {
&data_size as *const _ as *mut _, &data_size as *const _ as *mut _,
&device_name as *const _ as *mut _, &device_name as *const _ as *mut _,
); );
if status != kAudioHardwareNoError as i32 { check_os_status(status)?;
return format!("<OSStatus: {:?}>", status);
}
let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8); let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8);
if c_string == null() { if c_string == null() {
return "<null>".into(); let description = "core foundation unexpectedly returned null string".to_string();
let err = BackendSpecificError { description };
return Err(err.into());
} }
CStr::from_ptr(c_string as *mut _) CStr::from_ptr(c_string as *mut _)
}; };
c_str.to_string_lossy().into_owned() Ok(c_str.to_string_lossy().into_owned())
} }
// Logic re-used between `supported_input_formats` and `supported_output_formats`. // Logic re-used between `supported_input_formats` and `supported_output_formats`.

View File

@ -10,6 +10,7 @@ use stdweb::web::set_timeout;
use BuildStreamError; use BuildStreamError;
use DefaultFormatError; use DefaultFormatError;
use DeviceNameError;
use DevicesError; use DevicesError;
use Format; use Format;
use SupportedFormatsError; use SupportedFormatsError;
@ -229,8 +230,8 @@ pub struct Device;
impl Device { impl Device {
#[inline] #[inline]
pub fn name(&self) -> String { pub fn name(&self) -> Result<String, DeviceNameError> {
"Default Device".to_owned() Ok("Default Device".to_owned())
} }
#[inline] #[inline]

View File

@ -308,6 +308,17 @@ pub enum DevicesError {
} }
} }
/// An error that may occur while attempting to retrieve a device name.
#[derive(Debug, Fail)]
pub enum DeviceNameError {
/// See the `BackendSpecificError` docs for more information about this error variant.
#[fail(display = "{}", err)]
BackendSpecific {
#[fail(cause)]
err: BackendSpecificError,
}
}
/// Error that can happen when enumerating the list of supported formats. /// Error that can happen when enumerating the list of supported formats.
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
pub enum SupportedFormatsError { pub enum SupportedFormatsError {
@ -410,7 +421,7 @@ pub fn default_output_device() -> Option<Device> {
impl Device { impl Device {
/// The human-readable name of the device. /// The human-readable name of the device.
#[inline] #[inline]
pub fn name(&self) -> String { pub fn name(&self) -> Result<String, DeviceNameError> {
self.0.name() self.0.name()
} }
@ -742,6 +753,12 @@ impl From<BackendSpecificError> for DevicesError {
} }
} }
impl From<BackendSpecificError> for DeviceNameError {
fn from(err: BackendSpecificError) -> Self {
DeviceNameError::BackendSpecific { err }
}
}
impl From<BackendSpecificError> for SupportedFormatsError { impl From<BackendSpecificError> for SupportedFormatsError {
fn from(err: BackendSpecificError) -> Self { fn from(err: BackendSpecificError) -> Self {
SupportedFormatsError::BackendSpecific { err } SupportedFormatsError::BackendSpecific { err }

View File

@ -5,6 +5,7 @@ use std::marker::PhantomData;
use BuildStreamError; use BuildStreamError;
use DefaultFormatError; use DefaultFormatError;
use DevicesError; use DevicesError;
use DeviceNameError;
use Format; use Format;
use SupportedFormatsError; use SupportedFormatsError;
use StreamData; use StreamData;
@ -107,8 +108,8 @@ impl Device {
} }
#[inline] #[inline]
pub fn name(&self) -> String { pub fn name(&self) -> Result<String, DeviceNameError> {
"null".to_owned() Ok("null".to_owned())
} }
} }

View File

@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
use BackendSpecificError; use BackendSpecificError;
use DefaultFormatError; use DefaultFormatError;
use DeviceNameError;
use DevicesError; use DevicesError;
use Format; use Format;
use SupportedFormatsError; use SupportedFormatsError;
@ -297,7 +298,7 @@ unsafe impl Sync for Device {
} }
impl Device { impl Device {
pub fn name(&self) -> String { pub fn name(&self) -> Result<String, DeviceNameError> {
unsafe { unsafe {
// Open the device's property store. // Open the device's property store.
let mut property_store = ptr::null_mut(); let mut property_store = ptr::null_mut();
@ -305,15 +306,24 @@ impl Device {
// Get the endpoint's friendly-name property. // Get the endpoint's friendly-name property.
let mut property_value = mem::zeroed(); let mut property_value = mem::zeroed();
check_result( if let Err(err) = check_result(
(*property_store).GetValue( (*property_store).GetValue(
&devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _, &devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _,
&mut property_value &mut property_value
) )
).expect("failed to get friendly-name from property store"); ) {
let description = format!("failed to retrieve name from property store: {}", err);
let err = BackendSpecificError { description };
return Err(err.into());
}
// Read the friendly-name from the union data field, expecting a *const u16. // Read the friendly-name from the union data field, expecting a *const u16.
assert_eq!(property_value.vt, wtypes::VT_LPWSTR as _); if property_value.vt != wtypes::VT_LPWSTR as _ {
let description =
format!("property store produced invalid data: {:?}", property_value.vt);
let err = BackendSpecificError { description };
return Err(err.into());
}
let ptr_usize: usize = *(&property_value.data as *const _ as *const usize); let ptr_usize: usize = *(&property_value.data as *const _ as *const usize);
let ptr_utf16 = ptr_usize as *const u16; let ptr_utf16 = ptr_usize as *const u16;
@ -326,12 +336,15 @@ impl Device {
// Create the utf16 slice and covert it into a string. // Create the utf16 slice and covert it into a string.
let name_slice = slice::from_raw_parts(ptr_utf16, len as usize); let name_slice = slice::from_raw_parts(ptr_utf16, len as usize);
let name_os_string: OsString = OsStringExt::from_wide(name_slice); let name_os_string: OsString = OsStringExt::from_wide(name_slice);
let name_string = name_os_string.into_string().unwrap(); let name_string = match name_os_string.into_string() {
Ok(string) => string,
Err(os_string) => os_string.to_string_lossy().into(),
};
// Clean up the property. // Clean up the property.
PropVariantClear(&mut property_value); PropVariantClear(&mut property_value);
name_string Ok(name_string)
} }
} }