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