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