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:
parent
78a7cb9e79
commit
105086a108
|
@ -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(
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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]
|
||||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -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 }
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue