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
));
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
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
)
50 output_device_id_(kAudioObjectUnknown
),
52 hardware_latency_frames_(0) {
53 // We must have a 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
,
94 // Open and initialize the DefaultOutputUnit.
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
);
106 result
= OpenAComponent(comp
, &output_unit_
);
107 DCHECK_EQ(result
, 0);
111 result
= AudioUnitInitialize(output_unit_
);
113 DCHECK_EQ(result
, 0);
117 hardware_latency_frames_
= GetHardwareLatency();
122 bool AUAudioOutputStream::Configure() {
123 // Set the render callback.
124 AURenderCallbackStruct input
;
125 input
.inputProc
= InputProc
;
126 input
.inputProcRefCon
= this;
127 OSStatus result
= AudioUnitSetProperty(
129 kAudioUnitProperty_SetRenderCallback
,
130 kAudioUnitScope_Global
,
135 DCHECK_EQ(result
, 0);
139 // Set the stream format.
140 result
= AudioUnitSetProperty(
142 kAudioUnitProperty_StreamFormat
,
143 kAudioUnitScope_Input
,
147 DCHECK_EQ(result
, 0);
151 // Set the buffer frame size.
152 UInt32 buffer_size
= number_of_frames_
;
153 result
= AudioUnitSetProperty(
155 kAudioDevicePropertyBufferFrameSize
,
156 kAudioUnitScope_Output
,
159 sizeof(buffer_size
));
160 DCHECK_EQ(result
, 0);
167 void AUAudioOutputStream::Close() {
169 CloseComponent(output_unit_
);
171 // Inform the audio manager that we have been closed. This can cause our
173 manager_
->ReleaseOutputStream(this);
176 void AUAudioOutputStream::Start(AudioSourceCallback
* callback
) {
178 DLOG_IF(ERROR
, !output_unit_
) << "Open() has not been called successfully";
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.
192 AudioOutputUnitStop(output_unit_
);
195 void AUAudioOutputStream::SetVolume(double volume
) {
198 volume_
= static_cast<float>(volume
);
200 // TODO(crogers): set volume property
203 void AUAudioOutputStream::GetVolume(double* 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
);
240 // DefaultOutputUnit callback
241 OSStatus
AUAudioOutputStream::InputProc(void* user_data
,
242 AudioUnitRenderActionFlags
*,
243 const AudioTimeStamp
* output_time_stamp
,
245 UInt32 number_of_frames
,
246 AudioBufferList
* io_data
) {
247 AUAudioOutputStream
* audio_output
=
248 static_cast<AUAudioOutputStream
*>(user_data
);
249 DCHECK(audio_output
);
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
,
272 DCHECK_EQ(result
, 0);
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
,
289 &nominal_sample_rate
);
290 DCHECK_EQ(result
, 0);
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";
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
,
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_
,
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_
,
339 scoped_ptr_malloc
<AudioStreamID
>
340 streams(reinterpret_cast<AudioStreamID
*>(malloc(size
)));
341 AudioStreamID
* stream_ids
= streams
.get();
342 result
= AudioObjectGetPropertyData(output_device_id_
,
349 property_address
.mSelector
= kAudioStreamPropertyLatency
;
350 result
= AudioObjectGetPropertyData(stream_ids
[0],
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_
);