audio_input_device.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/audio_input_device.h" 6 7#include "base/basictypes.h" 8#include "base/bind.h" 9#include "base/message_loop.h" 10#include "base/threading/thread_restrictions.h" 11#include "base/time.h" 12#include "media/audio/audio_manager_base.h" 13#include "media/base/audio_bus.h" 14 15namespace media { 16 17// The number of shared memory buffer segments indicated to browser process 18// in order to avoid data overwriting. This number can be any positive number, 19// dependent how fast the renderer process can pick up captured data from 20// shared memory. 21static const int kRequestedSharedMemoryCount = 10; 22 23// Takes care of invoking the capture callback on the audio thread. 24// An instance of this class is created for each capture stream in 25// OnLowLatencyCreated(). 26class AudioInputDevice::AudioThreadCallback 27 : public AudioDeviceThread::Callback { 28 public: 29 AudioThreadCallback(const AudioParameters& audio_parameters, 30 base::SharedMemoryHandle memory, 31 int memory_length, 32 int total_segments, 33 CaptureCallback* capture_callback); 34 virtual ~AudioThreadCallback(); 35 36 virtual void MapSharedMemory() OVERRIDE; 37 38 // Called whenever we receive notifications about pending data. 39 virtual void Process(int pending_data) OVERRIDE; 40 41 private: 42 int current_segment_id_; 43 CaptureCallback* capture_callback_; 44 scoped_ptr<AudioBus> audio_bus_; 45 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); 46}; 47 48AudioInputDevice::AudioInputDevice( 49 scoped_ptr<AudioInputIPC> ipc, 50 const scoped_refptr<base::MessageLoopProxy>& io_loop) 51 : ScopedLoopObserver(io_loop), 52 callback_(NULL), 53 ipc_(ipc.Pass()), 54 state_(IDLE), 55 session_id_(0), 56 agc_is_enabled_(false), 57 stopping_hack_(false) { 58 CHECK(ipc_); 59 60 // The correctness of the code depends on the relative values assigned in the 61 // State enum. 62 COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0); 63 COMPILE_ASSERT(IDLE < CREATING_STREAM, invalid_enum_value_assignment_1); 64 COMPILE_ASSERT(CREATING_STREAM < RECORDING, invalid_enum_value_assignment_2); 65} 66 67void AudioInputDevice::Initialize(const AudioParameters& params, 68 CaptureCallback* callback, 69 int session_id) { 70 DCHECK(params.IsValid()); 71 DCHECK(!callback_); 72 DCHECK_EQ(0, session_id_); 73 audio_parameters_ = params; 74 callback_ = callback; 75 session_id_ = session_id; 76} 77 78void AudioInputDevice::Start() { 79 DCHECK(callback_) << "Initialize hasn't been called"; 80 DVLOG(1) << "Start()"; 81 message_loop()->PostTask(FROM_HERE, 82 base::Bind(&AudioInputDevice::StartUpOnIOThread, this)); 83} 84 85void AudioInputDevice::Stop() { 86 DVLOG(1) << "Stop()"; 87 88 { 89 base::AutoLock auto_lock(audio_thread_lock_); 90 audio_thread_.Stop(base::MessageLoop::current()); 91 stopping_hack_ = true; 92 } 93 94 message_loop()->PostTask(FROM_HERE, 95 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); 96} 97 98void AudioInputDevice::SetVolume(double volume) { 99 if (volume < 0 || volume > 1.0) { 100 DLOG(ERROR) << "Invalid volume value specified"; 101 return; 102 } 103 104 message_loop()->PostTask(FROM_HERE, 105 base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume)); 106} 107 108void AudioInputDevice::SetAutomaticGainControl(bool enabled) { 109 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; 110 message_loop()->PostTask(FROM_HERE, 111 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, 112 this, enabled)); 113} 114 115void AudioInputDevice::OnStreamCreated( 116 base::SharedMemoryHandle handle, 117 base::SyncSocket::Handle socket_handle, 118 int length, 119 int total_segments) { 120 DCHECK(message_loop()->BelongsToCurrentThread()); 121#if defined(OS_WIN) 122 DCHECK(handle); 123 DCHECK(socket_handle); 124#else 125 DCHECK_GE(handle.fd, 0); 126 DCHECK_GE(socket_handle, 0); 127#endif 128 DCHECK_GT(length, 0); 129 130 if (state_ != CREATING_STREAM) 131 return; 132 133 base::AutoLock auto_lock(audio_thread_lock_); 134 // TODO(miu): See TODO in OnStreamCreated method for AudioOutputDevice. 135 // Interface changes need to be made; likely, after AudioInputDevice is merged 136 // into AudioOutputDevice (http://crbug.com/179597). 137 if (stopping_hack_) 138 return; 139 140 DCHECK(audio_thread_.IsStopped()); 141 audio_callback_.reset( 142 new AudioInputDevice::AudioThreadCallback( 143 audio_parameters_, handle, length, total_segments, callback_)); 144 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); 145 146 state_ = RECORDING; 147 ipc_->RecordStream(); 148} 149 150void AudioInputDevice::OnVolume(double volume) { 151 NOTIMPLEMENTED(); 152} 153 154void AudioInputDevice::OnStateChanged( 155 AudioInputIPCDelegate::State state) { 156 DCHECK(message_loop()->BelongsToCurrentThread()); 157 158 // Do nothing if the stream has been closed. 159 if (state_ < CREATING_STREAM) 160 return; 161 162 // TODO(miu): Clean-up inconsistent and incomplete handling here. 163 // http://crbug.com/180640 164 switch (state) { 165 case AudioInputIPCDelegate::kStopped: 166 ShutDownOnIOThread(); 167 break; 168 case AudioInputIPCDelegate::kRecording: 169 NOTIMPLEMENTED(); 170 break; 171 case AudioInputIPCDelegate::kError: 172 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; 173 // Don't dereference the callback object if the audio thread 174 // is stopped or stopping. That could mean that the callback 175 // object has been deleted. 176 // TODO(tommi): Add an explicit contract for clearing the callback 177 // object. Possibly require calling Initialize again or provide 178 // a callback object via Start() and clear it in Stop(). 179 if (!audio_thread_.IsStopped()) 180 callback_->OnCaptureError(); 181 break; 182 default: 183 NOTREACHED(); 184 break; 185 } 186} 187 188void AudioInputDevice::OnIPCClosed() { 189 DCHECK(message_loop()->BelongsToCurrentThread()); 190 state_ = IPC_CLOSED; 191 ipc_.reset(); 192} 193 194AudioInputDevice::~AudioInputDevice() { 195 // TODO(henrika): The current design requires that the user calls 196 // Stop before deleting this class. 197 DCHECK(audio_thread_.IsStopped()); 198} 199 200void AudioInputDevice::StartUpOnIOThread() { 201 DCHECK(message_loop()->BelongsToCurrentThread()); 202 203 // Make sure we don't call Start() more than once. 204 if (state_ != IDLE) 205 return; 206 207 if (session_id_ <= 0) { 208 DLOG(WARNING) << "Invalid session id for the input stream " << session_id_; 209 return; 210 } 211 212 state_ = CREATING_STREAM; 213 ipc_->CreateStream(this, session_id_, audio_parameters_, 214 agc_is_enabled_, kRequestedSharedMemoryCount); 215} 216 217void AudioInputDevice::ShutDownOnIOThread() { 218 DCHECK(message_loop()->BelongsToCurrentThread()); 219 220 // Close the stream, if we haven't already. 221 if (state_ >= CREATING_STREAM) { 222 ipc_->CloseStream(); 223 state_ = IDLE; 224 agc_is_enabled_ = false; 225 } 226 227 // We can run into an issue where ShutDownOnIOThread is called right after 228 // OnStreamCreated is called in cases where Start/Stop are called before we 229 // get the OnStreamCreated callback. To handle that corner case, we call 230 // Stop(). In most cases, the thread will already be stopped. 231 // 232 // Another situation is when the IO thread goes away before Stop() is called 233 // in which case, we cannot use the message loop to close the thread handle 234 // and can't not rely on the main thread existing either. 235 base::AutoLock auto_lock_(audio_thread_lock_); 236 base::ThreadRestrictions::ScopedAllowIO allow_io; 237 audio_thread_.Stop(NULL); 238 audio_callback_.reset(); 239 stopping_hack_ = false; 240} 241 242void AudioInputDevice::SetVolumeOnIOThread(double volume) { 243 DCHECK(message_loop()->BelongsToCurrentThread()); 244 if (state_ >= CREATING_STREAM) 245 ipc_->SetVolume(volume); 246} 247 248void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { 249 DCHECK(message_loop()->BelongsToCurrentThread()); 250 251 if (state_ >= CREATING_STREAM) { 252 DLOG(WARNING) << "The AGC state can not be modified after starting."; 253 return; 254 } 255 256 // We simply store the new AGC setting here. This value will be used when 257 // a new stream is initialized and by GetAutomaticGainControl(). 258 agc_is_enabled_ = enabled; 259} 260 261void AudioInputDevice::WillDestroyCurrentMessageLoop() { 262 LOG(ERROR) << "IO loop going away before the input device has been stopped"; 263 ShutDownOnIOThread(); 264} 265 266// AudioInputDevice::AudioThreadCallback 267AudioInputDevice::AudioThreadCallback::AudioThreadCallback( 268 const AudioParameters& audio_parameters, 269 base::SharedMemoryHandle memory, 270 int memory_length, 271 int total_segments, 272 CaptureCallback* capture_callback) 273 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 274 total_segments), 275 current_segment_id_(0), 276 capture_callback_(capture_callback) { 277 audio_bus_ = AudioBus::Create(audio_parameters_); 278} 279 280AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { 281} 282 283void AudioInputDevice::AudioThreadCallback::MapSharedMemory() { 284 shared_memory_.Map(memory_length_); 285} 286 287void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { 288 // The shared memory represents parameters, size of the data buffer and the 289 // actual data buffer containing audio data. Map the memory into this 290 // structure and parse out parameters and the data area. 291 uint8* ptr = static_cast<uint8*>(shared_memory_.memory()); 292 ptr += current_segment_id_ * segment_length_; 293 AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); 294 DCHECK_EQ(buffer->params.size, 295 segment_length_ - sizeof(AudioInputBufferParameters)); 296 double volume = buffer->params.volume; 297 298 int audio_delay_milliseconds = pending_data / bytes_per_ms_; 299 int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]); 300 const int bytes_per_sample = sizeof(memory[0]); 301 302 if (++current_segment_id_ >= total_segments_) 303 current_segment_id_ = 0; 304 305 // Deinterleave each channel and convert to 32-bit floating-point 306 // with nominal range -1.0 -> +1.0. 307 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); 308 309 // Deliver captured data to the client in floating point format 310 // and update the audio-delay measurement. 311 capture_callback_->Capture(audio_bus_.get(), 312 audio_delay_milliseconds, volume); 313} 314 315} // namespace media 316