• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/audio_auhal_mac.h"
6 
7 #include <CoreServices/CoreServices.h>
8 
9 #include "base/basictypes.h"
10 #include "base/debug/trace_event.h"
11 #include "base/logging.h"
12 #include "base/mac/mac_logging.h"
13 #include "media/audio/mac/audio_manager_mac.h"
14 #include "media/base/audio_pull_fifo.h"
15 
16 namespace media {
17 
ZeroBufferList(AudioBufferList * buffer_list)18 static void ZeroBufferList(AudioBufferList* buffer_list) {
19   for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i) {
20     memset(buffer_list->mBuffers[i].mData,
21            0,
22            buffer_list->mBuffers[i].mDataByteSize);
23   }
24 }
25 
WrapBufferList(AudioBufferList * buffer_list,AudioBus * bus,int frames)26 static void WrapBufferList(AudioBufferList* buffer_list,
27                            AudioBus* bus,
28                            int frames) {
29   DCHECK(buffer_list);
30   DCHECK(bus);
31   const int channels = bus->channels();
32   const int buffer_list_channels = buffer_list->mNumberBuffers;
33   CHECK_EQ(channels, buffer_list_channels);
34 
35   // Copy pointers from AudioBufferList.
36   for (int i = 0; i < channels; ++i) {
37     bus->SetChannelData(
38         i, static_cast<float*>(buffer_list->mBuffers[i].mData));
39   }
40 
41   // Finally set the actual length.
42   bus->set_frames(frames);
43 }
44 
AUHALStream(AudioManagerMac * manager,const AudioParameters & params,AudioDeviceID device)45 AUHALStream::AUHALStream(
46     AudioManagerMac* manager,
47     const AudioParameters& params,
48     AudioDeviceID device)
49     : manager_(manager),
50       params_(params),
51       input_channels_(params_.input_channels()),
52       output_channels_(params_.channels()),
53       number_of_frames_(params_.frames_per_buffer()),
54       source_(NULL),
55       device_(device),
56       audio_unit_(0),
57       volume_(1),
58       hardware_latency_frames_(0),
59       stopped_(false),
60       input_buffer_list_(NULL),
61       current_hardware_pending_bytes_(0) {
62   // We must have a manager.
63   DCHECK(manager_);
64 
65   VLOG(1) << "AUHALStream::AUHALStream()";
66   VLOG(1) << "Device: " << device;
67   VLOG(1) << "Input channels: " << input_channels_;
68   VLOG(1) << "Output channels: " << output_channels_;
69   VLOG(1) << "Sample rate: " << params_.sample_rate();
70   VLOG(1) << "Buffer size: " << number_of_frames_;
71 }
72 
~AUHALStream()73 AUHALStream::~AUHALStream() {
74 }
75 
Open()76 bool AUHALStream::Open() {
77   // Get the total number of input and output channels that the
78   // hardware supports.
79   int device_input_channels;
80   bool got_input_channels = AudioManagerMac::GetDeviceChannels(
81       device_,
82       kAudioDevicePropertyScopeInput,
83       &device_input_channels);
84 
85   int device_output_channels;
86   bool got_output_channels = AudioManagerMac::GetDeviceChannels(
87       device_,
88       kAudioDevicePropertyScopeOutput,
89       &device_output_channels);
90 
91   // Sanity check the requested I/O channels.
92   if (!got_input_channels ||
93       input_channels_ < 0 || input_channels_ > device_input_channels) {
94     LOG(ERROR) << "AudioDevice does not support requested input channels.";
95     return false;
96   }
97 
98   if (!got_output_channels ||
99       output_channels_ <= 0 || output_channels_ > device_output_channels) {
100     LOG(ERROR) << "AudioDevice does not support requested output channels.";
101     return false;
102   }
103 
104   // The requested sample-rate must match the hardware sample-rate.
105   int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_);
106 
107   if (sample_rate != params_.sample_rate()) {
108     LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate()
109                << " must match the hardware sample-rate: " << sample_rate;
110     return false;
111   }
112 
113   CreateIOBusses();
114 
115   bool configured = ConfigureAUHAL();
116   if (configured)
117     hardware_latency_frames_ = GetHardwareLatency();
118 
119   return configured;
120 }
121 
Close()122 void AUHALStream::Close() {
123   if (input_buffer_list_) {
124     input_buffer_list_storage_.reset();
125     input_buffer_list_ = NULL;
126     input_bus_.reset(NULL);
127     output_bus_.reset(NULL);
128   }
129 
130   if (audio_unit_) {
131     OSStatus result = AudioUnitUninitialize(audio_unit_);
132     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
133         << "AudioUnitUninitialize() failed.";
134     result = AudioComponentInstanceDispose(audio_unit_);
135     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
136         << "AudioComponentInstanceDispose() failed.";
137   }
138 
139   // Inform the audio manager that we have been closed. This will cause our
140   // destruction.
141   manager_->ReleaseOutputStream(this);
142 }
143 
Start(AudioSourceCallback * callback)144 void AUHALStream::Start(AudioSourceCallback* callback) {
145   DCHECK(callback);
146   if (!audio_unit_) {
147     DLOG(ERROR) << "Open() has not been called successfully";
148     return;
149   }
150 
151   stopped_ = false;
152   audio_fifo_.reset();
153   {
154     base::AutoLock auto_lock(source_lock_);
155     source_ = callback;
156   }
157 
158   OSStatus result = AudioOutputUnitStart(audio_unit_);
159   if (result == noErr)
160     return;
161 
162   Stop();
163   OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed.";
164   callback->OnError(this);
165 }
166 
Stop()167 void AUHALStream::Stop() {
168   if (stopped_)
169     return;
170 
171   OSStatus result = AudioOutputUnitStop(audio_unit_);
172   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
173       << "AudioOutputUnitStop() failed.";
174   if (result != noErr)
175     source_->OnError(this);
176 
177   base::AutoLock auto_lock(source_lock_);
178   source_ = NULL;
179   stopped_ = true;
180 }
181 
SetVolume(double volume)182 void AUHALStream::SetVolume(double volume) {
183   volume_ = static_cast<float>(volume);
184 }
185 
GetVolume(double * volume)186 void AUHALStream::GetVolume(double* volume) {
187   *volume = volume_;
188 }
189 
190 // Pulls on our provider to get rendered audio stream.
191 // Note to future hackers of this function: Do not add locks which can
192 // be contended in the middle of stream processing here (starting and stopping
193 // the stream are ok) because this is running on a real-time thread.
Render(AudioUnitRenderActionFlags * flags,const AudioTimeStamp * output_time_stamp,UInt32 bus_number,UInt32 number_of_frames,AudioBufferList * io_data)194 OSStatus AUHALStream::Render(
195     AudioUnitRenderActionFlags* flags,
196     const AudioTimeStamp* output_time_stamp,
197     UInt32 bus_number,
198     UInt32 number_of_frames,
199     AudioBufferList* io_data) {
200   TRACE_EVENT0("audio", "AUHALStream::Render");
201 
202   // If the stream parameters change for any reason, we need to insert a FIFO
203   // since the OnMoreData() pipeline can't handle frame size changes.  Generally
204   // this is a temporary situation which can occur after a device change has
205   // occurred but the AudioManager hasn't received the notification yet.
206   if (number_of_frames != number_of_frames_) {
207     // Create a FIFO on the fly to handle any discrepancies in callback rates.
208     if (!audio_fifo_) {
209       VLOG(1) << "Audio frame size change detected; adding FIFO to compensate.";
210       audio_fifo_.reset(new AudioPullFifo(
211           output_channels_,
212           number_of_frames_,
213           base::Bind(&AUHALStream::ProvideInput, base::Unretained(this))));
214     }
215 
216     // Synchronous IO is not supported in this state.
217     if (input_channels_ > 0)
218       input_bus_->Zero();
219   } else {
220     if (input_channels_ > 0 && input_buffer_list_) {
221       // Get the input data.  |input_buffer_list_| is wrapped
222       // to point to the data allocated in |input_bus_|.
223       OSStatus result = AudioUnitRender(audio_unit_,
224                                         flags,
225                                         output_time_stamp,
226                                         1,
227                                         number_of_frames,
228                                         input_buffer_list_);
229       if (result != noErr)
230         ZeroBufferList(input_buffer_list_);
231     }
232   }
233 
234   // Make |output_bus_| wrap the output AudioBufferList.
235   WrapBufferList(io_data, output_bus_.get(), number_of_frames);
236 
237   // Update the playout latency.
238   const double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
239   current_hardware_pending_bytes_ = static_cast<uint32>(
240       (playout_latency_frames + 0.5) * params_.GetBytesPerFrame());
241 
242   if (audio_fifo_)
243     audio_fifo_->Consume(output_bus_.get(), output_bus_->frames());
244   else
245     ProvideInput(0, output_bus_.get());
246 
247   return noErr;
248 }
249 
ProvideInput(int frame_delay,AudioBus * dest)250 void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) {
251   base::AutoLock auto_lock(source_lock_);
252   if (!source_) {
253     dest->Zero();
254     return;
255   }
256 
257   // Supply the input data and render the output data.
258   source_->OnMoreIOData(
259       input_bus_.get(),
260       dest,
261       AudioBuffersState(0,
262                         current_hardware_pending_bytes_ +
263                             frame_delay * params_.GetBytesPerFrame()));
264   dest->Scale(volume_);
265 }
266 
267 // AUHAL callback.
InputProc(void * user_data,AudioUnitRenderActionFlags * flags,const AudioTimeStamp * output_time_stamp,UInt32 bus_number,UInt32 number_of_frames,AudioBufferList * io_data)268 OSStatus AUHALStream::InputProc(
269     void* user_data,
270     AudioUnitRenderActionFlags* flags,
271     const AudioTimeStamp* output_time_stamp,
272     UInt32 bus_number,
273     UInt32 number_of_frames,
274     AudioBufferList* io_data) {
275   // Dispatch to our class method.
276   AUHALStream* audio_output =
277       static_cast<AUHALStream*>(user_data);
278   if (!audio_output)
279     return -1;
280 
281   return audio_output->Render(
282       flags,
283       output_time_stamp,
284       bus_number,
285       number_of_frames,
286       io_data);
287 }
288 
GetHardwareLatency()289 double AUHALStream::GetHardwareLatency() {
290   if (!audio_unit_ || device_ == kAudioObjectUnknown) {
291     DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown";
292     return 0.0;
293   }
294 
295   // Get audio unit latency.
296   Float64 audio_unit_latency_sec = 0.0;
297   UInt32 size = sizeof(audio_unit_latency_sec);
298   OSStatus result = AudioUnitGetProperty(
299       audio_unit_,
300       kAudioUnitProperty_Latency,
301       kAudioUnitScope_Global,
302       0,
303       &audio_unit_latency_sec,
304       &size);
305   if (result != noErr) {
306     OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency";
307     return 0.0;
308   }
309 
310   // Get output audio device latency.
311   static const AudioObjectPropertyAddress property_address = {
312     kAudioDevicePropertyLatency,
313     kAudioDevicePropertyScopeOutput,
314     kAudioObjectPropertyElementMaster
315   };
316 
317   UInt32 device_latency_frames = 0;
318   size = sizeof(device_latency_frames);
319   result = AudioObjectGetPropertyData(
320       device_,
321       &property_address,
322       0,
323       NULL,
324       &size,
325       &device_latency_frames);
326   if (result != noErr) {
327     OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency";
328     return 0.0;
329   }
330 
331   return static_cast<double>((audio_unit_latency_sec *
332       output_format_.mSampleRate) + device_latency_frames);
333 }
334 
GetPlayoutLatency(const AudioTimeStamp * output_time_stamp)335 double AUHALStream::GetPlayoutLatency(
336     const AudioTimeStamp* output_time_stamp) {
337   // Ensure mHostTime is valid.
338   if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0)
339     return 0;
340 
341   // Get the delay between the moment getting the callback and the scheduled
342   // time stamp that tells when the data is going to be played out.
343   UInt64 output_time_ns = AudioConvertHostTimeToNanos(
344       output_time_stamp->mHostTime);
345   UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
346 
347   // Prevent overflow leading to huge delay information; occurs regularly on
348   // the bots, probably less so in the wild.
349   if (now_ns > output_time_ns)
350     return 0;
351 
352   double delay_frames = static_cast<double>
353       (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate);
354 
355   return (delay_frames + hardware_latency_frames_);
356 }
357 
CreateIOBusses()358 void AUHALStream::CreateIOBusses() {
359   if (input_channels_ > 0) {
360     // Allocate storage for the AudioBufferList used for the
361     // input data from the input AudioUnit.
362     // We allocate enough space for with one AudioBuffer per channel.
363     size_t buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) +
364         (sizeof(AudioBuffer) * input_channels_);
365     input_buffer_list_storage_.reset(new uint8[buffer_list_size]);
366 
367     input_buffer_list_ =
368         reinterpret_cast<AudioBufferList*>(input_buffer_list_storage_.get());
369     input_buffer_list_->mNumberBuffers = input_channels_;
370 
371     // |input_bus_| allocates the storage for the PCM input data.
372     input_bus_ = AudioBus::Create(input_channels_, number_of_frames_);
373 
374     // Make the AudioBufferList point to the memory in |input_bus_|.
375     UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32);
376     for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) {
377       input_buffer_list_->mBuffers[i].mNumberChannels = 1;
378       input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes;
379       input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i);
380     }
381   }
382 
383   // The output bus will wrap the AudioBufferList given to us in
384   // the Render() callback.
385   DCHECK_GT(output_channels_, 0);
386   output_bus_ = AudioBus::CreateWrapper(output_channels_);
387 }
388 
EnableIO(bool enable,UInt32 scope)389 bool AUHALStream::EnableIO(bool enable, UInt32 scope) {
390   // See Apple technote for details about the EnableIO property.
391   // Note that we use bus 1 for input and bus 0 for output:
392   // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
393   UInt32 enable_IO = enable ? 1 : 0;
394   OSStatus result = AudioUnitSetProperty(
395       audio_unit_,
396       kAudioOutputUnitProperty_EnableIO,
397       scope,
398       (scope == kAudioUnitScope_Input) ? 1 : 0,
399       &enable_IO,
400       sizeof(enable_IO));
401   return (result == noErr);
402 }
403 
SetStreamFormat(AudioStreamBasicDescription * desc,int channels,UInt32 scope,UInt32 element)404 bool AUHALStream::SetStreamFormat(
405     AudioStreamBasicDescription* desc,
406     int channels,
407     UInt32 scope,
408     UInt32 element) {
409   DCHECK(desc);
410   AudioStreamBasicDescription& format = *desc;
411 
412   format.mSampleRate = params_.sample_rate();
413   format.mFormatID = kAudioFormatLinearPCM;
414   format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked |
415       kLinearPCMFormatFlagIsNonInterleaved;
416   format.mBytesPerPacket = sizeof(Float32);
417   format.mFramesPerPacket = 1;
418   format.mBytesPerFrame = sizeof(Float32);
419   format.mChannelsPerFrame = channels;
420   format.mBitsPerChannel = 32;
421   format.mReserved = 0;
422 
423   OSStatus result = AudioUnitSetProperty(
424       audio_unit_,
425       kAudioUnitProperty_StreamFormat,
426       scope,
427       element,
428       &format,
429       sizeof(format));
430   return (result == noErr);
431 }
432 
ConfigureAUHAL()433 bool AUHALStream::ConfigureAUHAL() {
434   if (device_ == kAudioObjectUnknown ||
435       (input_channels_ == 0 && output_channels_ == 0))
436     return false;
437 
438   AudioComponentDescription desc = {
439       kAudioUnitType_Output,
440       kAudioUnitSubType_HALOutput,
441       kAudioUnitManufacturer_Apple,
442       0,
443       0
444   };
445   AudioComponent comp = AudioComponentFindNext(0, &desc);
446   if (!comp)
447     return false;
448 
449   OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
450   if (result != noErr) {
451     OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
452     return false;
453   }
454 
455   // Enable input and output as appropriate.
456   if (!EnableIO(input_channels_ > 0, kAudioUnitScope_Input))
457     return false;
458   if (!EnableIO(output_channels_ > 0, kAudioUnitScope_Output))
459     return false;
460 
461   // Set the device to be used with the AUHAL AudioUnit.
462   result = AudioUnitSetProperty(
463       audio_unit_,
464       kAudioOutputUnitProperty_CurrentDevice,
465       kAudioUnitScope_Global,
466       0,
467       &device_,
468       sizeof(AudioDeviceID));
469   if (result != noErr)
470     return false;
471 
472   // Set stream formats.
473   // See Apple's tech note for details on the peculiar way that
474   // inputs and outputs are handled in the AUHAL concerning scope and bus
475   // (element) numbers:
476   // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
477 
478   if (input_channels_ > 0) {
479     if (!SetStreamFormat(&input_format_,
480                          input_channels_,
481                          kAudioUnitScope_Output,
482                          1))
483       return false;
484   }
485 
486   if (output_channels_ > 0) {
487     if (!SetStreamFormat(&output_format_,
488                          output_channels_,
489                          kAudioUnitScope_Input,
490                          0))
491       return false;
492   }
493 
494   // Set the buffer frame size.
495   // WARNING: Setting this value changes the frame size for all audio units in
496   // the current process.  It's imperative that the input and output frame sizes
497   // be the same as the frames_per_buffer() returned by
498   // GetDefaultOutputStreamParameters().
499   // See http://crbug.com/154352 for details.
500   UInt32 buffer_size = number_of_frames_;
501   result = AudioUnitSetProperty(
502       audio_unit_,
503       kAudioDevicePropertyBufferFrameSize,
504       kAudioUnitScope_Output,
505       0,
506       &buffer_size,
507       sizeof(buffer_size));
508   if (result != noErr) {
509     OSSTATUS_DLOG(ERROR, result)
510         << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
511     return false;
512   }
513 
514   // Setup callback.
515   AURenderCallbackStruct callback;
516   callback.inputProc = InputProc;
517   callback.inputProcRefCon = this;
518   result = AudioUnitSetProperty(
519       audio_unit_,
520       kAudioUnitProperty_SetRenderCallback,
521       kAudioUnitScope_Input,
522       0,
523       &callback,
524       sizeof(callback));
525   if (result != noErr)
526     return false;
527 
528   result = AudioUnitInitialize(audio_unit_);
529   if (result != noErr) {
530     OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
531     return false;
532   }
533 
534   return true;
535 }
536 
537 }  // namespace media
538