Index: media/audio/mac/audio_manager_mac.cc |
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc |
index 21b931809a5333e89595deae95be058455791d6f..bb2292beac712d31332148e4ea688b81012f9ecb 100644 |
--- a/media/audio/mac/audio_manager_mac.cc |
+++ b/media/audio/mac/audio_manager_mac.cc |
@@ -186,7 +186,7 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, |
UInt32 device_size = sizeof(audio_device_id); |
OSStatus result = -1; |
- if (device_id == AudioManagerBase::kDefaultDeviceId) { |
+ if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { |
// Default Device. |
property_address.mSelector = is_input ? |
kAudioHardwarePropertyDefaultInputDevice : |
@@ -260,7 +260,7 @@ bool AudioManagerMac::HasAudioInputDevices() { |
return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); |
} |
-// TODO(crogers): There are several places on the OSX specific code which |
+// TODO(xians): There are several places on the OSX specific code which |
// could benefit from these helper functions. |
bool AudioManagerMac::GetDefaultInputDevice( |
AudioDeviceID* device) { |
@@ -441,6 +441,64 @@ AudioParameters AudioManagerMac::GetInputStreamParameters( |
sample_rate, 16, buffer_size); |
} |
+std::string AudioManagerMac::GetAssociatedOutputDeviceID( |
+ const std::string& input_device_id) { |
+ AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); |
+ if (device == kAudioObjectUnknown) |
+ return std::string(); |
+ |
+ UInt32 size = 0; |
+ AudioObjectPropertyAddress pa = { |
+ kAudioDevicePropertyRelatedDevices, |
+ kAudioDevicePropertyScopeOutput, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); |
+ if (result || !size) |
+ return std::string(); |
+ |
+ int device_count = size / sizeof(AudioDeviceID); |
+ scoped_ptr_malloc<AudioDeviceID> |
+ devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); |
+ result = AudioObjectGetPropertyData( |
+ device, &pa, 0, NULL, &size, devices.get()); |
+ if (result) |
+ return std::string(); |
+ |
+ for (int i = 0; i < device_count; ++i) { |
+ // Get the number of output channels of the device. |
+ pa.mSelector = kAudioDevicePropertyStreams; |
+ size = 0; |
+ result = AudioObjectGetPropertyDataSize(devices.get()[i], |
+ &pa, |
+ 0, |
+ NULL, |
+ &size); |
+ if (result || !size) |
+ continue; // Skip if there aren't any output channels. |
+ |
+ // Get device UID. |
+ CFStringRef uid = NULL; |
+ size = sizeof(uid); |
+ pa.mSelector = kAudioDevicePropertyDeviceUID; |
+ result = AudioObjectGetPropertyData(devices.get()[i], |
+ &pa, |
+ 0, |
+ NULL, |
+ &size, |
+ &uid); |
+ if (result || !uid) |
+ continue; |
+ |
+ std::string ret(base::SysCFStringRefToUTF8(uid)); |
+ CFRelease(uid); |
+ return ret; |
+ } |
+ |
+ // No matching device found. |
+ return std::string(); |
+} |
+ |
AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( |
const AudioParameters& params) { |
return MakeLowLatencyOutputStream(params, std::string(), std::string()); |
@@ -450,15 +508,19 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( |
const AudioParameters& params, |
const std::string& device_id, |
const std::string& input_device_id) { |
- DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; |
// Handle basic output with no input channels. |
if (params.input_channels() == 0) { |
- AudioDeviceID device = kAudioObjectUnknown; |
- GetDefaultOutputDevice(&device); |
+ AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); |
+ if (device == kAudioObjectUnknown) { |
+ DLOG(ERROR) << "Failed to open output device: " << device_id; |
+ return NULL; |
+ } |
return new AUHALStream(this, params, device); |
} |
- // TODO(crogers): support more than stereo input. |
+ DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; |
+ |
+ // TODO(xians): support more than stereo input. |
if (params.input_channels() != 2) { |
// WebAudio is currently hard-coded to 2 channels so we should not |
// see this case. |
@@ -495,7 +557,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( |
// different and arbitrary combinations of input and output devices |
// even running at different sample-rates. |
// kAudioDeviceUnknown translates to "use default" here. |
- // TODO(crogers): consider tracking UMA stats on AUHALStream |
+ // TODO(xians): consider tracking UMA stats on AUHALStream |
// versus AudioSynchronizedStream. |
AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); |
if (audio_device_id == kAudioObjectUnknown) |
@@ -507,6 +569,33 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( |
kAudioDeviceUnknown); |
} |
+std::string AudioManagerMac::GetDefaultOutputDeviceID() { |
+ AudioDeviceID device_id = kAudioObjectUnknown; |
+ if (!GetDefaultOutputDevice(&device_id)) |
+ return std::string(); |
+ |
+ const AudioObjectPropertyAddress property_address = { |
+ kAudioDevicePropertyDeviceUID, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ CFStringRef device_uid = NULL; |
+ UInt32 size = sizeof(device_uid); |
+ OSStatus status = AudioObjectGetPropertyData(device_id, |
+ &property_address, |
+ 0, |
+ NULL, |
+ &size, |
+ &device_uid); |
+ if (status != kAudioHardwareNoError || !device_uid) |
+ return std::string(); |
+ |
+ std::string ret(base::SysCFStringRefToUTF8(device_uid)); |
+ CFRelease(device_uid); |
+ |
+ return ret; |
+} |
+ |
AudioInputStream* AudioManagerMac::MakeLinearInputStream( |
const AudioParameters& params, const std::string& device_id) { |
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); |
@@ -516,12 +605,24 @@ AudioInputStream* AudioManagerMac::MakeLinearInputStream( |
AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( |
const AudioParameters& params, const std::string& device_id) { |
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); |
- // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device |
+ // Gets the AudioDeviceID that refers to the AudioInputDevice with the device |
// unique id. This AudioDeviceID is used to set the device for Audio Unit. |
AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); |
AudioInputStream* stream = NULL; |
- if (audio_device_id != kAudioObjectUnknown) |
- stream = new AUAudioInputStream(this, params, audio_device_id); |
+ if (audio_device_id != kAudioObjectUnknown) { |
+ // AUAudioInputStream needs to be fed the preferred audio output parameters |
+ // of the matching device so that the buffer size of both input and output |
+ // can be matched. See constructor of AUAudioInputStream for more. |
+ const std::string associated_output_device( |
+ GetAssociatedOutputDeviceID(device_id)); |
+ const AudioParameters output_params = |
+ GetPreferredOutputStreamParameters( |
+ associated_output_device.empty() ? |
+ AudioManagerBase::kDefaultDeviceId : associated_output_device, |
+ params); |
+ stream = new AUAudioInputStream(this, params, output_params, |
+ audio_device_id); |
+ } |
return stream; |
} |
@@ -529,17 +630,22 @@ AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( |
AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( |
const std::string& output_device_id, |
const AudioParameters& input_params) { |
- // TODO(tommi): Support |output_device_id|. |
- DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; |
+ AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); |
+ if (device == kAudioObjectUnknown) { |
+ DLOG(ERROR) << "Invalid output device " << output_device_id; |
+ return AudioParameters(); |
+ } |
+ |
int hardware_channels = 2; |
- if (!GetDefaultOutputChannels(&hardware_channels)) { |
+ if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, |
+ &hardware_channels)) { |
// Fallback to stereo. |
hardware_channels = 2; |
} |
ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); |
- const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate(); |
+ const int hardware_sample_rate = HardwareSampleRateForDevice(device); |
const int buffer_size = ChooseBufferSize(hardware_sample_rate); |
int input_channels = 0; |
@@ -547,7 +653,7 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( |
input_channels = input_params.input_channels(); |
if (input_channels > 0) { |
- // TODO(crogers): given the limitations of the AudioOutputStream |
+ // TODO(xians): given the limitations of the AudioOutputStream |
// back-ends used with synchronized I/O, we hard-code to stereo. |
// Specifically, this is a limitation of AudioSynchronizedStream which |
// can be removed as part of the work to consolidate these back-ends. |