• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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_low_latency_output_mac.h"
6 
7 #include <CoreServices/CoreServices.h>
8 
9 #include "base/basictypes.h"
10 #include "base/command_line.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/media_switches.h"
15 
16 namespace media {
17 
operator <<(std::ostream & os,const AudioStreamBasicDescription & format)18 static std::ostream& operator<<(std::ostream& os,
19                                 const AudioStreamBasicDescription& format) {
20   os << "sample rate       : " << format.mSampleRate << std::endl
21      << "format ID         : " << format.mFormatID << std::endl
22      << "format flags      : " << format.mFormatFlags << std::endl
23      << "bytes per packet  : " << format.mBytesPerPacket << std::endl
24      << "frames per packet : " << format.mFramesPerPacket << std::endl
25      << "bytes per frame   : " << format.mBytesPerFrame << std::endl
26      << "channels per frame: " << format.mChannelsPerFrame << std::endl
27      << "bits per channel  : " << format.mBitsPerChannel;
28   return os;
29 }
30 
31 static AudioObjectPropertyAddress kDefaultOutputDeviceAddress = {
32   kAudioHardwarePropertyDefaultOutputDevice,
33   kAudioObjectPropertyScopeGlobal,
34   kAudioObjectPropertyElementMaster
35 };
36 
37 // Overview of operation:
38 // 1) An object of AUAudioOutputStream is created by the AudioManager
39 // factory: audio_man->MakeAudioStream().
40 // 2) Next some thread will call Open(), at that point the underlying
41 // default output Audio Unit is created and configured.
42 // 3) Then some thread will call Start(source).
43 // Then the Audio Unit is started which creates its own thread which
44 // periodically will call the source for more data as buffers are being
45 // consumed.
46 // 4) At some point some thread will call Stop(), which we handle by directly
47 // stopping the default output Audio Unit.
48 // 6) The same thread that called stop will call Close() where we cleanup
49 // and notify the audio manager, which likely will destroy this object.
50 
AUAudioOutputStream(AudioManagerMac * manager,const AudioParameters & params)51 AUAudioOutputStream::AUAudioOutputStream(
52     AudioManagerMac* manager, const AudioParameters& params)
53     : manager_(manager),
54       source_(NULL),
55       output_unit_(0),
56       output_device_id_(kAudioObjectUnknown),
57       volume_(1),
58       hardware_latency_frames_(0),
59       stopped_(false),
60       audio_bus_(AudioBus::Create(params)) {
61   // We must have a manager.
62   DCHECK(manager_);
63 
64   // A frame is one sample across all channels. In interleaved audio the per
65   // frame fields identify the set of n |channels|. In uncompressed audio, a
66   // packet is always one frame.
67   format_.mSampleRate = params.sample_rate();
68   format_.mFormatID = kAudioFormatLinearPCM;
69   format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
70                          kLinearPCMFormatFlagIsSignedInteger;
71   format_.mBitsPerChannel = params.bits_per_sample();
72   format_.mChannelsPerFrame = params.channels();
73   format_.mFramesPerPacket = 1;
74   format_.mBytesPerPacket = (format_.mBitsPerChannel * params.channels()) / 8;
75   format_.mBytesPerFrame = format_.mBytesPerPacket;
76   format_.mReserved = 0;
77 
78   DVLOG(1) << "Desired ouput format: " << format_;
79 
80   // Calculate the number of sample frames per callback.
81   number_of_frames_ = params.frames_per_buffer();
82   DVLOG(1) << "Number of frames per callback: " << number_of_frames_;
83 }
84 
~AUAudioOutputStream()85 AUAudioOutputStream::~AUAudioOutputStream() {
86 }
87 
Open()88 bool AUAudioOutputStream::Open() {
89   // Obtain the current input device selected by the user.
90   UInt32 size = sizeof(output_device_id_);
91   OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
92                                                &kDefaultOutputDeviceAddress,
93                                                0,
94                                                0,
95                                                &size,
96                                                &output_device_id_);
97   if (result != noErr || output_device_id_ == kAudioObjectUnknown) {
98     OSSTATUS_DLOG(ERROR, result)
99         << "Could not get default audio output device.";
100     return false;
101   }
102 
103   // Open and initialize the DefaultOutputUnit.
104   AudioComponent comp;
105   AudioComponentDescription desc;
106 
107   desc.componentType = kAudioUnitType_Output;
108   desc.componentSubType = kAudioUnitSubType_DefaultOutput;
109   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
110   desc.componentFlags = 0;
111   desc.componentFlagsMask = 0;
112   comp = AudioComponentFindNext(0, &desc);
113   if (!comp)
114     return false;
115 
116   result = AudioComponentInstanceNew(comp, &output_unit_);
117   if (result != noErr) {
118     OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
119     return false;
120   }
121 
122   result = AudioUnitInitialize(output_unit_);
123   if (result != noErr) {
124     OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
125     return false;
126   }
127 
128   hardware_latency_frames_ = GetHardwareLatency();
129 
130   return Configure();
131 }
132 
Configure()133 bool AUAudioOutputStream::Configure() {
134   // Set the render callback.
135   AURenderCallbackStruct input;
136   input.inputProc = InputProc;
137   input.inputProcRefCon = this;
138   OSStatus result = AudioUnitSetProperty(
139       output_unit_,
140       kAudioUnitProperty_SetRenderCallback,
141       kAudioUnitScope_Global,
142       0,
143       &input,
144       sizeof(input));
145   if (result != noErr) {
146     OSSTATUS_DLOG(ERROR, result)
147       << "AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed.";
148     return false;
149   }
150 
151   // Set the stream format.
152   result = AudioUnitSetProperty(
153       output_unit_,
154       kAudioUnitProperty_StreamFormat,
155       kAudioUnitScope_Input,
156       0,
157       &format_,
158       sizeof(format_));
159   if (result != noErr) {
160     OSSTATUS_DLOG(ERROR, result)
161         << "AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed.";
162     return false;
163   }
164 
165   // Set the buffer frame size.
166   // WARNING: Setting this value changes the frame size for all audio units in
167   // the current process.  It's imperative that the input and output frame sizes
168   // be the same as the frames_per_buffer() returned by
169   // GetDefaultOutputStreamParameters.
170   // See http://crbug.com/154352 for details.
171   const AudioParameters hw_params =
172       manager_->GetDefaultOutputStreamParameters();
173   if (number_of_frames_ != static_cast<size_t>(hw_params.frames_per_buffer())) {
174     DLOG(ERROR) << "Audio buffer size does not match hardware buffer size.";
175     return false;
176   }
177 
178   UInt32 buffer_size = number_of_frames_;
179   result = AudioUnitSetProperty(
180       output_unit_,
181       kAudioDevicePropertyBufferFrameSize,
182       kAudioUnitScope_Output,
183       0,
184       &buffer_size,
185       sizeof(buffer_size));
186   if (result != noErr) {
187     OSSTATUS_DLOG(ERROR, result)
188         << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
189     return false;
190   }
191 
192   return true;
193 }
194 
Close()195 void AUAudioOutputStream::Close() {
196   if (output_unit_)
197     AudioComponentInstanceDispose(output_unit_);
198 
199   // Inform the audio manager that we have been closed. This can cause our
200   // destruction.
201   manager_->ReleaseOutputStream(this);
202 }
203 
Start(AudioSourceCallback * callback)204 void AUAudioOutputStream::Start(AudioSourceCallback* callback) {
205   DCHECK(callback);
206   if (!output_unit_) {
207     DLOG(ERROR) << "Open() has not been called successfully";
208     return;
209   }
210 
211   stopped_ = false;
212   {
213     base::AutoLock auto_lock(source_lock_);
214     source_ = callback;
215   }
216 
217   AudioOutputUnitStart(output_unit_);
218 }
219 
Stop()220 void AUAudioOutputStream::Stop() {
221   if (stopped_)
222     return;
223 
224   AudioOutputUnitStop(output_unit_);
225 
226   base::AutoLock auto_lock(source_lock_);
227   source_ = NULL;
228   stopped_ = true;
229 }
230 
SetVolume(double volume)231 void AUAudioOutputStream::SetVolume(double volume) {
232   if (!output_unit_)
233     return;
234   volume_ = static_cast<float>(volume);
235 
236   // TODO(crogers): set volume property
237 }
238 
GetVolume(double * volume)239 void AUAudioOutputStream::GetVolume(double* volume) {
240   if (!output_unit_)
241     return;
242   *volume = volume_;
243 }
244 
245 // Pulls on our provider to get rendered audio stream.
246 // Note to future hackers of this function: Do not add locks here because this
247 // is running on a real-time thread (for low-latency).
Render(UInt32 number_of_frames,AudioBufferList * io_data,const AudioTimeStamp * output_time_stamp)248 OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames,
249                                      AudioBufferList* io_data,
250                                      const AudioTimeStamp* output_time_stamp) {
251   // Update the playout latency.
252   double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
253 
254   AudioBuffer& buffer = io_data->mBuffers[0];
255   uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
256   uint32 hardware_pending_bytes = static_cast<uint32>
257       ((playout_latency_frames + 0.5) * format_.mBytesPerFrame);
258 
259   // Unfortunately AUAudioInputStream and AUAudioOutputStream share the frame
260   // size set by kAudioDevicePropertyBufferFrameSize above on a per process
261   // basis.  What this means is that the |number_of_frames| value may be larger
262   // or smaller than the value set during Configure().  In this case either
263   // audio input or audio output will be broken, so just output silence.
264   // TODO(crogers): Figure out what can trigger a change in |number_of_frames|.
265   // See http://crbug.com/154352 for details.
266   if (number_of_frames != static_cast<UInt32>(audio_bus_->frames())) {
267     memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame);
268     return noErr;
269   }
270 
271   int frames_filled = 0;
272   {
273     // Render() shouldn't be called except between AudioOutputUnitStart() and
274     // AudioOutputUnitStop() calls, but crash reports have shown otherwise:
275     // http://crbug.com/178765.  We use |source_lock_| to prevent races and
276     // crashes in Render() when |source_| is cleared.
277     base::AutoLock auto_lock(source_lock_);
278     if (!source_) {
279       memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame);
280       return noErr;
281     }
282 
283     frames_filled = source_->OnMoreData(
284         audio_bus_.get(), AudioBuffersState(0, hardware_pending_bytes));
285   }
286 
287   // Note: If this ever changes to output raw float the data must be clipped and
288   // sanitized since it may come from an untrusted source such as NaCl.
289   audio_bus_->Scale(volume_);
290   audio_bus_->ToInterleaved(
291       frames_filled, format_.mBitsPerChannel / 8, audio_data);
292 
293   return noErr;
294 }
295 
296 // DefaultOutputUnit callback
InputProc(void * user_data,AudioUnitRenderActionFlags *,const AudioTimeStamp * output_time_stamp,UInt32,UInt32 number_of_frames,AudioBufferList * io_data)297 OSStatus AUAudioOutputStream::InputProc(void* user_data,
298                                         AudioUnitRenderActionFlags*,
299                                         const AudioTimeStamp* output_time_stamp,
300                                         UInt32,
301                                         UInt32 number_of_frames,
302                                         AudioBufferList* io_data) {
303   AUAudioOutputStream* audio_output =
304       static_cast<AUAudioOutputStream*>(user_data);
305   if (!audio_output)
306     return -1;
307 
308   return audio_output->Render(number_of_frames, io_data, output_time_stamp);
309 }
310 
HardwareSampleRate()311 int AUAudioOutputStream::HardwareSampleRate() {
312   // Determine the default output device's sample-rate.
313   AudioDeviceID device_id = kAudioObjectUnknown;
314   UInt32 info_size = sizeof(device_id);
315   OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
316                                                &kDefaultOutputDeviceAddress,
317                                                0,
318                                                0,
319                                                &info_size,
320                                                &device_id);
321   if (result != noErr || device_id == kAudioObjectUnknown) {
322     OSSTATUS_DLOG(WARNING, result)
323         << "Could not get default audio output device.";
324     return 0;
325   }
326 
327   Float64 nominal_sample_rate;
328   info_size = sizeof(nominal_sample_rate);
329 
330   AudioObjectPropertyAddress nominal_sample_rate_address = {
331       kAudioDevicePropertyNominalSampleRate,
332       kAudioObjectPropertyScopeGlobal,
333       kAudioObjectPropertyElementMaster
334   };
335   result = AudioObjectGetPropertyData(device_id,
336                                       &nominal_sample_rate_address,
337                                       0,
338                                       0,
339                                       &info_size,
340                                       &nominal_sample_rate);
341   if (result != noErr) {
342     OSSTATUS_DLOG(WARNING, result)
343         << "Could not get default sample rate for device: " << device_id;
344     return 0;
345   }
346 
347   return static_cast<int>(nominal_sample_rate);
348 }
349 
GetHardwareLatency()350 double AUAudioOutputStream::GetHardwareLatency() {
351   if (!output_unit_ || output_device_id_ == kAudioObjectUnknown) {
352     DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
353     return 0.0;
354   }
355 
356   // Get audio unit latency.
357   Float64 audio_unit_latency_sec = 0.0;
358   UInt32 size = sizeof(audio_unit_latency_sec);
359   OSStatus result = AudioUnitGetProperty(output_unit_,
360                                          kAudioUnitProperty_Latency,
361                                          kAudioUnitScope_Global,
362                                          0,
363                                          &audio_unit_latency_sec,
364                                          &size);
365   if (result != noErr) {
366     OSSTATUS_DLOG(WARNING, result) << "Could not get audio unit latency";
367     return 0.0;
368   }
369 
370   // Get output audio device latency.
371   AudioObjectPropertyAddress property_address = {
372     kAudioDevicePropertyLatency,
373     kAudioDevicePropertyScopeOutput,
374     kAudioObjectPropertyElementMaster
375   };
376   UInt32 device_latency_frames = 0;
377   size = sizeof(device_latency_frames);
378   result = AudioObjectGetPropertyData(output_device_id_,
379                                       &property_address,
380                                       0,
381                                       NULL,
382                                       &size,
383                                       &device_latency_frames);
384   if (result != noErr) {
385     OSSTATUS_DLOG(WARNING, result) << "Could not get audio unit latency";
386     return 0.0;
387   }
388 
389   return static_cast<double>((audio_unit_latency_sec *
390       format_.mSampleRate) + device_latency_frames);
391 }
392 
GetPlayoutLatency(const AudioTimeStamp * output_time_stamp)393 double AUAudioOutputStream::GetPlayoutLatency(
394     const AudioTimeStamp* output_time_stamp) {
395   // Ensure mHostTime is valid.
396   if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0)
397     return 0;
398 
399   // Get the delay between the moment getting the callback and the scheduled
400   // time stamp that tells when the data is going to be played out.
401   UInt64 output_time_ns = AudioConvertHostTimeToNanos(
402       output_time_stamp->mHostTime);
403   UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
404 
405   // Prevent overflow leading to huge delay information; occurs regularly on
406   // the bots, probably less so in the wild.
407   if (now_ns > output_time_ns)
408     return 0;
409 
410   double delay_frames = static_cast<double>
411       (1e-9 * (output_time_ns - now_ns) * format_.mSampleRate);
412 
413   return (delay_frames + hardware_latency_frames_);
414 }
415 
416 }  // namespace media
417