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

View File

@ -5,6 +5,7 @@ use ChannelCount;
use BackendSpecificError;
use BuildStreamError;
use DefaultFormatError;
use DeviceNameError;
use Format;
use SupportedFormatsError;
use Sample;
@ -76,7 +77,7 @@ pub struct Device {
}
impl Device {
pub fn name(&self) -> String {
pub fn name(&self) -> Result<String, DeviceNameError> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceNameCFString,
mScope: kAudioDevicePropertyScopeOutput,
@ -93,16 +94,17 @@ impl Device {
&data_size as *const _ as *mut _,
&device_name as *const _ as *mut _,
);
if status != kAudioHardwareNoError as i32 {
return format!("<OSStatus: {:?}>", status);
}
check_os_status(status)?;
let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8);
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 _)
};
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`.

View File

@ -10,6 +10,7 @@ use stdweb::web::set_timeout;
use BuildStreamError;
use DefaultFormatError;
use DeviceNameError;
use DevicesError;
use Format;
use SupportedFormatsError;
@ -229,8 +230,8 @@ pub struct Device;
impl Device {
#[inline]
pub fn name(&self) -> String {
"Default Device".to_owned()
pub fn name(&self) -> Result<String, DeviceNameError> {
Ok("Default Device".to_owned())
}
#[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.
#[derive(Debug, Fail)]
pub enum SupportedFormatsError {
@ -410,7 +421,7 @@ pub fn default_output_device() -> Option<Device> {
impl Device {
/// The human-readable name of the device.
#[inline]
pub fn name(&self) -> String {
pub fn name(&self) -> Result<String, DeviceNameError> {
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 {
fn from(err: BackendSpecificError) -> Self {
SupportedFormatsError::BackendSpecific { err }

View File

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

View File

@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
use BackendSpecificError;
use DefaultFormatError;
use DeviceNameError;
use DevicesError;
use Format;
use SupportedFormatsError;
@ -297,7 +298,7 @@ unsafe impl Sync for Device {
}
impl Device {
pub fn name(&self) -> String {
pub fn name(&self) -> Result<String, DeviceNameError> {
unsafe {
// Open the device's property store.
let mut property_store = ptr::null_mut();
@ -305,15 +306,24 @@ impl Device {
// Get the endpoint's friendly-name property.
let mut property_value = mem::zeroed();
check_result(
if let Err(err) = check_result(
(*property_store).GetValue(
&devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _,
&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.
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_utf16 = ptr_usize as *const u16;
@ -326,12 +336,15 @@ impl Device {
// Create the utf16 slice and covert it into a string.
let name_slice = slice::from_raw_parts(ptr_utf16, len as usize);
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.
PropVariantClear(&mut property_value);
name_string
Ok(name_string)
}
}