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