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

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 AudioManagerMac::GetDefaultInputDevice(&input_device_);
25 AudioManagerMac::GetDefaultOutputDevice(&output_device_);
26 }
27
28 AggregateDeviceManager::~AggregateDeviceManager() {
29 DestroyAggregateDevice();
30 }
31
32 AudioDeviceID AggregateDeviceManager::GetDefaultAggregateDevice() {
33 // TODO(crogers): handle default device changes for synchronized I/O.
34 // Right now, we just re-use the same previously created aggregate
35 // device even if the user has switched the default devices in the
36 // meantime.
37 if (aggregate_device_ != kAudioObjectUnknown)
38 return aggregate_device_;
39
40 OSStatus result = CreateAggregateDevice(
41 input_device_,
42 output_device_,
43 &aggregate_device_);
44 if (result != noErr)
45 DestroyAggregateDevice();
46
47 return aggregate_device_;
48 }
49
50 CFStringRef AggregateDeviceManager::GetDeviceUID(AudioDeviceID id) {
51 static const AudioObjectPropertyAddress kDeviceUIDAddress = {
52 kAudioDevicePropertyDeviceUID,
53 kAudioObjectPropertyScopeGlobal,
54 kAudioObjectPropertyElementMaster
55 };
56
57 // As stated in the CoreAudio header (AudioHardwareBase.h),
58 // the caller is responsible for releasing the device_UID.
59 CFStringRef device_UID;
60 UInt32 size = sizeof(device_UID);
61 OSStatus result = AudioObjectGetPropertyData(
62 id,
63 &kDeviceUIDAddress,
64 0,
65 0,
66 &size,
67 &device_UID);
68
69 return (result == noErr) ? device_UID : NULL;
70 }
71
72 OSStatus AggregateDeviceManager::GetDeviceName(
73 AudioDeviceID id, char* name, UInt32 size) {
74 static const AudioObjectPropertyAddress kDeviceNameAddress = {
75 kAudioDevicePropertyDeviceName,
76 kAudioObjectPropertyScopeGlobal,
77 kAudioObjectPropertyElementMaster
78 };
79
80 OSStatus result = AudioObjectGetPropertyData(
81 id,
82 &kDeviceNameAddress,
83 0,
84 0,
85 &size,
86 name);
87
88 return result;
89 }
90
91 UInt32 AggregateDeviceManager::GetClockDomain(AudioDeviceID device_id) {
92 static const AudioObjectPropertyAddress kClockDomainAddress = {
93 kAudioDevicePropertyClockDomain,
94 kAudioObjectPropertyScopeGlobal,
95 kAudioObjectPropertyElementMaster
96 };
97
98 UInt32 clockdomain = 0;
99 UInt32 size = sizeof(UInt32);
100 OSStatus result = AudioObjectGetPropertyData(
101 device_id,
102 &kClockDomainAddress,
103 0,
104 0,
105 &size,
106 &clockdomain);
107 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.
108 clockdomain = 0;
109 return clockdomain;
110 }
111
112 bool AggregateDeviceManager::UseDefaultAggregateDevice() {
113 UInt32 input_clockdomain = GetClockDomain(input_device_);
114 UInt32 output_clockdomain = GetClockDomain(output_device_);
115 VLOG(1) << "input_clockdomain: " << input_clockdomain;
116 VLOG(1) << "output_clockdomain: " << output_clockdomain;
117
118 return input_clockdomain > 0 &&
119 input_clockdomain == output_clockdomain;
120 }
121
122 OSStatus AggregateDeviceManager::GetPluginID(AudioObjectID* id) {
123 DCHECK(id);
124
125 // Get the audio hardware plugin.
126 CFStringRef bundle_name = CFSTR("com.apple.audio.CoreAudio");
127
128 AudioValueTranslation plugin_translation;
129 plugin_translation.mInputData = &bundle_name;
130 plugin_translation.mInputDataSize = sizeof(bundle_name);
131 plugin_translation.mOutputData = id;
132 plugin_translation.mOutputDataSize = sizeof(*id);
133
134 static const AudioObjectPropertyAddress kPlugInAddress = {
135 kAudioHardwarePropertyPlugInForBundleID,
136 kAudioObjectPropertyScopeGlobal,
137 kAudioObjectPropertyElementMaster
138 };
139
140 UInt32 size = sizeof(plugin_translation);
141 OSStatus result = AudioObjectGetPropertyData(
142 kAudioObjectSystemObject,
143 &kPlugInAddress,
144 0,
145 0,
146 &size,
147 &plugin_translation);
148
149 VLOG(1) << "CoreAudio plugin ID: " << *id;
150
151 return result;
152 }
153
154 CFMutableDictionaryRef
155 AggregateDeviceManager::CreateAggregateDeviceDictionary(
156 AudioDeviceID input_id,
157 AudioDeviceID output_id) {
158 CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(
159 NULL,
160 0,
161 &kCFTypeDictionaryKeyCallBacks,
162 &kCFTypeDictionaryValueCallBacks);
163 if (!aggregate_device_dict)
164 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.
165
166 CFStringRef kAggregateDeviceName =
167 CFSTR("ChromeAggregateAudioDevice");
168 CFStringRef kAggregateDeviceUID =
169 CFSTR("com.google.chrome.AggregateAudioDevice");
170
171 // Add name and UID of the device to the dictionary.
172 CFDictionaryAddValue(
173 aggregate_device_dict,
174 CFSTR(kAudioAggregateDeviceNameKey),
175 kAggregateDeviceName);
176 CFDictionaryAddValue(
177 aggregate_device_dict,
178 CFSTR(kAudioAggregateDeviceUIDKey),
179 kAggregateDeviceUID);
180
181 // Add a "private aggregate key" to the dictionary.
182 // The 1 value means that the created aggregate device will
183 // only be accessible from the process that created it, and
184 // won't be visible to outside processes.
185 int value = 1;
186 CFNumberRef kAggregateDeviceNumber = CFNumberCreate(
187 NULL,
188 kCFNumberIntType,
189 &value);
190 CFDictionaryAddValue(
191 aggregate_device_dict,
192 CFSTR(kAudioAggregateDeviceIsPrivateKey),
193 kAggregateDeviceNumber);
194
195 return aggregate_device_dict;
196 }
197
198 CFMutableArrayRef
199 AggregateDeviceManager::CreateSubDeviceArray(
200 CFStringRef input_device_UID, CFStringRef output_device_UID) {
201 CFMutableArrayRef sub_devices_array = CFArrayCreateMutable(
202 NULL,
203 0,
204 &kCFTypeArrayCallBacks);
205
206 CFArrayAppendValue(sub_devices_array, input_device_UID);
207 CFArrayAppendValue(sub_devices_array, output_device_UID);
208
209 return sub_devices_array;
210 }
211
212 OSStatus AggregateDeviceManager::CreateAggregateDevice(
213 AudioDeviceID input_id,
214 AudioDeviceID output_id,
215 AudioDeviceID* aggregate_device) {
216 DCHECK(aggregate_device);
217
218 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.
219 GetDeviceName(
220 input_id,
221 input_device_name_,
222 sizeof(input_device_name_));
223 VLOG(1) << "Input device: \n" << input_device_name_;
224
225 char output_device_name_[256];
226 GetDeviceName(
227 output_id,
228 output_device_name_,
229 sizeof(output_device_name_));
230 VLOG(1) << "Output device: \n" << output_device_name_;
231
232 OSStatus result = GetPluginID(&plugin_id_);
233 if (result != noErr)
234 return result;
235
236 // Create a dictionary for the aggregate device.
237 ScopedCFTypeRef<CFMutableDictionaryRef> aggregate_device_dict(
238 CreateAggregateDeviceDictionary(input_id, output_id));
239 if (aggregate_device_dict == NULL)
240 return -1;
241
242 // Create the aggregate device.
243 static const AudioObjectPropertyAddress kCreateAggregateDeviceAddress = {
244 kAudioPlugInCreateAggregateDevice,
245 kAudioObjectPropertyScopeGlobal,
246 kAudioObjectPropertyElementMaster
247 };
248
249 UInt32 size = sizeof(*aggregate_device);
250 result = AudioObjectGetPropertyData(
251 plugin_id_,
252 &kCreateAggregateDeviceAddress,
253 sizeof(aggregate_device_dict),
254 &aggregate_device_dict,
255 &size,
256 aggregate_device);
257 if (result != noErr) {
258 DLOG(ERROR) << "Error creating aggregate audio device!";
259 return result;
260 }
261
262 // Set the sub-devices for the aggregate device.
263 // In this case we use two: the input and output devices.
264
265 ScopedCFTypeRef<CFStringRef> input_device_UID(GetDeviceUID(input_id));
266 ScopedCFTypeRef<CFStringRef> output_device_UID(GetDeviceUID(output_id));
267 if (input_device_UID == NULL || output_device_UID == NULL)
268 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.
269
270 ScopedCFTypeRef<CFMutableArrayRef> sub_devices_array(
271 CreateSubDeviceArray(input_device_UID, output_device_UID));
272 if (sub_devices_array == NULL)
273 return -1;
no longer working on chromium 2013/04/03 21:11:57 ditto
Chris Rogers 2013/04/05 20:01:38 Done.
274
275 static const AudioObjectPropertyAddress kSetSubDevicesAddress = {
276 kAudioAggregateDevicePropertyFullSubDeviceList,
277 kAudioObjectPropertyScopeGlobal,
278 kAudioObjectPropertyElementMaster
279 };
280
281 size = sizeof(CFMutableArrayRef);
282 result = AudioObjectSetPropertyData(
283 *aggregate_device,
284 &kSetSubDevicesAddress,
285 0,
286 NULL,
287 size,
288 &sub_devices_array);
289 if (result != noErr) {
290 DLOG(ERROR) << "Error setting aggregate audio device sub-devices!";
291 return result;
292 }
293
294 // Use the input device as the master device.
295 static const AudioObjectPropertyAddress kSetMasterDeviceAddress = {
296 kAudioAggregateDevicePropertyMasterSubDevice,
297 kAudioObjectPropertyScopeGlobal,
298 kAudioObjectPropertyElementMaster
299 };
300
301 size = sizeof(CFStringRef);
302 result = AudioObjectSetPropertyData(
303 *aggregate_device,
304 &kSetMasterDeviceAddress,
305 0,
306 NULL,
307 size,
308 &input_device_UID);
309 if (result != noErr) {
310 DLOG(ERROR) << "Error setting aggregate audio device master device!";
311 return result;
312 }
313
314 VLOG(1) << "New aggregate device: " << *aggregate_device;
315 return noErr;
316 }
317
318 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.
319 if (aggregate_device_ == kAudioObjectUnknown)
320 return noErr;
321
322 static const AudioObjectPropertyAddress kDestroyAddress = {
323 kAudioPlugInDestroyAggregateDevice,
324 kAudioObjectPropertyScopeGlobal,
325 kAudioObjectPropertyElementMaster
326 };
327
328 UInt32 size = sizeof(aggregate_device_);
329 OSStatus result = AudioObjectGetPropertyData(
330 plugin_id_,
331 &kDestroyAddress,
332 0,
333 NULL,
334 &size,
335 &aggregate_device_);
336 if (result != noErr) {
337 DLOG(ERROR) << "Error destroying aggregate audio device!";
338 return result;
339 }
340
341 aggregate_device_ = kAudioObjectUnknown;
342
343 return noErr;
344 }
345
346 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698