audio_input_mac.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_input_mac.h"
6
7#include "base/basictypes.h"
8#include "base/logging.h"
9#include "base/mac/mac_logging.h"
10#include "media/audio/audio_manager_base.h"
11#include "media/audio/audio_util.h"
12
13#if !defined(OS_IOS)
14#include <CoreServices/CoreServices.h>
15#endif
16
17namespace media {
18
19PCMQueueInAudioInputStream::PCMQueueInAudioInputStream(
20    AudioManagerBase* manager, const AudioParameters& params)
21    : manager_(manager),
22      callback_(NULL),
23      audio_queue_(NULL),
24      buffer_size_bytes_(0),
25      started_(false) {
26  // We must have a manager.
27  DCHECK(manager_);
28  // A frame is one sample across all channels. In interleaved audio the per
29  // frame fields identify the set of n |channels|. In uncompressed audio, a
30  // packet is always one frame.
31  format_.mSampleRate = params.sample_rate();
32  format_.mFormatID = kAudioFormatLinearPCM;
33  format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
34                         kLinearPCMFormatFlagIsSignedInteger;
35  format_.mBitsPerChannel = params.bits_per_sample();
36  format_.mChannelsPerFrame = params.channels();
37  format_.mFramesPerPacket = 1;
38  format_.mBytesPerPacket = (params.bits_per_sample() * params.channels()) / 8;
39  format_.mBytesPerFrame = format_.mBytesPerPacket;
40  format_.mReserved = 0;
41
42  buffer_size_bytes_ = params.GetBytesPerBuffer();
43}
44
45PCMQueueInAudioInputStream::~PCMQueueInAudioInputStream() {
46  DCHECK(!callback_);
47  DCHECK(!audio_queue_);
48}
49
50bool PCMQueueInAudioInputStream::Open() {
51  OSStatus err = AudioQueueNewInput(&format_,
52                                    &HandleInputBufferStatic,
53                                    this,
54                                    NULL,  // Use OS CFRunLoop for |callback|
55                                    kCFRunLoopCommonModes,
56                                    0,  // Reserved
57                                    &audio_queue_);
58  if (err != noErr) {
59    HandleError(err);
60    return false;
61  }
62  return SetupBuffers();
63}
64
65void PCMQueueInAudioInputStream::Start(AudioInputCallback* callback) {
66  DCHECK(callback);
67  DLOG_IF(ERROR, !audio_queue_) << "Open() has not been called successfully";
68  if (callback_ || !audio_queue_)
69    return;
70  callback_ = callback;
71  OSStatus err = AudioQueueStart(audio_queue_, NULL);
72  if (err != noErr) {
73    HandleError(err);
74  } else {
75    started_ = true;
76  }
77}
78
79void PCMQueueInAudioInputStream::Stop() {
80  if (!audio_queue_ || !started_)
81    return;
82
83  // We request a synchronous stop, so the next call can take some time. In
84  // the windows implementation we block here as well.
85  OSStatus err = AudioQueueStop(audio_queue_, true);
86  if (err != noErr)
87    HandleError(err);
88
89  started_ = false;
90}
91
92void PCMQueueInAudioInputStream::Close() {
93  // It is valid to call Close() before calling Open() or Start(), thus
94  // |audio_queue_| and |callback_| might be NULL.
95  if (audio_queue_) {
96    OSStatus err = AudioQueueDispose(audio_queue_, true);
97    audio_queue_ = NULL;
98    if (err != noErr)
99      HandleError(err);
100  }
101  if (callback_) {
102    callback_->OnClose(this);
103    callback_ = NULL;
104  }
105  manager_->ReleaseInputStream(this);
106  // CARE: This object may now be destroyed.
107}
108
109double PCMQueueInAudioInputStream::GetMaxVolume() {
110  NOTREACHED() << "Only supported for low-latency mode.";
111  return 0.0;
112}
113
114void PCMQueueInAudioInputStream::SetVolume(double volume) {
115  NOTREACHED() << "Only supported for low-latency mode.";
116}
117
118double PCMQueueInAudioInputStream::GetVolume() {
119  NOTREACHED() << "Only supported for low-latency mode.";
120  return 0.0;
121}
122
123void PCMQueueInAudioInputStream::SetAutomaticGainControl(bool enabled) {
124  NOTREACHED() << "Only supported for low-latency mode.";
125}
126
127bool PCMQueueInAudioInputStream::GetAutomaticGainControl() {
128  NOTREACHED() << "Only supported for low-latency mode.";
129  return false;
130}
131
132void PCMQueueInAudioInputStream::HandleError(OSStatus err) {
133  if (callback_)
134    callback_->OnError(this);
135  // This point should never be reached.
136  OSSTATUS_DCHECK(0, err);
137}
138
139bool PCMQueueInAudioInputStream::SetupBuffers() {
140  DCHECK(buffer_size_bytes_);
141  for (int i = 0; i < kNumberBuffers; ++i) {
142    AudioQueueBufferRef buffer;
143    OSStatus err = AudioQueueAllocateBuffer(audio_queue_,
144                                            buffer_size_bytes_,
145                                            &buffer);
146    if (err == noErr)
147      err = QueueNextBuffer(buffer);
148    if (err != noErr) {
149      HandleError(err);
150      return false;
151    }
152    // |buffer| will automatically be freed when |audio_queue_| is released.
153  }
154  return true;
155}
156
157OSStatus PCMQueueInAudioInputStream::QueueNextBuffer(
158    AudioQueueBufferRef audio_buffer) {
159  // Only the first 2 params are needed for recording.
160  return AudioQueueEnqueueBuffer(audio_queue_, audio_buffer, 0, NULL);
161}
162
163// static
164void PCMQueueInAudioInputStream::HandleInputBufferStatic(
165    void* data,
166    AudioQueueRef audio_queue,
167    AudioQueueBufferRef audio_buffer,
168    const AudioTimeStamp* start_time,
169    UInt32 num_packets,
170    const AudioStreamPacketDescription* desc) {
171  reinterpret_cast<PCMQueueInAudioInputStream*>(data)->
172      HandleInputBuffer(audio_queue, audio_buffer, start_time,
173                        num_packets, desc);
174}
175
176void PCMQueueInAudioInputStream::HandleInputBuffer(
177    AudioQueueRef audio_queue,
178    AudioQueueBufferRef audio_buffer,
179    const AudioTimeStamp* start_time,
180    UInt32 num_packets,
181    const AudioStreamPacketDescription* packet_desc) {
182  DCHECK_EQ(audio_queue_, audio_queue);
183  DCHECK(audio_buffer->mAudioData);
184  if (!callback_) {
185    // This can happen if Stop() was called without start.
186    DCHECK_EQ(0U, audio_buffer->mAudioDataByteSize);
187    return;
188  }
189
190  if (audio_buffer->mAudioDataByteSize) {
191    // The AudioQueue API may use a large internal buffer and repeatedly call us
192    // back to back once that internal buffer is filled.  When this happens the
193    // renderer client does not have enough time to read data back from the
194    // shared memory before the next write comes along.  If HandleInputBuffer()
195    // is called too frequently, Sleep() at least 5ms to ensure the shared
196    // memory doesn't get trampled.
197    // TODO(dalecurtis): This is a HACK.  Long term the AudioQueue path is going
198    // away in favor of the AudioUnit based AUAudioInputStream().  Tracked by
199    // http://crbug.com/161383.
200    base::TimeDelta elapsed = base::Time::Now() - last_fill_;
201    const base::TimeDelta kMinDelay = base::TimeDelta::FromMilliseconds(5);
202    if (elapsed < kMinDelay)
203      base::PlatformThread::Sleep(kMinDelay - elapsed);
204
205    callback_->OnData(this,
206                      reinterpret_cast<const uint8*>(audio_buffer->mAudioData),
207                      audio_buffer->mAudioDataByteSize,
208                      audio_buffer->mAudioDataByteSize,
209                      0.0);
210
211    last_fill_ = base::Time::Now();
212  }
213  // Recycle the buffer.
214  OSStatus err = QueueNextBuffer(audio_buffer);
215  if (err != noErr) {
216    if (err == kAudioQueueErr_EnqueueDuringReset) {
217      // This is the error you get if you try to enqueue a buffer and the
218      // queue has been closed. Not really a problem if indeed the queue
219      // has been closed.
220      // TODO(joth): PCMQueueOutAudioOutputStream uses callback_ to provide an
221      // extra guard for this situation, but it seems to introduce more
222      // complications than it solves (memory barrier issues accessing it from
223      // multiple threads, looses the means to indicate OnClosed to client).
224      // Should determine if we need to do something equivalent here.
225      return;
226    }
227    HandleError(err);
228  }
229}
230
231}  // namespace media
232