- add sources.
[platform/framework/web/crosswalk.git] / src / media / audio / mac / audio_auhal_mac.cc
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
17 namespace media {
18
19 static 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
32 static 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
40 static 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   CHECK_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
59 AUHALStream::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
87 AUHALStream::~AUHALStream() {
88 }
89
90 bool 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
136 void 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     OSStatus result = AudioUnitUninitialize(audio_unit_);
146     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
147         << "AudioUnitUninitialize() failed.";
148     result = AudioComponentInstanceDispose(audio_unit_);
149     OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
150         << "AudioComponentInstanceDispose() failed.";
151   }
152
153   // Inform the audio manager that we have been closed. This will cause our
154   // destruction.
155   manager_->ReleaseOutputStream(this);
156 }
157
158 void AUHALStream::Start(AudioSourceCallback* callback) {
159   DCHECK(callback);
160   if (!audio_unit_) {
161     DLOG(ERROR) << "Open() has not been called successfully";
162     return;
163   }
164
165   stopped_ = false;
166   notified_for_possible_device_change_ = false;
167   {
168     base::AutoLock auto_lock(source_lock_);
169     source_ = callback;
170   }
171
172   OSStatus result = AudioOutputUnitStart(audio_unit_);
173   if (result == noErr)
174     return;
175
176   Stop();
177   OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed.";
178   callback->OnError(this);
179 }
180
181 void AUHALStream::Stop() {
182   if (stopped_)
183     return;
184
185   OSStatus result = AudioOutputUnitStop(audio_unit_);
186   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
187       << "AudioOutputUnitStop() failed.";
188   if (result != noErr)
189     source_->OnError(this);
190
191   base::AutoLock auto_lock(source_lock_);
192   source_ = NULL;
193   stopped_ = true;
194 }
195
196 void AUHALStream::SetVolume(double volume) {
197   volume_ = static_cast<float>(volume);
198 }
199
200 void AUHALStream::GetVolume(double* volume) {
201   *volume = volume_;
202 }
203
204 // Pulls on our provider to get rendered audio stream.
205 // Note to future hackers of this function: Do not add locks which can
206 // be contended in the middle of stream processing here (starting and stopping
207 // the stream are ok) because this is running on a real-time thread.
208 OSStatus AUHALStream::Render(
209     AudioUnitRenderActionFlags* flags,
210     const AudioTimeStamp* output_time_stamp,
211     UInt32 bus_number,
212     UInt32 number_of_frames,
213     AudioBufferList* io_data) {
214   TRACE_EVENT0("audio", "AUHALStream::Render");
215
216   if (number_of_frames != number_of_frames_) {
217     // This can happen if we've suddenly changed sample-rates.
218     // The stream should be stopping very soon.
219     //
220     // Unfortunately AUAudioInputStream and AUHALStream share the frame
221     // size set by kAudioDevicePropertyBufferFrameSize above on a per process
222     // basis.  What this means is that the |number_of_frames| value may be
223     // larger or smaller than the value set during ConfigureAUHAL().
224     // In this case either audio input or audio output will be broken,
225     // so just output silence.
226     ZeroBufferList(io_data);
227     return noErr;
228   }
229
230   if (input_channels_ > 0 && input_buffer_list_) {
231     // Get the input data.  |input_buffer_list_| is wrapped
232     // to point to the data allocated in |input_bus_|.
233     OSStatus result = AudioUnitRender(
234         audio_unit_,
235         flags,
236         output_time_stamp,
237         1,
238         number_of_frames,
239         input_buffer_list_);
240     if (result != noErr)
241       ZeroBufferList(input_buffer_list_);
242   }
243
244   // Make |output_bus_| wrap the output AudioBufferList.
245   WrapBufferList(io_data, output_bus_.get(), number_of_frames);
246
247   // Update the playout latency.
248   double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
249
250   uint32 hardware_pending_bytes = static_cast<uint32>
251       ((playout_latency_frames + 0.5) * output_format_.mBytesPerFrame);
252
253   {
254     // Render() shouldn't be called except between AudioOutputUnitStart() and
255     // AudioOutputUnitStop() calls, but crash reports have shown otherwise:
256     // http://crbug.com/178765.  We use |source_lock_| to prevent races and
257     // crashes in Render() when |source_| is cleared.
258     base::AutoLock auto_lock(source_lock_);
259     if (!source_) {
260       ZeroBufferList(io_data);
261       return noErr;
262     }
263
264     // Supply the input data and render the output data.
265     source_->OnMoreIOData(
266         input_bus_.get(),
267         output_bus_.get(),
268         AudioBuffersState(0, hardware_pending_bytes));
269     output_bus_->Scale(volume_);
270   }
271
272   return noErr;
273 }
274
275 // AUHAL callback.
276 OSStatus AUHALStream::InputProc(
277     void* user_data,
278     AudioUnitRenderActionFlags* flags,
279     const AudioTimeStamp* output_time_stamp,
280     UInt32 bus_number,
281     UInt32 number_of_frames,
282     AudioBufferList* io_data) {
283   // Dispatch to our class method.
284   AUHALStream* audio_output =
285       static_cast<AUHALStream*>(user_data);
286   if (!audio_output)
287     return -1;
288
289   return audio_output->Render(
290       flags,
291       output_time_stamp,
292       bus_number,
293       number_of_frames,
294       io_data);
295 }
296
297 double AUHALStream::GetHardwareLatency() {
298   if (!audio_unit_ || device_ == kAudioObjectUnknown) {
299     DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown";
300     return 0.0;
301   }
302
303   // Get audio unit latency.
304   Float64 audio_unit_latency_sec = 0.0;
305   UInt32 size = sizeof(audio_unit_latency_sec);
306   OSStatus result = AudioUnitGetProperty(
307       audio_unit_,
308       kAudioUnitProperty_Latency,
309       kAudioUnitScope_Global,
310       0,
311       &audio_unit_latency_sec,
312       &size);
313   if (result != noErr) {
314     OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency";
315     return 0.0;
316   }
317
318   // Get output audio device latency.
319   static const AudioObjectPropertyAddress property_address = {
320     kAudioDevicePropertyLatency,
321     kAudioDevicePropertyScopeOutput,
322     kAudioObjectPropertyElementMaster
323   };
324
325   UInt32 device_latency_frames = 0;
326   size = sizeof(device_latency_frames);
327   result = AudioObjectGetPropertyData(
328       device_,
329       &property_address,
330       0,
331       NULL,
332       &size,
333       &device_latency_frames);
334   if (result != noErr) {
335     OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency";
336     return 0.0;
337   }
338
339   return static_cast<double>((audio_unit_latency_sec *
340       output_format_.mSampleRate) + device_latency_frames);
341 }
342
343 double AUHALStream::GetPlayoutLatency(
344     const AudioTimeStamp* output_time_stamp) {
345   // Ensure mHostTime is valid.
346   if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0)
347     return 0;
348
349   // Get the delay between the moment getting the callback and the scheduled
350   // time stamp that tells when the data is going to be played out.
351   UInt64 output_time_ns = AudioConvertHostTimeToNanos(
352       output_time_stamp->mHostTime);
353   UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
354
355   // Prevent overflow leading to huge delay information; occurs regularly on
356   // the bots, probably less so in the wild.
357   if (now_ns > output_time_ns)
358     return 0;
359
360   double delay_frames = static_cast<double>
361       (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate);
362
363   return (delay_frames + hardware_latency_frames_);
364 }
365
366 void AUHALStream::CreateIOBusses() {
367   if (input_channels_ > 0) {
368     // Allocate storage for the AudioBufferList used for the
369     // input data from the input AudioUnit.
370     // We allocate enough space for with one AudioBuffer per channel.
371     size_t buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) +
372         (sizeof(AudioBuffer) * input_channels_);
373     input_buffer_list_storage_.reset(new uint8[buffer_list_size]);
374
375     input_buffer_list_ =
376         reinterpret_cast<AudioBufferList*>(input_buffer_list_storage_.get());
377     input_buffer_list_->mNumberBuffers = input_channels_;
378
379     // |input_bus_| allocates the storage for the PCM input data.
380     input_bus_ = AudioBus::Create(input_channels_, number_of_frames_);
381
382     // Make the AudioBufferList point to the memory in |input_bus_|.
383     UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32);
384     for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) {
385       input_buffer_list_->mBuffers[i].mNumberChannels = 1;
386       input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes;
387       input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i);
388     }
389   }
390
391   // The output bus will wrap the AudioBufferList given to us in
392   // the Render() callback.
393   DCHECK_GT(output_channels_, 0);
394   output_bus_ = AudioBus::CreateWrapper(output_channels_);
395 }
396
397 bool AUHALStream::EnableIO(bool enable, UInt32 scope) {
398   // See Apple technote for details about the EnableIO property.
399   // Note that we use bus 1 for input and bus 0 for output:
400   // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
401   UInt32 enable_IO = enable ? 1 : 0;
402   OSStatus result = AudioUnitSetProperty(
403       audio_unit_,
404       kAudioOutputUnitProperty_EnableIO,
405       scope,
406       (scope == kAudioUnitScope_Input) ? 1 : 0,
407       &enable_IO,
408       sizeof(enable_IO));
409   return (result == noErr);
410 }
411
412 bool AUHALStream::SetStreamFormat(
413     AudioStreamBasicDescription* desc,
414     int channels,
415     UInt32 scope,
416     UInt32 element) {
417   DCHECK(desc);
418   AudioStreamBasicDescription& format = *desc;
419
420   format.mSampleRate = params_.sample_rate();
421   format.mFormatID = kAudioFormatLinearPCM;
422   format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked |
423       kLinearPCMFormatFlagIsNonInterleaved;
424   format.mBytesPerPacket = sizeof(Float32);
425   format.mFramesPerPacket = 1;
426   format.mBytesPerFrame = sizeof(Float32);
427   format.mChannelsPerFrame = channels;
428   format.mBitsPerChannel = 32;
429   format.mReserved = 0;
430
431   OSStatus result = AudioUnitSetProperty(
432       audio_unit_,
433       kAudioUnitProperty_StreamFormat,
434       scope,
435       element,
436       &format,
437       sizeof(format));
438   return (result == noErr);
439 }
440
441 bool AUHALStream::ConfigureAUHAL() {
442   if (device_ == kAudioObjectUnknown ||
443       (input_channels_ == 0 && output_channels_ == 0))
444     return false;
445
446   AudioComponentDescription desc = {
447       kAudioUnitType_Output,
448       kAudioUnitSubType_HALOutput,
449       kAudioUnitManufacturer_Apple,
450       0,
451       0
452   };
453   AudioComponent comp = AudioComponentFindNext(0, &desc);
454   if (!comp)
455     return false;
456
457   OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
458   if (result != noErr) {
459     OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
460     return false;
461   }
462
463   // Enable input and output as appropriate.
464   if (!EnableIO(input_channels_ > 0, kAudioUnitScope_Input))
465     return false;
466   if (!EnableIO(output_channels_ > 0, kAudioUnitScope_Output))
467     return false;
468
469   // Set the device to be used with the AUHAL AudioUnit.
470   result = AudioUnitSetProperty(
471       audio_unit_,
472       kAudioOutputUnitProperty_CurrentDevice,
473       kAudioUnitScope_Global,
474       0,
475       &device_,
476       sizeof(AudioDeviceID));
477   if (result != noErr)
478     return false;
479
480   // Set stream formats.
481   // See Apple's tech note for details on the peculiar way that
482   // inputs and outputs are handled in the AUHAL concerning scope and bus
483   // (element) numbers:
484   // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
485
486   if (input_channels_ > 0) {
487     if (!SetStreamFormat(&input_format_,
488                          input_channels_,
489                          kAudioUnitScope_Output,
490                          1))
491       return false;
492   }
493
494   if (output_channels_ > 0) {
495     if (!SetStreamFormat(&output_format_,
496                          output_channels_,
497                          kAudioUnitScope_Input,
498                          0))
499       return false;
500   }
501
502   // Set the buffer frame size.
503   // WARNING: Setting this value changes the frame size for all audio units in
504   // the current process.  It's imperative that the input and output frame sizes
505   // be the same as the frames_per_buffer() returned by
506   // GetDefaultOutputStreamParameters().
507   // See http://crbug.com/154352 for details.
508   UInt32 buffer_size = number_of_frames_;
509   result = AudioUnitSetProperty(
510       audio_unit_,
511       kAudioDevicePropertyBufferFrameSize,
512       kAudioUnitScope_Output,
513       0,
514       &buffer_size,
515       sizeof(buffer_size));
516   if (result != noErr) {
517     OSSTATUS_DLOG(ERROR, result)
518         << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
519     return false;
520   }
521
522   // Setup callback.
523   AURenderCallbackStruct callback;
524   callback.inputProc = InputProc;
525   callback.inputProcRefCon = this;
526   result = AudioUnitSetProperty(
527       audio_unit_,
528       kAudioUnitProperty_SetRenderCallback,
529       kAudioUnitScope_Input,
530       0,
531       &callback,
532       sizeof(callback));
533   if (result != noErr)
534     return false;
535
536   result = AudioUnitInitialize(audio_unit_);
537   if (result != noErr) {
538     OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
539     return false;
540   }
541
542   return true;
543 }
544
545 }  // namespace media