srpcgen: Use 'const char*' for string parameters
[chromium-blink-merge.git] / media / audio / mac / audio_low_latency_output_mac.cc
bloba304c64e26f822121ed8e6039c0080afd0584083
1 // Copyright (c) 2011 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.
5 #include "media/audio/mac/audio_low_latency_output_mac.h"
7 #include <CoreServices/CoreServices.h>
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "media/audio/audio_util.h"
12 #include "media/audio/mac/audio_manager_mac.h"
14 // Reorder PCM from AAC layout to Core Audio 5.1 layout.
15 // TODO(fbarchard): Switch layout when ffmpeg is updated.
16 template<class Format>
17 static void SwizzleCoreAudioLayout5_1(Format* b, uint32 filled) {
18 static const int kNumSurroundChannels = 6;
19 Format aac[kNumSurroundChannels];
20 for (uint32 i = 0; i < filled; i += sizeof(aac), b += kNumSurroundChannels) {
21 memcpy(aac, b, sizeof(aac));
22 b[0] = aac[1]; // L
23 b[1] = aac[2]; // R
24 b[2] = aac[0]; // C
25 b[3] = aac[5]; // LFE
26 b[4] = aac[3]; // Ls
27 b[5] = aac[4]; // Rs
31 // Overview of operation:
32 // 1) An object of AUAudioOutputStream is created by the AudioManager
33 // factory: audio_man->MakeAudioStream().
34 // 2) Next some thread will call Open(), at that point the underlying
35 // default output Audio Unit is created and configured.
36 // 3) Then some thread will call Start(source).
37 // Then the Audio Unit is started which creates its own thread which
38 // periodically will call the source for more data as buffers are being
39 // consumed.
40 // 4) At some point some thread will call Stop(), which we handle by directly
41 // stopping the default output Audio Unit.
42 // 6) The same thread that called stop will call Close() where we cleanup
43 // and notify the audio manager, which likely will destroy this object.
45 AUAudioOutputStream::AUAudioOutputStream(
46 AudioManagerMac* manager, const AudioParameters& params)
47 : manager_(manager),
48 source_(NULL),
49 output_unit_(0),
50 output_device_id_(kAudioObjectUnknown),
51 volume_(1),
52 hardware_latency_frames_(0) {
53 // We must have a manager.
54 DCHECK(manager_);
55 // A frame is one sample across all channels. In interleaved audio the per
56 // frame fields identify the set of n |channels|. In uncompressed audio, a
57 // packet is always one frame.
58 format_.mSampleRate = params.sample_rate;
59 format_.mFormatID = kAudioFormatLinearPCM;
60 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
61 kLinearPCMFormatFlagIsSignedInteger;
62 format_.mBitsPerChannel = params.bits_per_sample;
63 format_.mChannelsPerFrame = params.channels;
64 format_.mFramesPerPacket = 1;
65 format_.mBytesPerPacket = (format_.mBitsPerChannel * params.channels) / 8;
66 format_.mBytesPerFrame = format_.mBytesPerPacket;
67 format_.mReserved = 0;
69 // Calculate the number of sample frames per callback.
70 number_of_frames_ = params.GetPacketSize() / format_.mBytesPerPacket;
73 AUAudioOutputStream::~AUAudioOutputStream() {
76 bool AUAudioOutputStream::Open() {
77 // Obtain the current input device selected by the user.
78 UInt32 size = sizeof(output_device_id_);
79 AudioObjectPropertyAddress default_output_device_address = {
80 kAudioHardwarePropertyDefaultOutputDevice,
81 kAudioObjectPropertyScopeGlobal,
82 kAudioObjectPropertyElementMaster
84 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
85 &default_output_device_address,
88 &size,
89 &output_device_id_);
90 DCHECK_EQ(result, 0);
91 if (result)
92 return false;
94 // Open and initialize the DefaultOutputUnit.
95 Component comp;
96 ComponentDescription desc;
98 desc.componentType = kAudioUnitType_Output;
99 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
100 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
101 desc.componentFlags = 0;
102 desc.componentFlagsMask = 0;
103 comp = FindNextComponent(0, &desc);
104 DCHECK(comp);
106 result = OpenAComponent(comp, &output_unit_);
107 DCHECK_EQ(result, 0);
108 if (result)
109 return false;
111 result = AudioUnitInitialize(output_unit_);
113 DCHECK_EQ(result, 0);
114 if (result)
115 return false;
117 hardware_latency_frames_ = GetHardwareLatency();
119 return Configure();
122 bool AUAudioOutputStream::Configure() {
123 // Set the render callback.
124 AURenderCallbackStruct input;
125 input.inputProc = InputProc;
126 input.inputProcRefCon = this;
127 OSStatus result = AudioUnitSetProperty(
128 output_unit_,
129 kAudioUnitProperty_SetRenderCallback,
130 kAudioUnitScope_Global,
132 &input,
133 sizeof(input));
135 DCHECK_EQ(result, 0);
136 if (result)
137 return false;
139 // Set the stream format.
140 result = AudioUnitSetProperty(
141 output_unit_,
142 kAudioUnitProperty_StreamFormat,
143 kAudioUnitScope_Input,
145 &format_,
146 sizeof(format_));
147 DCHECK_EQ(result, 0);
148 if (result)
149 return false;
151 // Set the buffer frame size.
152 UInt32 buffer_size = number_of_frames_;
153 result = AudioUnitSetProperty(
154 output_unit_,
155 kAudioDevicePropertyBufferFrameSize,
156 kAudioUnitScope_Output,
158 &buffer_size,
159 sizeof(buffer_size));
160 DCHECK_EQ(result, 0);
161 if (result)
162 return false;
164 return true;
167 void AUAudioOutputStream::Close() {
168 if (output_unit_)
169 CloseComponent(output_unit_);
171 // Inform the audio manager that we have been closed. This can cause our
172 // destruction.
173 manager_->ReleaseOutputStream(this);
176 void AUAudioOutputStream::Start(AudioSourceCallback* callback) {
177 DCHECK(callback);
178 DLOG_IF(ERROR, !output_unit_) << "Open() has not been called successfully";
179 if (!output_unit_)
180 return;
182 source_ = callback;
184 AudioOutputUnitStart(output_unit_);
187 void AUAudioOutputStream::Stop() {
188 // We request a synchronous stop, so the next call can take some time. In
189 // the windows implementation we block here as well.
190 source_ = NULL;
192 AudioOutputUnitStop(output_unit_);
195 void AUAudioOutputStream::SetVolume(double volume) {
196 if (!output_unit_)
197 return;
198 volume_ = static_cast<float>(volume);
200 // TODO(crogers): set volume property
203 void AUAudioOutputStream::GetVolume(double* volume) {
204 if (!output_unit_)
205 return;
206 *volume = volume_;
209 // Pulls on our provider to get rendered audio stream.
210 // Note to future hackers of this function: Do not add locks here because this
211 // is running on a real-time thread (for low-latency).
212 OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames,
213 AudioBufferList* io_data,
214 const AudioTimeStamp* output_time_stamp) {
215 // Update the playout latency.
216 double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
218 AudioBuffer& buffer = io_data->mBuffers[0];
219 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
220 uint32 hardware_pending_bytes = static_cast<uint32>
221 ((playout_latency_frames + 0.5) * format_.mBytesPerFrame);
222 uint32 filled = source_->OnMoreData(
223 this, audio_data, buffer.mDataByteSize,
224 AudioBuffersState(0, hardware_pending_bytes));
226 // Handle channel order for 5.1 audio.
227 if (format_.mChannelsPerFrame == 6) {
228 if (format_.mBitsPerChannel == 8) {
229 SwizzleCoreAudioLayout5_1(reinterpret_cast<uint8*>(audio_data), filled);
230 } else if (format_.mBitsPerChannel == 16) {
231 SwizzleCoreAudioLayout5_1(reinterpret_cast<int16*>(audio_data), filled);
232 } else if (format_.mBitsPerChannel == 32) {
233 SwizzleCoreAudioLayout5_1(reinterpret_cast<int32*>(audio_data), filled);
237 return noErr;
240 // DefaultOutputUnit callback
241 OSStatus AUAudioOutputStream::InputProc(void* user_data,
242 AudioUnitRenderActionFlags*,
243 const AudioTimeStamp* output_time_stamp,
244 UInt32,
245 UInt32 number_of_frames,
246 AudioBufferList* io_data) {
247 AUAudioOutputStream* audio_output =
248 static_cast<AUAudioOutputStream*>(user_data);
249 DCHECK(audio_output);
250 if (!audio_output)
251 return -1;
253 return audio_output->Render(number_of_frames, io_data, output_time_stamp);
256 double AUAudioOutputStream::HardwareSampleRate() {
257 // Determine the default output device's sample-rate.
258 AudioDeviceID device_id = kAudioObjectUnknown;
259 UInt32 info_size = sizeof(device_id);
261 AudioObjectPropertyAddress default_output_device_address = {
262 kAudioHardwarePropertyDefaultOutputDevice,
263 kAudioObjectPropertyScopeGlobal,
264 kAudioObjectPropertyElementMaster
266 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
267 &default_output_device_address,
270 &info_size,
271 &device_id);
272 DCHECK_EQ(result, 0);
273 if (result)
274 return 0.0; // error
276 Float64 nominal_sample_rate;
277 info_size = sizeof(nominal_sample_rate);
279 AudioObjectPropertyAddress nominal_sample_rate_address = {
280 kAudioDevicePropertyNominalSampleRate,
281 kAudioObjectPropertyScopeGlobal,
282 kAudioObjectPropertyElementMaster
284 result = AudioObjectGetPropertyData(device_id,
285 &nominal_sample_rate_address,
288 &info_size,
289 &nominal_sample_rate);
290 DCHECK_EQ(result, 0);
291 if (result)
292 return 0.0; // error
294 return nominal_sample_rate;
297 double AUAudioOutputStream::GetHardwareLatency() {
298 if (!output_unit_ || output_device_id_ == kAudioObjectUnknown) {
299 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
300 return 0.0;
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(output_unit_,
307 kAudioUnitProperty_Latency,
308 kAudioUnitScope_Global,
310 &audio_unit_latency_sec,
311 &size);
312 DLOG_IF(WARNING, result != noErr) << "Could not get audio unit latency.";
314 // Get output audio device latency.
315 AudioObjectPropertyAddress property_address = {
316 kAudioDevicePropertyLatency,
317 kAudioDevicePropertyScopeOutput,
318 kAudioObjectPropertyElementMaster
320 UInt32 device_latency_frames = 0;
321 size = sizeof(device_latency_frames);
322 result = AudioObjectGetPropertyData(output_device_id_,
323 &property_address,
325 NULL,
326 &size,
327 &device_latency_frames);
328 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
330 // Get the stream latency.
331 property_address.mSelector = kAudioDevicePropertyStreams;
332 UInt32 stream_latency_frames = 0;
333 result = AudioObjectGetPropertyDataSize(output_device_id_,
334 &property_address,
336 NULL,
337 &size);
338 if (!result) {
339 scoped_ptr_malloc<AudioStreamID>
340 streams(reinterpret_cast<AudioStreamID*>(malloc(size)));
341 AudioStreamID* stream_ids = streams.get();
342 result = AudioObjectGetPropertyData(output_device_id_,
343 &property_address,
345 NULL,
346 &size,
347 stream_ids);
348 if (!result) {
349 property_address.mSelector = kAudioStreamPropertyLatency;
350 result = AudioObjectGetPropertyData(stream_ids[0],
351 &property_address,
353 NULL,
354 &size,
355 &stream_latency_frames);
358 DLOG_IF(WARNING, result != noErr) << "Could not get audio stream latency.";
360 return static_cast<double>((audio_unit_latency_sec *
361 format_.mSampleRate) + device_latency_frames + stream_latency_frames);
364 double AUAudioOutputStream::GetPlayoutLatency(
365 const AudioTimeStamp* output_time_stamp) {
366 // Get the delay between the moment getting the callback and the scheduled
367 // time stamp that tells when the data is going to be played out.
368 UInt64 output_time_ns = AudioConvertHostTimeToNanos(
369 output_time_stamp->mHostTime);
370 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
371 double delay_frames = static_cast<double>
372 (1e-9 * (output_time_ns - now_ns) * format_.mSampleRate);
374 return (delay_frames + hardware_latency_frames_);