audio_input_mac.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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    manager_->IncreaseActiveInputStreamCount();
77  }
78}
79
80void PCMQueueInAudioInputStream::Stop() {
81  if (!audio_queue_ || !started_)
82    return;
83
84  // Stop is always called before Close. In case of error, this will be
85  // also called when closing the input controller.
86  manager_->DecreaseActiveInputStreamCount();
87
88  // We request a synchronous stop, so the next call can take some time. In
89  // the windows implementation we block here as well.
90  OSStatus err = AudioQueueStop(audio_queue_, true);
91  if (err != noErr)
92    HandleError(err);
93
94  started_ = false;
95}
96
97void PCMQueueInAudioInputStream::Close() {
98  // It is valid to call Close() before calling Open() or Start(), thus
99  // |audio_queue_| and |callback_| might be NULL.
100  if (audio_queue_) {
101    OSStatus err = AudioQueueDispose(audio_queue_, true);
102    audio_queue_ = NULL;
103    if (err != noErr)
104      HandleError(err);
105  }
106  if (callback_) {
107    callback_->OnClose(this);
108    callback_ = NULL;
109  }
110  manager_->ReleaseInputStream(this);
111  // CARE: This object may now be destroyed.
112}
113
114double PCMQueueInAudioInputStream::GetMaxVolume() {
115  NOTREACHED() << "Only supported for low-latency mode.";
116  return 0.0;
117}
118
119void PCMQueueInAudioInputStream::SetVolume(double volume) {
120  NOTREACHED() << "Only supported for low-latency mode.";
121}
122
123double PCMQueueInAudioInputStream::GetVolume() {
124  NOTREACHED() << "Only supported for low-latency mode.";
125  return 0.0;
126}
127
128void PCMQueueInAudioInputStream::SetAutomaticGainControl(bool enabled) {
129  NOTREACHED() << "Only supported for low-latency mode.";
130}
131
132bool PCMQueueInAudioInputStream::GetAutomaticGainControl() {
133  NOTREACHED() << "Only supported for low-latency mode.";
134  return false;
135}
136
137void PCMQueueInAudioInputStream::HandleError(OSStatus err) {
138  if (callback_)
139    callback_->OnError(this, static_cast<int>(err));
140  // This point should never be reached.
141  OSSTATUS_DCHECK(0, err);
142}
143
144bool PCMQueueInAudioInputStream::SetupBuffers() {
145  DCHECK(buffer_size_bytes_);
146  for (int i = 0; i < kNumberBuffers; ++i) {
147    AudioQueueBufferRef buffer;
148    OSStatus err = AudioQueueAllocateBuffer(audio_queue_,
149                                            buffer_size_bytes_,
150                                            &buffer);
151    if (err == noErr)
152      err = QueueNextBuffer(buffer);
153    if (err != noErr) {
154      HandleError(err);
155      return false;
156    }
157    // |buffer| will automatically be freed when |audio_queue_| is released.
158  }
159  return true;
160}
161
162OSStatus PCMQueueInAudioInputStream::QueueNextBuffer(
163    AudioQueueBufferRef audio_buffer) {
164  // Only the first 2 params are needed for recording.
165  return AudioQueueEnqueueBuffer(audio_queue_, audio_buffer, 0, NULL);
166}
167
168// static
169void PCMQueueInAudioInputStream::HandleInputBufferStatic(
170    void* data,
171    AudioQueueRef audio_queue,
172    AudioQueueBufferRef audio_buffer,
173    const AudioTimeStamp* start_time,
174    UInt32 num_packets,
175    const AudioStreamPacketDescription* desc) {
176  reinterpret_cast<PCMQueueInAudioInputStream*>(data)->
177      HandleInputBuffer(audio_queue, audio_buffer, start_time,
178                        num_packets, desc);
179}
180
181void PCMQueueInAudioInputStream::HandleInputBuffer(
182    AudioQueueRef audio_queue,
183    AudioQueueBufferRef audio_buffer,
184    const AudioTimeStamp* start_time,
185    UInt32 num_packets,
186    const AudioStreamPacketDescription* packet_desc) {
187  DCHECK_EQ(audio_queue_, audio_queue);
188  DCHECK(audio_buffer->mAudioData);
189  if (!callback_) {
190    // This can happen if Stop() was called without start.
191    DCHECK_EQ(0U, audio_buffer->mAudioDataByteSize);
192    return;
193  }
194
195  if (audio_buffer->mAudioDataByteSize)
196    callback_->OnData(this,
197                      reinterpret_cast<const uint8*>(audio_buffer->mAudioData),
198                      audio_buffer->mAudioDataByteSize,
199                      audio_buffer->mAudioDataByteSize,
200                      0.0);
201  // Recycle the buffer.
202  OSStatus err = QueueNextBuffer(audio_buffer);
203  if (err != noErr) {
204    if (err == kAudioQueueErr_EnqueueDuringReset) {
205      // This is the error you get if you try to enqueue a buffer and the
206      // queue has been closed. Not really a problem if indeed the queue
207      // has been closed.
208      // TODO(joth): PCMQueueOutAudioOutputStream uses callback_ to provide an
209      // extra guard for this situation, but it seems to introduce more
210      // complications than it solves (memory barrier issues accessing it from
211      // multiple threads, looses the means to indicate OnClosed to client).
212      // Should determine if we need to do something equivalent here.
213      return;
214    }
215    HandleError(err);
216  }
217}
218
219}  // namespace media
220