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

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.
DaleCurtis 2013/04/17 01:01:38 Should you destroy the old one at this point?
Chris Rogers 2013/04/17 20:42:31 I thought about this, but it would not be safe bec
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 VLOG(1) << "input_clockdomain: " << input_clockdomain;
DaleCurtis 2013/04/17 01:01:38 DVLOG? Here and all other VLOG.
Chris Rogers 2013/04/17 20:42:31 Done.
58 VLOG(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 OSStatus AggregateDeviceManager::GetDeviceName(
no longer working on chromium 2013/04/17 09:16:22 nit, make it void since we don't care the return v
Chris Rogers 2013/04/17 20:42:31 Done.
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 return result;
112 }
113
114 UInt32 AggregateDeviceManager::GetClockDomain(AudioDeviceID device_id) {
115 static const AudioObjectPropertyAddress kClockDomainAddress = {
116 kAudioDevicePropertyClockDomain,
117 kAudioObjectPropertyScopeGlobal,
118 kAudioObjectPropertyElementMaster
119 };
120
121 UInt32 clockdomain = 0;
122 UInt32 size = sizeof(UInt32);
123 OSStatus result = AudioObjectGetPropertyData(
124 device_id,
125 &kClockDomainAddress,
126 0,
127 0,
128 &size,
129 &clockdomain);
130
131 return (result == noErr) ? clockdomain : 0;
132 }
133
134 OSStatus AggregateDeviceManager::GetPluginID(AudioObjectID* id) {
135 DCHECK(id);
136
137 // Get the audio hardware plugin.
138 CFStringRef bundle_name = CFSTR("com.apple.audio.CoreAudio");
139
140 AudioValueTranslation plugin_translation;
141 plugin_translation.mInputData = &bundle_name;
142 plugin_translation.mInputDataSize = sizeof(bundle_name);
143 plugin_translation.mOutputData = id;
144 plugin_translation.mOutputDataSize = sizeof(*id);
145
146 static const AudioObjectPropertyAddress kPlugInAddress = {
147 kAudioHardwarePropertyPlugInForBundleID,
148 kAudioObjectPropertyScopeGlobal,
149 kAudioObjectPropertyElementMaster
150 };
151
152 UInt32 size = sizeof(plugin_translation);
153 OSStatus result = AudioObjectGetPropertyData(
154 kAudioObjectSystemObject,
155 &kPlugInAddress,
156 0,
157 0,
158 &size,
159 &plugin_translation);
160
161 VLOG(1) << "CoreAudio plugin ID: " << *id;
162
163 return result;
164 }
165
166 CFMutableDictionaryRef
167 AggregateDeviceManager::CreateAggregateDeviceDictionary(
168 AudioDeviceID input_id,
169 AudioDeviceID output_id) {
170 CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(
171 NULL,
172 0,
173 &kCFTypeDictionaryKeyCallBacks,
174 &kCFTypeDictionaryValueCallBacks);
175 if (!aggregate_device_dict)
176 return NULL;
177
178 CFStringRef kAggregateDeviceName =
no longer working on chromium 2013/04/17 09:16:22 nit, should it be aggregate_device_name here, acco
Chris Rogers 2013/04/17 20:42:31 changed to "const"
179 CFSTR("ChromeAggregateAudioDevice");
180 CFStringRef kAggregateDeviceUID =
181 CFSTR("com.google.chrome.AggregateAudioDevice");
182
183 // Add name and UID of the device to the dictionary.
184 CFDictionaryAddValue(
185 aggregate_device_dict,
186 CFSTR(kAudioAggregateDeviceNameKey),
187 kAggregateDeviceName);
188 CFDictionaryAddValue(
189 aggregate_device_dict,
190 CFSTR(kAudioAggregateDeviceUIDKey),
191 kAggregateDeviceUID);
192
193 // Add a "private aggregate key" to the dictionary.
194 // The 1 value means that the created aggregate device will
195 // only be accessible from the process that created it, and
196 // won't be visible to outside processes.
197 int value = 1;
198 CFNumberRef kAggregateDeviceNumber = CFNumberCreate(
no longer working on chromium 2013/04/17 09:16:22 is it a leak here? I guess you might need to call
Chris Rogers 2013/04/17 20:42:31 Yes, good catch - Fixed
199 NULL,
200 kCFNumberIntType,
201 &value);
202 CFDictionaryAddValue(
203 aggregate_device_dict,
204 CFSTR(kAudioAggregateDeviceIsPrivateKey),
205 kAggregateDeviceNumber);
206
207 return aggregate_device_dict;
208 }
209
210 CFMutableArrayRef
211 AggregateDeviceManager::CreateSubDeviceArray(
212 CFStringRef input_device_UID, CFStringRef output_device_UID) {
213 CFMutableArrayRef sub_devices_array = CFArrayCreateMutable(
214 NULL,
215 0,
216 &kCFTypeArrayCallBacks);
217
218 CFArrayAppendValue(sub_devices_array, input_device_UID);
219 CFArrayAppendValue(sub_devices_array, output_device_UID);
220
221 return sub_devices_array;
222 }
223
224 OSStatus AggregateDeviceManager::CreateAggregateDevice(
225 AudioDeviceID input_id,
226 AudioDeviceID output_id,
227 AudioDeviceID* aggregate_device) {
228 DCHECK(aggregate_device);
229
230 const size_t kMaxDeviceNameLength = 256;
231 char input_device_name_[kMaxDeviceNameLength];
DaleCurtis 2013/04/17 01:01:38 dynamically allocate instead?
Chris Rogers 2013/04/17 20:42:31 Done.
232 GetDeviceName(
233 input_id,
234 input_device_name_,
235 sizeof(input_device_name_));
236 VLOG(1) << "Input device: \n" << input_device_name_;
237
238 char output_device_name_[kMaxDeviceNameLength];
DaleCurtis 2013/04/17 01:01:38 Ditto.
Chris Rogers 2013/04/17 20:42:31 Done.
239 GetDeviceName(
240 output_id,
241 output_device_name_,
242 sizeof(output_device_name_));
243 VLOG(1) << "Output device: \n" << output_device_name_;
244
245 OSStatus result = GetPluginID(&plugin_id_);
246 if (result != noErr)
247 return result;
248
249 // Create a dictionary for the aggregate device.
250 ScopedCFTypeRef<CFMutableDictionaryRef> aggregate_device_dict(
251 CreateAggregateDeviceDictionary(input_id, output_id));
252 if (aggregate_device_dict == NULL)
DaleCurtis 2013/04/17 01:01:38 !aggregate_device_dict
Chris Rogers 2013/04/17 20:42:31 Done.
253 return -1;
254
255 // Create the aggregate device.
256 static const AudioObjectPropertyAddress kCreateAggregateDeviceAddress = {
257 kAudioPlugInCreateAggregateDevice,
258 kAudioObjectPropertyScopeGlobal,
259 kAudioObjectPropertyElementMaster
260 };
261
262 UInt32 size = sizeof(*aggregate_device);
263 result = AudioObjectGetPropertyData(
264 plugin_id_,
265 &kCreateAggregateDeviceAddress,
266 sizeof(aggregate_device_dict),
267 &aggregate_device_dict,
268 &size,
269 aggregate_device);
270 if (result != noErr) {
271 DLOG(ERROR) << "Error creating aggregate audio device!";
272 return result;
273 }
274
275 // Set the sub-devices for the aggregate device.
276 // In this case we use two: the input and output devices.
277
278 ScopedCFTypeRef<CFStringRef> input_device_UID(GetDeviceUID(input_id));
279 ScopedCFTypeRef<CFStringRef> output_device_UID(GetDeviceUID(output_id));
280 if (input_device_UID == NULL || output_device_UID == NULL) {
DaleCurtis 2013/04/17 01:01:38 use ! instead.
Chris Rogers 2013/04/17 20:42:31 Done.
281 DLOG(ERROR) << "Error getting audio device UID strings.";
282 return -1;
283 }
284
285 ScopedCFTypeRef<CFMutableArrayRef> sub_devices_array(
286 CreateSubDeviceArray(input_device_UID, output_device_UID));
287 if (sub_devices_array == NULL) {
288 DLOG(ERROR) << "Error creating sub-devices array.";
289 return -1;
290 }
291
292 static const AudioObjectPropertyAddress kSetSubDevicesAddress = {
293 kAudioAggregateDevicePropertyFullSubDeviceList,
294 kAudioObjectPropertyScopeGlobal,
295 kAudioObjectPropertyElementMaster
296 };
297
298 size = sizeof(CFMutableArrayRef);
299 result = AudioObjectSetPropertyData(
300 *aggregate_device,
301 &kSetSubDevicesAddress,
302 0,
303 NULL,
304 size,
305 &sub_devices_array);
306 if (result != noErr) {
307 DLOG(ERROR) << "Error setting aggregate audio device sub-devices!";
308 return result;
309 }
310
311 // Use the input device as the master device.
312 static const AudioObjectPropertyAddress kSetMasterDeviceAddress = {
313 kAudioAggregateDevicePropertyMasterSubDevice,
314 kAudioObjectPropertyScopeGlobal,
315 kAudioObjectPropertyElementMaster
316 };
317
318 size = sizeof(CFStringRef);
319 result = AudioObjectSetPropertyData(
320 *aggregate_device,
321 &kSetMasterDeviceAddress,
322 0,
323 NULL,
324 size,
325 &input_device_UID);
326 if (result != noErr) {
327 DLOG(ERROR) << "Error setting aggregate audio device master device!";
328 return result;
329 }
330
331 VLOG(1) << "New aggregate device: " << *aggregate_device;
332 return noErr;
333 }
334
335 void AggregateDeviceManager::DestroyAggregateDevice() {
336 if (aggregate_device_ == kAudioObjectUnknown)
337 return;
338
339 static const AudioObjectPropertyAddress kDestroyAddress = {
340 kAudioPlugInDestroyAggregateDevice,
341 kAudioObjectPropertyScopeGlobal,
342 kAudioObjectPropertyElementMaster
343 };
344
345 UInt32 size = sizeof(aggregate_device_);
346 OSStatus result = AudioObjectGetPropertyData(
347 plugin_id_,
348 &kDestroyAddress,
349 0,
350 NULL,
351 &size,
352 &aggregate_device_);
353 if (result != noErr) {
354 DLOG(ERROR) << "Error destroying aggregate audio device!";
355 return;
356 }
357
358 aggregate_device_ = kAudioObjectUnknown;
359 }
360
361 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698