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 |