Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Unified Diff: media/audio/mac/aggregate_device_manager.cc

Issue 13403002: Add OSX aggregate audio device support for best performance. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698