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