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