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