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

Side by Side 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, 8 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/mac/aggregate_device_manager.h"
6
7 #include <CoreAudio/AudioHardware.h>
8 #include <string>
9
10 #include "base/mac/mac_logging.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "media/audio/audio_parameters.h"
13 #include "media/audio/mac/audio_manager_mac.h"
14
15 using base::mac::ScopedCFTypeRef;
16
17 namespace media {
18
19 AggregateDeviceManager::AggregateDeviceManager()
20 : plugin_id_(kAudioObjectUnknown),
21 input_device_(kAudioDeviceUnknown),
22 output_device_(kAudioDeviceUnknown),
23 aggregate_device_(kAudioObjectUnknown) {
24 }
25
26 AggregateDeviceManager::~AggregateDeviceManager() {
27 DestroyAggregateDevice();
28 }
29
30 AudioDeviceID AggregateDeviceManager::GetDefaultAggregateDevice() {
31 // Use a lazily created aggregate device if it's already available
32 // and still appropriate.
33 if (aggregate_device_ != kAudioObjectUnknown) {
34 // TODO(crogers): handle default device changes for synchronized I/O.
35 // For now, we check to make sure the default devices haven't changed
36 // since we lazily created the aggregate device.
37 AudioDeviceID current_input_device;
38 AudioDeviceID current_output_device;
39 AudioManagerMac::GetDefaultInputDevice(&current_input_device);
40 AudioManagerMac::GetDefaultOutputDevice(&current_output_device);
41
42 if (current_input_device == input_device_ &&
43 current_output_device == output_device_)
44 return aggregate_device_;
45
46 // For now, once lazily created don't attempt to create another
47 // aggregate device.
48 return kAudioDeviceUnknown;
49 }
50
51 AudioManagerMac::GetDefaultInputDevice(&input_device_);
52 AudioManagerMac::GetDefaultOutputDevice(&output_device_);
53
54 // Only create an aggregrate device if the clock domains match.
55 UInt32 input_clockdomain = GetClockDomain(input_device_);
56 UInt32 output_clockdomain = GetClockDomain(output_device_);
57 DVLOG(1) << "input_clockdomain: " << input_clockdomain;
58 DVLOG(1) << "output_clockdomain: " << output_clockdomain;
59
60 if (input_clockdomain == 0 || input_clockdomain != output_clockdomain)
61 return kAudioDeviceUnknown;
62
63 OSStatus result = CreateAggregateDevice(
64 input_device_,
65 output_device_,
66 &aggregate_device_);
67 if (result != noErr)
68 DestroyAggregateDevice();
69
70 return aggregate_device_;
71 }
72
73 CFStringRef AggregateDeviceManager::GetDeviceUID(AudioDeviceID id) {
74 static const AudioObjectPropertyAddress kDeviceUIDAddress = {
75 kAudioDevicePropertyDeviceUID,
76 kAudioObjectPropertyScopeGlobal,
77 kAudioObjectPropertyElementMaster
78 };
79
80 // As stated in the CoreAudio header (AudioHardwareBase.h),
81 // the caller is responsible for releasing the device_UID.
82 CFStringRef device_UID;
83 UInt32 size = sizeof(device_UID);
84 OSStatus result = AudioObjectGetPropertyData(
85 id,
86 &kDeviceUIDAddress,
87 0,
88 0,
89 &size,
90 &device_UID);
91
92 return (result == noErr) ? device_UID : NULL;
93 }
94
95 void AggregateDeviceManager::GetDeviceName(
96 AudioDeviceID id, char* name, UInt32 size) {
97 static const AudioObjectPropertyAddress kDeviceNameAddress = {
98 kAudioDevicePropertyDeviceName,
99 kAudioObjectPropertyScopeGlobal,
100 kAudioObjectPropertyElementMaster
101 };
102
103 OSStatus result = AudioObjectGetPropertyData(
104 id,
105 &kDeviceNameAddress,
106 0,
107 0,
108 &size,
109 name);
110
111 if (result != noErr && size > 0)
112 name[0] = 0;
113 }
114
115 UInt32 AggregateDeviceManager::GetClockDomain(AudioDeviceID device_id) {
116 static const AudioObjectPropertyAddress kClockDomainAddress = {
117 kAudioDevicePropertyClockDomain,
118 kAudioObjectPropertyScopeGlobal,
119 kAudioObjectPropertyElementMaster
120 };
121
122 UInt32 clockdomain = 0;
123 UInt32 size = sizeof(UInt32);
124 OSStatus result = AudioObjectGetPropertyData(
125 device_id,
126 &kClockDomainAddress,
127 0,
128 0,
129 &size,
130 &clockdomain);
131
132 return (result == noErr) ? clockdomain : 0;
133 }
134
135 OSStatus AggregateDeviceManager::GetPluginID(AudioObjectID* id) {
136 DCHECK(id);
137
138 // Get the audio hardware plugin.
139 CFStringRef bundle_name = CFSTR("com.apple.audio.CoreAudio");
140
141 AudioValueTranslation plugin_translation;
142 plugin_translation.mInputData = &bundle_name;
143 plugin_translation.mInputDataSize = sizeof(bundle_name);
144 plugin_translation.mOutputData = id;
145 plugin_translation.mOutputDataSize = sizeof(*id);
146
147 static const AudioObjectPropertyAddress kPlugInAddress = {
148 kAudioHardwarePropertyPlugInForBundleID,
149 kAudioObjectPropertyScopeGlobal,
150 kAudioObjectPropertyElementMaster
151 };
152
153 UInt32 size = sizeof(plugin_translation);
154 OSStatus result = AudioObjectGetPropertyData(
155 kAudioObjectSystemObject,
156 &kPlugInAddress,
157 0,
158 0,
159 &size,
160 &plugin_translation);
161
162 DVLOG(1) << "CoreAudio plugin ID: " << *id;
163
164 return result;
165 }
166
167 CFMutableDictionaryRef
168 AggregateDeviceManager::CreateAggregateDeviceDictionary(
169 AudioDeviceID input_id,
170 AudioDeviceID output_id) {
171 CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(
172 NULL,
173 0,
174 &kCFTypeDictionaryKeyCallBacks,
175 &kCFTypeDictionaryValueCallBacks);
176 if (!aggregate_device_dict)
177 return NULL;
178
179 const CFStringRef kAggregateDeviceName =
180 CFSTR("ChromeAggregateAudioDevice");
181 const CFStringRef kAggregateDeviceUID =
182 CFSTR("com.google.chrome.AggregateAudioDevice");
183
184 // Add name and UID of the device to the dictionary.
185 CFDictionaryAddValue(
186 aggregate_device_dict,
187 CFSTR(kAudioAggregateDeviceNameKey),
188 kAggregateDeviceName);
189 CFDictionaryAddValue(
190 aggregate_device_dict,
191 CFSTR(kAudioAggregateDeviceUIDKey),
192 kAggregateDeviceUID);
193
194 // Add a "private aggregate key" to the dictionary.
195 // The 1 value means that the created aggregate device will
196 // only be accessible from the process that created it, and
197 // won't be visible to outside processes.
198 int value = 1;
199 ScopedCFTypeRef<CFNumberRef> aggregate_device_number(CFNumberCreate(
200 NULL,
201 kCFNumberIntType,
202 &value));
203 CFDictionaryAddValue(
204 aggregate_device_dict,
205 CFSTR(kAudioAggregateDeviceIsPrivateKey),
206 aggregate_device_number);
207
208 return aggregate_device_dict;
209 }
210
211 CFMutableArrayRef
212 AggregateDeviceManager::CreateSubDeviceArray(
213 CFStringRef input_device_UID, CFStringRef output_device_UID) {
214 CFMutableArrayRef sub_devices_array = CFArrayCreateMutable(
215 NULL,
216 0,
217 &kCFTypeArrayCallBacks);
218
219 CFArrayAppendValue(sub_devices_array, input_device_UID);
220 CFArrayAppendValue(sub_devices_array, output_device_UID);
221
222 return sub_devices_array;
223 }
224
225 OSStatus AggregateDeviceManager::CreateAggregateDevice(
226 AudioDeviceID input_id,
227 AudioDeviceID output_id,
228 AudioDeviceID* aggregate_device) {
229 DCHECK(aggregate_device);
230
231 const size_t kMaxDeviceNameLength = 256;
232
233 scoped_ptr<char[]> input_device_name(new char[kMaxDeviceNameLength]);
234 GetDeviceName(
235 input_id,
236 input_device_name.get(),
237 sizeof(input_device_name));
238 DVLOG(1) << "Input device: \n" << input_device_name;
239
240 scoped_ptr<char[]> output_device_name(new char[kMaxDeviceNameLength]);
241 GetDeviceName(
242 output_id,
243 output_device_name.get(),
244 sizeof(output_device_name));
245 DVLOG(1) << "Output device: \n" << output_device_name;
246
247 OSStatus result = GetPluginID(&plugin_id_);
248 if (result != noErr)
249 return result;
250
251 // Create a dictionary for the aggregate device.
252 ScopedCFTypeRef<CFMutableDictionaryRef> aggregate_device_dict(
253 CreateAggregateDeviceDictionary(input_id, output_id));
254 if (!aggregate_device_dict)
255 return -1;
256
257 // Create the aggregate device.
258 static const AudioObjectPropertyAddress kCreateAggregateDeviceAddress = {
259 kAudioPlugInCreateAggregateDevice,
260 kAudioObjectPropertyScopeGlobal,
261 kAudioObjectPropertyElementMaster
262 };
263
264 UInt32 size = sizeof(*aggregate_device);
265 result = AudioObjectGetPropertyData(
266 plugin_id_,
267 &kCreateAggregateDeviceAddress,
268 sizeof(aggregate_device_dict),
269 &aggregate_device_dict,
270 &size,
271 aggregate_device);
272 if (result != noErr) {
273 DLOG(ERROR) << "Error creating aggregate audio device!";
274 return result;
275 }
276
277 // Set the sub-devices for the aggregate device.
278 // In this case we use two: the input and output devices.
279
280 ScopedCFTypeRef<CFStringRef> input_device_UID(GetDeviceUID(input_id));
281 ScopedCFTypeRef<CFStringRef> output_device_UID(GetDeviceUID(output_id));
282 if (!input_device_UID || !output_device_UID) {
283 DLOG(ERROR) << "Error getting audio device UID strings.";
284 return -1;
285 }
286
287 ScopedCFTypeRef<CFMutableArrayRef> sub_devices_array(
288 CreateSubDeviceArray(input_device_UID, output_device_UID));
289 if (sub_devices_array == NULL) {
290 DLOG(ERROR) << "Error creating sub-devices array.";
291 return -1;
292 }
293
294 static const AudioObjectPropertyAddress kSetSubDevicesAddress = {
295 kAudioAggregateDevicePropertyFullSubDeviceList,
296 kAudioObjectPropertyScopeGlobal,
297 kAudioObjectPropertyElementMaster
298 };
299
300 size = sizeof(CFMutableArrayRef);
301 result = AudioObjectSetPropertyData(
302 *aggregate_device,
303 &kSetSubDevicesAddress,
304 0,
305 NULL,
306 size,
307 &sub_devices_array);
308 if (result != noErr) {
309 DLOG(ERROR) << "Error setting aggregate audio device sub-devices!";
310 return result;
311 }
312
313 // Use the input device as the master device.
314 static const AudioObjectPropertyAddress kSetMasterDeviceAddress = {
315 kAudioAggregateDevicePropertyMasterSubDevice,
316 kAudioObjectPropertyScopeGlobal,
317 kAudioObjectPropertyElementMaster
318 };
319
320 size = sizeof(CFStringRef);
321 result = AudioObjectSetPropertyData(
322 *aggregate_device,
323 &kSetMasterDeviceAddress,
324 0,
325 NULL,
326 size,
327 &input_device_UID);
328 if (result != noErr) {
329 DLOG(ERROR) << "Error setting aggregate audio device master device!";
330 return result;
331 }
332
333 DVLOG(1) << "New aggregate device: " << *aggregate_device;
334 return noErr;
335 }
336
337 void AggregateDeviceManager::DestroyAggregateDevice() {
338 if (aggregate_device_ == kAudioObjectUnknown)
339 return;
340
341 static const AudioObjectPropertyAddress kDestroyAddress = {
342 kAudioPlugInDestroyAggregateDevice,
343 kAudioObjectPropertyScopeGlobal,
344 kAudioObjectPropertyElementMaster
345 };
346
347 UInt32 size = sizeof(aggregate_device_);
348 OSStatus result = AudioObjectGetPropertyData(
349 plugin_id_,
350 &kDestroyAddress,
351 0,
352 NULL,
353 &size,
354 &aggregate_device_);
355 if (result != noErr) {
356 DLOG(ERROR) << "Error destroying aggregate audio device!";
357 return;
358 }
359
360 aggregate_device_ = kAudioObjectUnknown;
361 }
362
363 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698