Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/media/audio_input_device.h" | 5 #include "content/renderer/media/audio_input_device.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/threading/thread_restrictions.h" | 9 #include "base/threading/thread_restrictions.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 41 int channels, | 41 int channels, |
| 42 double sample_rate, | 42 double sample_rate, |
| 43 CaptureCallback* callback, | 43 CaptureCallback* callback, |
| 44 CaptureEventHandler* event_handler) | 44 CaptureEventHandler* event_handler) |
| 45 : ScopedLoopObserver(ChildProcess::current()->io_message_loop()), | 45 : ScopedLoopObserver(ChildProcess::current()->io_message_loop()), |
| 46 callback_(callback), | 46 callback_(callback), |
| 47 event_handler_(event_handler), | 47 event_handler_(event_handler), |
| 48 volume_(1.0), | 48 volume_(1.0), |
| 49 stream_id_(0), | 49 stream_id_(0), |
| 50 session_id_(0), | 50 session_id_(0), |
| 51 pending_device_ready_(false) { | 51 pending_device_ready_(false) { |
|
scherkus (not reviewing)
2012/03/20 13:49:41
should you initialize agc_is_enabled_ here?
henrika (OOO until Aug 14)
2012/03/21 10:16:04
OOps. Thanks.
| |
| 52 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); | 52 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); |
| 53 #if defined(OS_MACOSX) | 53 #if defined(OS_MACOSX) |
| 54 DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Mac OS X."; | 54 DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Mac OS X."; |
| 55 audio_parameters_.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; | 55 audio_parameters_.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; |
| 56 #elif defined(OS_WIN) | 56 #elif defined(OS_WIN) |
| 57 DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Windows."; | 57 DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Windows."; |
| 58 audio_parameters_.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; | 58 audio_parameters_.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; |
| 59 #else | 59 #else |
| 60 // TODO(henrika): add support for AUDIO_PCM_LOW_LATENCY on Linux as well. | 60 // TODO(henrika): add support for AUDIO_PCM_LOW_LATENCY on Linux as well. |
| 61 audio_parameters_.format = AudioParameters::AUDIO_PCM_LINEAR; | 61 audio_parameters_.format = AudioParameters::AUDIO_PCM_LINEAR; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 92 base::AutoLock auto_lock(audio_thread_lock_); | 92 base::AutoLock auto_lock(audio_thread_lock_); |
| 93 callback_ = NULL; // After Stop() returns, we must never deref callback_. | 93 callback_ = NULL; // After Stop() returns, we must never deref callback_. |
| 94 audio_thread_.Stop(MessageLoop::current()); | 94 audio_thread_.Stop(MessageLoop::current()); |
| 95 } | 95 } |
| 96 | 96 |
| 97 message_loop()->PostTask(FROM_HERE, | 97 message_loop()->PostTask(FROM_HERE, |
| 98 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); | 98 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); |
| 99 } | 99 } |
| 100 | 100 |
| 101 bool AudioInputDevice::SetVolume(double volume) { | 101 bool AudioInputDevice::SetVolume(double volume) { |
| 102 NOTIMPLEMENTED(); | 102 if (volume < 0 || volume > 1.0) |
| 103 return false; | |
| 104 | |
| 105 message_loop()->PostTask(FROM_HERE, | |
| 106 base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume)); | |
| 107 | |
| 108 return true; | |
| 109 } | |
| 110 | |
| 111 bool AudioInputDevice::GetVolume(double* volume) { | |
| 112 NOTREACHED(); | |
| 103 return false; | 113 return false; |
| 104 } | 114 } |
| 105 | 115 |
| 106 bool AudioInputDevice::GetVolume(double* volume) { | 116 void AudioInputDevice::SetAutomaticGainControl(bool enabled) { |
| 107 NOTIMPLEMENTED(); | 117 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; |
| 108 return false; | 118 message_loop()->PostTask(FROM_HERE, |
| 119 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, | |
| 120 this, enabled)); | |
| 109 } | 121 } |
| 110 | 122 |
| 111 void AudioInputDevice::InitializeOnIOThread() { | 123 void AudioInputDevice::InitializeOnIOThread() { |
| 112 DCHECK(message_loop()->BelongsToCurrentThread()); | 124 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 113 // Make sure we don't call Start() more than once. | 125 // Make sure we don't call Start() more than once. |
| 114 DCHECK_EQ(0, stream_id_); | 126 DCHECK_EQ(0, stream_id_); |
| 115 if (stream_id_) | 127 if (stream_id_) |
| 116 return; | 128 return; |
| 117 | 129 |
| 118 stream_id_ = filter_->AddDelegate(this); | 130 stream_id_ = filter_->AddDelegate(this); |
| 119 // If |session_id_| is not specified, it will directly create the stream; | 131 // If |session_id_| is not specified, it will directly create the stream; |
| 120 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | 132 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser |
| 121 // and create the stream when getting a OnDeviceReady() callback. | 133 // and create the stream when getting a OnDeviceReady() callback. |
| 122 if (!session_id_) { | 134 if (!session_id_) { |
| 123 Send(new AudioInputHostMsg_CreateStream( | 135 Send(new AudioInputHostMsg_CreateStream( |
| 124 stream_id_, audio_parameters_, AudioManagerBase::kDefaultDeviceId)); | 136 stream_id_, audio_parameters_, AudioManagerBase::kDefaultDeviceId, |
| 137 agc_is_enabled_)); | |
| 125 } else { | 138 } else { |
| 126 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); | 139 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); |
| 127 pending_device_ready_ = true; | 140 pending_device_ready_ = true; |
| 128 } | 141 } |
| 129 } | 142 } |
| 130 | 143 |
| 131 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { | 144 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { |
| 132 DCHECK(message_loop()->BelongsToCurrentThread()); | 145 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 133 session_id_ = session_id; | 146 session_id_ = session_id; |
| 134 } | 147 } |
| 135 | 148 |
| 136 void AudioInputDevice::StartOnIOThread() { | 149 void AudioInputDevice::StartOnIOThread() { |
| 137 DCHECK(message_loop()->BelongsToCurrentThread()); | 150 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 138 if (stream_id_) | 151 if (stream_id_) |
| 139 Send(new AudioInputHostMsg_RecordStream(stream_id_)); | 152 Send(new AudioInputHostMsg_RecordStream(stream_id_)); |
| 140 } | 153 } |
| 141 | 154 |
| 142 void AudioInputDevice::ShutDownOnIOThread() { | 155 void AudioInputDevice::ShutDownOnIOThread() { |
| 143 DCHECK(message_loop()->BelongsToCurrentThread()); | 156 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 144 // NOTE: |completion| may be NULL. | 157 // NOTE: |completion| may be NULL. |
| 145 // Make sure we don't call shutdown more than once. | 158 // Make sure we don't call shutdown more than once. |
| 146 if (stream_id_) { | 159 if (stream_id_) { |
| 147 filter_->RemoveDelegate(stream_id_); | 160 filter_->RemoveDelegate(stream_id_); |
| 148 Send(new AudioInputHostMsg_CloseStream(stream_id_)); | 161 Send(new AudioInputHostMsg_CloseStream(stream_id_)); |
| 149 | 162 |
| 150 stream_id_ = 0; | 163 stream_id_ = 0; |
| 151 session_id_ = 0; | 164 session_id_ = 0; |
| 152 pending_device_ready_ = false; | 165 pending_device_ready_ = false; |
| 166 agc_is_enabled_ = false; | |
| 153 } | 167 } |
| 154 | 168 |
| 155 // We can run into an issue where ShutDownOnIOThread is called right after | 169 // We can run into an issue where ShutDownOnIOThread is called right after |
| 156 // OnStreamCreated is called in cases where Start/Stop are called before we | 170 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 157 // get the OnStreamCreated callback. To handle that corner case, we call | 171 // get the OnStreamCreated callback. To handle that corner case, we call |
| 158 // Stop(). In most cases, the thread will already be stopped. | 172 // Stop(). In most cases, the thread will already be stopped. |
| 159 // Another situation is when the IO thread goes away before Stop() is called | 173 // Another situation is when the IO thread goes away before Stop() is called |
| 160 // in which case, we cannot use the message loop to close the thread handle | 174 // in which case, we cannot use the message loop to close the thread handle |
| 161 // and can't not rely on the main thread existing either. | 175 // and can't not rely on the main thread existing either. |
| 162 base::ThreadRestrictions::ScopedAllowIO allow_io; | 176 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 163 audio_thread_.Stop(NULL); | 177 audio_thread_.Stop(NULL); |
| 164 audio_callback_.reset(); | 178 audio_callback_.reset(); |
| 165 } | 179 } |
| 166 | 180 |
| 167 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | 181 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
| 168 DCHECK(message_loop()->BelongsToCurrentThread()); | 182 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 169 if (stream_id_) | 183 if (stream_id_) |
| 170 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); | 184 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); |
| 171 } | 185 } |
| 172 | 186 |
| 187 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | |
| 188 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 189 if (stream_id_) { | |
| 190 // We enter this scope if InitializeOnIOThread() has already been called. | |
| 191 // It means that an audio stream already exists but AGC is disabled. | |
| 192 // We must therefore modify the current AGC setting for the existing | |
| 193 // stream using IPC. | |
| 194 Send(new AudioInputHostMsg_SetAutomaticGainControl(stream_id_, enabled)); | |
| 195 } | |
| 196 | |
| 197 // Always store new AGC setting. This value will be used when a new stream | |
| 198 // is initialized and by GetAutomaticGainControl(). | |
| 199 agc_is_enabled_ = enabled; | |
| 200 } | |
| 201 | |
| 173 void AudioInputDevice::OnStreamCreated( | 202 void AudioInputDevice::OnStreamCreated( |
| 174 base::SharedMemoryHandle handle, | 203 base::SharedMemoryHandle handle, |
| 175 base::SyncSocket::Handle socket_handle, | 204 base::SyncSocket::Handle socket_handle, |
| 176 uint32 length) { | 205 uint32 length) { |
| 177 DCHECK(message_loop()->BelongsToCurrentThread()); | 206 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 178 #if defined(OS_WIN) | 207 #if defined(OS_WIN) |
| 179 DCHECK(handle); | 208 DCHECK(handle); |
| 180 DCHECK(socket_handle); | 209 DCHECK(socket_handle); |
| 181 #else | 210 #else |
| 182 DCHECK_GE(handle.fd, 0); | 211 DCHECK_GE(handle.fd, 0); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 254 if (!pending_device_ready_) | 283 if (!pending_device_ready_) |
| 255 return; | 284 return; |
| 256 | 285 |
| 257 // If AudioInputDeviceManager returns an empty string, it means no device | 286 // If AudioInputDeviceManager returns an empty string, it means no device |
| 258 // is ready for start. | 287 // is ready for start. |
| 259 if (device_id.empty()) { | 288 if (device_id.empty()) { |
| 260 filter_->RemoveDelegate(stream_id_); | 289 filter_->RemoveDelegate(stream_id_); |
| 261 stream_id_ = 0; | 290 stream_id_ = 0; |
| 262 } else { | 291 } else { |
| 263 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, | 292 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, |
| 264 device_id)); | 293 device_id, agc_is_enabled_)); |
| 265 } | 294 } |
| 266 | 295 |
| 267 pending_device_ready_ = false; | 296 pending_device_ready_ = false; |
| 268 // Notify the client that the device has been started. | 297 // Notify the client that the device has been started. |
| 269 if (event_handler_) | 298 if (event_handler_) |
| 270 event_handler_->OnDeviceStarted(device_id); | 299 event_handler_->OnDeviceStarted(device_id); |
| 271 } | 300 } |
| 272 | 301 |
| 273 void AudioInputDevice::Send(IPC::Message* message) { | 302 void AudioInputDevice::Send(IPC::Message* message) { |
| 274 filter_->Send(message); | 303 filter_->Send(message); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 291 | 320 |
| 292 AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { | 321 AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { |
| 293 } | 322 } |
| 294 | 323 |
| 295 void AudioInputDevice::AudioThreadCallback::MapSharedMemory() { | 324 void AudioInputDevice::AudioThreadCallback::MapSharedMemory() { |
| 296 shared_memory_.Map(memory_length_); | 325 shared_memory_.Map(memory_length_); |
| 297 } | 326 } |
| 298 | 327 |
| 299 void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { | 328 void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { |
| 300 int audio_delay_milliseconds = pending_data / bytes_per_ms_; | 329 int audio_delay_milliseconds = pending_data / bytes_per_ms_; |
| 301 int16* memory = reinterpret_cast<int16*>(shared_memory_.memory()); | 330 |
| 331 AudioInputBuffer* buffer = | |
| 332 reinterpret_cast<AudioInputBuffer*>(shared_memory_.memory()); | |
|
tommi (sloooow) - chröme
2012/03/16 13:31:05
indent
henrika (OOO until Aug 14)
2012/03/21 10:16:04
Done.
| |
| 333 | |
| 334 // TODO(henrika): uint32 size = buffer->size; | |
|
tommi (sloooow) - chröme
2012/03/16 13:31:05
ping
henrika (OOO until Aug 14)
2012/03/21 10:16:04
Added DCHECK which uses the size variable.
| |
| 335 | |
| 336 double volume = buffer->volume; | |
| 337 int16* memory = reinterpret_cast<int16*>(buffer->audio); | |
|
tommi (sloooow) - chröme
2012/03/16 13:31:05
nit: &buffer->audio[0]
henrika (OOO until Aug 14)
2012/03/21 10:16:04
Done.
| |
| 302 const size_t number_of_frames = audio_parameters_.samples_per_packet; | 338 const size_t number_of_frames = audio_parameters_.samples_per_packet; |
| 303 const int bytes_per_sample = sizeof(memory[0]); | 339 const int bytes_per_sample = sizeof(memory[0]); |
| 304 | 340 |
| 305 // Deinterleave each channel and convert to 32-bit floating-point | 341 // Deinterleave each channel and convert to 32-bit floating-point |
| 306 // with nominal range -1.0 -> +1.0. | 342 // with nominal range -1.0 -> +1.0. |
| 307 for (int channel_index = 0; channel_index < audio_parameters_.channels; | 343 for (int channel_index = 0; channel_index < audio_parameters_.channels; |
| 308 ++channel_index) { | 344 ++channel_index) { |
| 309 media::DeinterleaveAudioChannel(memory, | 345 media::DeinterleaveAudioChannel(memory, |
| 310 audio_data_[channel_index], | 346 audio_data_[channel_index], |
| 311 audio_parameters_.channels, | 347 audio_parameters_.channels, |
| 312 channel_index, | 348 channel_index, |
| 313 bytes_per_sample, | 349 bytes_per_sample, |
| 314 number_of_frames); | 350 number_of_frames); |
| 315 } | 351 } |
| 316 | 352 |
| 317 // Deliver captured data to the client in floating point format | 353 // Deliver captured data to the client in floating point format |
| 318 // and update the audio-delay measurement. | 354 // and update the audio-delay measurement. |
| 319 capture_callback_->Capture(audio_data_, number_of_frames, | 355 capture_callback_->Capture(audio_data_, number_of_frames, |
| 320 audio_delay_milliseconds); | 356 audio_delay_milliseconds, volume); |
| 321 } | 357 } |
| OLD | NEW |