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