Index: media/audio/mac/aggregate_device_manager.cc |
=================================================================== |
--- media/audio/mac/aggregate_device_manager.cc (revision 0) |
+++ media/audio/mac/aggregate_device_manager.cc (revision 0) |
@@ -0,0 +1,346 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/audio/mac/aggregate_device_manager.h" |
+ |
+#include <CoreAudio/AudioHardware.h> |
+#include <string> |
+ |
+#include "base/mac/mac_logging.h" |
+#include "base/mac/scoped_cftyperef.h" |
+#include "media/audio/audio_parameters.h" |
+#include "media/audio/mac/audio_manager_mac.h" |
+ |
+using base::mac::ScopedCFTypeRef; |
+ |
+namespace media { |
+ |
+AggregateDeviceManager::AggregateDeviceManager() |
+ : plugin_id_(kAudioObjectUnknown), |
+ input_device_(kAudioDeviceUnknown), |
+ output_device_(kAudioDeviceUnknown), |
+ aggregate_device_(kAudioObjectUnknown) { |
+ AudioManagerMac::GetDefaultInputDevice(&input_device_); |
+ AudioManagerMac::GetDefaultOutputDevice(&output_device_); |
+} |
+ |
+AggregateDeviceManager::~AggregateDeviceManager() { |
+ DestroyAggregateDevice(); |
+} |
+ |
+AudioDeviceID AggregateDeviceManager::GetDefaultAggregateDevice() { |
+ // TODO(crogers): handle default device changes for synchronized I/O. |
+ // Right now, we just re-use the same previously created aggregate |
+ // device even if the user has switched the default devices in the |
+ // meantime. |
+ if (aggregate_device_ != kAudioObjectUnknown) |
+ return aggregate_device_; |
+ |
+ OSStatus result = CreateAggregateDevice( |
+ input_device_, |
+ output_device_, |
+ &aggregate_device_); |
+ if (result != noErr) |
+ DestroyAggregateDevice(); |
+ |
+ return aggregate_device_; |
+} |
+ |
+CFStringRef AggregateDeviceManager::GetDeviceUID(AudioDeviceID id) { |
+ static const AudioObjectPropertyAddress kDeviceUIDAddress = { |
+ kAudioDevicePropertyDeviceUID, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ // As stated in the CoreAudio header (AudioHardwareBase.h), |
+ // the caller is responsible for releasing the device_UID. |
+ CFStringRef device_UID; |
+ UInt32 size = sizeof(device_UID); |
+ OSStatus result = AudioObjectGetPropertyData( |
+ id, |
+ &kDeviceUIDAddress, |
+ 0, |
+ 0, |
+ &size, |
+ &device_UID); |
+ |
+ return (result == noErr) ? device_UID : NULL; |
+} |
+ |
+OSStatus AggregateDeviceManager::GetDeviceName( |
+ AudioDeviceID id, char* name, UInt32 size) { |
+ static const AudioObjectPropertyAddress kDeviceNameAddress = { |
+ kAudioDevicePropertyDeviceName, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ OSStatus result = AudioObjectGetPropertyData( |
+ id, |
+ &kDeviceNameAddress, |
+ 0, |
+ 0, |
+ &size, |
+ name); |
+ |
+ return result; |
+} |
+ |
+UInt32 AggregateDeviceManager::GetClockDomain(AudioDeviceID device_id) { |
+ static const AudioObjectPropertyAddress kClockDomainAddress = { |
+ kAudioDevicePropertyClockDomain, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ UInt32 clockdomain = 0; |
+ UInt32 size = sizeof(UInt32); |
+ OSStatus result = AudioObjectGetPropertyData( |
+ device_id, |
+ &kClockDomainAddress, |
+ 0, |
+ 0, |
+ &size, |
+ &clockdomain); |
+ if (result != noErr) |
no longer working on chromium
2013/04/03 21:11:57
nit, simply do
return (result == noErr) ? clockdom
Chris Rogers
2013/04/05 20:01:38
Done.
|
+ clockdomain = 0; |
+ return clockdomain; |
+} |
+ |
+bool AggregateDeviceManager::UseDefaultAggregateDevice() { |
+ UInt32 input_clockdomain = GetClockDomain(input_device_); |
+ UInt32 output_clockdomain = GetClockDomain(output_device_); |
+ VLOG(1) << "input_clockdomain: " << input_clockdomain; |
+ VLOG(1) << "output_clockdomain: " << output_clockdomain; |
+ |
+ return input_clockdomain > 0 && |
+ input_clockdomain == output_clockdomain; |
+} |
+ |
+OSStatus AggregateDeviceManager::GetPluginID(AudioObjectID* id) { |
+ DCHECK(id); |
+ |
+ // Get the audio hardware plugin. |
+ CFStringRef bundle_name = CFSTR("com.apple.audio.CoreAudio"); |
+ |
+ AudioValueTranslation plugin_translation; |
+ plugin_translation.mInputData = &bundle_name; |
+ plugin_translation.mInputDataSize = sizeof(bundle_name); |
+ plugin_translation.mOutputData = id; |
+ plugin_translation.mOutputDataSize = sizeof(*id); |
+ |
+ static const AudioObjectPropertyAddress kPlugInAddress = { |
+ kAudioHardwarePropertyPlugInForBundleID, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ UInt32 size = sizeof(plugin_translation); |
+ OSStatus result = AudioObjectGetPropertyData( |
+ kAudioObjectSystemObject, |
+ &kPlugInAddress, |
+ 0, |
+ 0, |
+ &size, |
+ &plugin_translation); |
+ |
+ VLOG(1) << "CoreAudio plugin ID: " << *id; |
+ |
+ return result; |
+} |
+ |
+CFMutableDictionaryRef |
+AggregateDeviceManager::CreateAggregateDeviceDictionary( |
+ AudioDeviceID input_id, |
+ AudioDeviceID output_id) { |
+ CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable( |
+ NULL, |
+ 0, |
+ &kCFTypeDictionaryKeyCallBacks, |
+ &kCFTypeDictionaryValueCallBacks); |
+ if (!aggregate_device_dict) |
+ return 0; |
no longer working on chromium
2013/04/03 21:11:57
nit, return NULL
Chris Rogers
2013/04/05 20:01:38
Done.
|
+ |
+ CFStringRef kAggregateDeviceName = |
+ CFSTR("ChromeAggregateAudioDevice"); |
+ CFStringRef kAggregateDeviceUID = |
+ CFSTR("com.google.chrome.AggregateAudioDevice"); |
+ |
+ // Add name and UID of the device to the dictionary. |
+ CFDictionaryAddValue( |
+ aggregate_device_dict, |
+ CFSTR(kAudioAggregateDeviceNameKey), |
+ kAggregateDeviceName); |
+ CFDictionaryAddValue( |
+ aggregate_device_dict, |
+ CFSTR(kAudioAggregateDeviceUIDKey), |
+ kAggregateDeviceUID); |
+ |
+ // Add a "private aggregate key" to the dictionary. |
+ // The 1 value means that the created aggregate device will |
+ // only be accessible from the process that created it, and |
+ // won't be visible to outside processes. |
+ int value = 1; |
+ CFNumberRef kAggregateDeviceNumber = CFNumberCreate( |
+ NULL, |
+ kCFNumberIntType, |
+ &value); |
+ CFDictionaryAddValue( |
+ aggregate_device_dict, |
+ CFSTR(kAudioAggregateDeviceIsPrivateKey), |
+ kAggregateDeviceNumber); |
+ |
+ return aggregate_device_dict; |
+} |
+ |
+CFMutableArrayRef |
+AggregateDeviceManager::CreateSubDeviceArray( |
+ CFStringRef input_device_UID, CFStringRef output_device_UID) { |
+ CFMutableArrayRef sub_devices_array = CFArrayCreateMutable( |
+ NULL, |
+ 0, |
+ &kCFTypeArrayCallBacks); |
+ |
+ CFArrayAppendValue(sub_devices_array, input_device_UID); |
+ CFArrayAppendValue(sub_devices_array, output_device_UID); |
+ |
+ return sub_devices_array; |
+} |
+ |
+OSStatus AggregateDeviceManager::CreateAggregateDevice( |
+ AudioDeviceID input_id, |
+ AudioDeviceID output_id, |
+ AudioDeviceID* aggregate_device) { |
+ DCHECK(aggregate_device); |
+ |
+ char input_device_name_[256]; |
no longer working on chromium
2013/04/03 21:11:57
nit, put this 256 into a static variable like kMax
Chris Rogers
2013/04/05 20:01:38
Done.
|
+ GetDeviceName( |
+ input_id, |
+ input_device_name_, |
+ sizeof(input_device_name_)); |
+ VLOG(1) << "Input device: \n" << input_device_name_; |
+ |
+ char output_device_name_[256]; |
+ GetDeviceName( |
+ output_id, |
+ output_device_name_, |
+ sizeof(output_device_name_)); |
+ VLOG(1) << "Output device: \n" << output_device_name_; |
+ |
+ OSStatus result = GetPluginID(&plugin_id_); |
+ if (result != noErr) |
+ return result; |
+ |
+ // Create a dictionary for the aggregate device. |
+ ScopedCFTypeRef<CFMutableDictionaryRef> aggregate_device_dict( |
+ CreateAggregateDeviceDictionary(input_id, output_id)); |
+ if (aggregate_device_dict == NULL) |
+ return -1; |
+ |
+ // Create the aggregate device. |
+ static const AudioObjectPropertyAddress kCreateAggregateDeviceAddress = { |
+ kAudioPlugInCreateAggregateDevice, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ UInt32 size = sizeof(*aggregate_device); |
+ result = AudioObjectGetPropertyData( |
+ plugin_id_, |
+ &kCreateAggregateDeviceAddress, |
+ sizeof(aggregate_device_dict), |
+ &aggregate_device_dict, |
+ &size, |
+ aggregate_device); |
+ if (result != noErr) { |
+ DLOG(ERROR) << "Error creating aggregate audio device!"; |
+ return result; |
+ } |
+ |
+ // Set the sub-devices for the aggregate device. |
+ // In this case we use two: the input and output devices. |
+ |
+ ScopedCFTypeRef<CFStringRef> input_device_UID(GetDeviceUID(input_id)); |
+ ScopedCFTypeRef<CFStringRef> output_device_UID(GetDeviceUID(output_id)); |
+ if (input_device_UID == NULL || output_device_UID == NULL) |
+ return -1; |
no longer working on chromium
2013/04/03 21:11:57
should we also log the error? like how is done els
Chris Rogers
2013/04/05 20:01:38
Done.
|
+ |
+ ScopedCFTypeRef<CFMutableArrayRef> sub_devices_array( |
+ CreateSubDeviceArray(input_device_UID, output_device_UID)); |
+ if (sub_devices_array == NULL) |
+ return -1; |
no longer working on chromium
2013/04/03 21:11:57
ditto
Chris Rogers
2013/04/05 20:01:38
Done.
|
+ |
+ static const AudioObjectPropertyAddress kSetSubDevicesAddress = { |
+ kAudioAggregateDevicePropertyFullSubDeviceList, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ size = sizeof(CFMutableArrayRef); |
+ result = AudioObjectSetPropertyData( |
+ *aggregate_device, |
+ &kSetSubDevicesAddress, |
+ 0, |
+ NULL, |
+ size, |
+ &sub_devices_array); |
+ if (result != noErr) { |
+ DLOG(ERROR) << "Error setting aggregate audio device sub-devices!"; |
+ return result; |
+ } |
+ |
+ // Use the input device as the master device. |
+ static const AudioObjectPropertyAddress kSetMasterDeviceAddress = { |
+ kAudioAggregateDevicePropertyMasterSubDevice, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ size = sizeof(CFStringRef); |
+ result = AudioObjectSetPropertyData( |
+ *aggregate_device, |
+ &kSetMasterDeviceAddress, |
+ 0, |
+ NULL, |
+ size, |
+ &input_device_UID); |
+ if (result != noErr) { |
+ DLOG(ERROR) << "Error setting aggregate audio device master device!"; |
+ return result; |
+ } |
+ |
+ VLOG(1) << "New aggregate device: " << *aggregate_device; |
+ return noErr; |
+} |
+ |
+OSStatus AggregateDeviceManager::DestroyAggregateDevice() { |
no longer working on chromium
2013/04/03 21:11:57
it looks like this return value is never used, mak
Chris Rogers
2013/04/05 20:01:38
Done.
|
+ if (aggregate_device_ == kAudioObjectUnknown) |
+ return noErr; |
+ |
+ static const AudioObjectPropertyAddress kDestroyAddress = { |
+ kAudioPlugInDestroyAggregateDevice, |
+ kAudioObjectPropertyScopeGlobal, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ UInt32 size = sizeof(aggregate_device_); |
+ OSStatus result = AudioObjectGetPropertyData( |
+ plugin_id_, |
+ &kDestroyAddress, |
+ 0, |
+ NULL, |
+ &size, |
+ &aggregate_device_); |
+ if (result != noErr) { |
+ DLOG(ERROR) << "Error destroying aggregate audio device!"; |
+ return result; |
+ } |
+ |
+ aggregate_device_ = kAudioObjectUnknown; |
+ |
+ return noErr; |
+} |
+ |
+} // namespace media |