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 "media/audio/audio_input_controller.h" | 5 #include "media/audio/audio_input_controller.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/threading/thread_restrictions.h" | 8 #include "base/threading/thread_restrictions.h" |
9 #include "media/base/limits.h" | 9 #include "media/base/limits.h" |
10 | 10 |
11 namespace { | 11 namespace { |
12 const int kMaxInputChannels = 2; | 12 const int kMaxInputChannels = 2; |
13 const int kTimerResetInterval = 1; // One second. | 13 const int kTimerResetInterval = 1; // One second. |
14 } | 14 } |
15 | 15 |
16 namespace media { | 16 namespace media { |
17 | 17 |
18 // static | 18 // static |
19 AudioInputController::Factory* AudioInputController::factory_ = NULL; | 19 AudioInputController::Factory* AudioInputController::factory_ = NULL; |
20 | 20 |
21 AudioInputController::AudioInputController(EventHandler* handler, | 21 AudioInputController::AudioInputController(EventHandler* handler, |
22 SyncWriter* sync_writer) | 22 SyncWriter* sync_writer) |
23 : creator_loop_(base::MessageLoopProxy::current()), | 23 : creator_loop_(base::MessageLoopProxy::current()), |
24 handler_(handler), | 24 handler_(handler), |
25 stream_(NULL), | 25 stream_(NULL), |
26 data_is_active_(false), | |
26 state_(kEmpty), | 27 state_(kEmpty), |
27 sync_writer_(sync_writer), | 28 sync_writer_(sync_writer), |
28 max_volume_(0.0) { | 29 max_volume_(0.0) { |
29 DCHECK(creator_loop_); | 30 DCHECK(creator_loop_); |
30 no_data_timer_.reset(new base::DelayTimer<AudioInputController>(FROM_HERE, | |
31 base::TimeDelta::FromSeconds(kTimerResetInterval), | |
32 this, | |
33 &AudioInputController::DoReportNoDataError)); | |
34 } | 31 } |
35 | 32 |
36 AudioInputController::~AudioInputController() { | 33 AudioInputController::~AudioInputController() { |
37 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); | 34 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); |
38 } | 35 } |
39 | 36 |
40 // static | 37 // static |
41 scoped_refptr<AudioInputController> AudioInputController::Create( | 38 scoped_refptr<AudioInputController> AudioInputController::Create( |
42 AudioManager* audio_manager, | 39 AudioManager* audio_manager, |
43 EventHandler* event_handler, | 40 EventHandler* event_handler, |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
98 } | 95 } |
99 | 96 |
100 void AudioInputController::Record() { | 97 void AudioInputController::Record() { |
101 message_loop_->PostTask(FROM_HERE, base::Bind( | 98 message_loop_->PostTask(FROM_HERE, base::Bind( |
102 &AudioInputController::DoRecord, this)); | 99 &AudioInputController::DoRecord, this)); |
103 } | 100 } |
104 | 101 |
105 void AudioInputController::Close(const base::Closure& closed_task) { | 102 void AudioInputController::Close(const base::Closure& closed_task) { |
106 DCHECK(!closed_task.is_null()); | 103 DCHECK(!closed_task.is_null()); |
107 DCHECK(creator_loop_->BelongsToCurrentThread()); | 104 DCHECK(creator_loop_->BelongsToCurrentThread()); |
108 // See crbug.com/119783: Deleting the timer now to avoid disaster if | 105 |
109 // AudioInputController is destructed on a thread other than the creator | |
110 // thread. | |
111 no_data_timer_.reset(); | |
112 message_loop_->PostTaskAndReply( | 106 message_loop_->PostTaskAndReply( |
113 FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task); | 107 FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task); |
114 } | 108 } |
115 | 109 |
116 void AudioInputController::SetVolume(double volume) { | 110 void AudioInputController::SetVolume(double volume) { |
117 message_loop_->PostTask(FROM_HERE, base::Bind( | 111 message_loop_->PostTask(FROM_HERE, base::Bind( |
118 &AudioInputController::DoSetVolume, this, volume)); | 112 &AudioInputController::DoSetVolume, this, volume)); |
119 } | 113 } |
120 | 114 |
121 void AudioInputController::SetAutomaticGainControl(bool enabled) { | 115 void AudioInputController::SetAutomaticGainControl(bool enabled) { |
(...skipping 15 matching lines...) Expand all Loading... | |
137 } | 131 } |
138 | 132 |
139 if (stream_ && !stream_->Open()) { | 133 if (stream_ && !stream_->Open()) { |
140 stream_->Close(); | 134 stream_->Close(); |
141 stream_ = NULL; | 135 stream_ = NULL; |
142 // TODO(satish): Define error types. | 136 // TODO(satish): Define error types. |
143 handler_->OnError(this, 0); | 137 handler_->OnError(this, 0); |
144 return; | 138 return; |
145 } | 139 } |
146 | 140 |
147 creator_loop_->PostTask(FROM_HERE, base::Bind( | 141 if (!no_data_timer_.get()) { |
tommi (sloooow) - chröme
2012/04/17 13:12:49
sorry, what I meant was this:
DCHECK(!no_data_tim
henrika (OOO until Aug 14)
2012/04/17 13:38:31
Done.
| |
148 &AudioInputController::DoResetNoDataTimer, this)); | 142 // Create the data timer which will call DoCheckForNoData() after a delay |
143 // of |kTimerResetInterval| seconds. The timer is started in DoRecord() | |
144 // and restarted in each DoCheckForNoData() callback. | |
145 no_data_timer_.reset(new base::DelayTimer<AudioInputController>(FROM_HERE, | |
146 base::TimeDelta::FromSeconds(kTimerResetInterval), | |
147 this, | |
148 &AudioInputController::DoCheckForNoData)); | |
149 } | |
149 | 150 |
150 state_ = kCreated; | 151 state_ = kCreated; |
151 handler_->OnCreated(this); | 152 handler_->OnCreated(this); |
152 } | 153 } |
153 | 154 |
154 void AudioInputController::DoRecord() { | 155 void AudioInputController::DoRecord() { |
155 DCHECK(message_loop_->BelongsToCurrentThread()); | 156 DCHECK(message_loop_->BelongsToCurrentThread()); |
156 | 157 |
157 if (state_ != kCreated) | 158 if (state_ != kCreated) |
158 return; | 159 return; |
159 | 160 |
160 { | 161 { |
161 base::AutoLock auto_lock(lock_); | 162 base::AutoLock auto_lock(lock_); |
162 state_ = kRecording; | 163 state_ = kRecording; |
163 } | 164 } |
164 | 165 |
166 // Start the data timer. Once |kTimerResetInterval| seconds have passed, | |
167 // a callback to DoCheckForNoData() made. | |
168 no_data_timer_->Reset(); | |
169 | |
165 stream_->Start(this); | 170 stream_->Start(this); |
166 handler_->OnRecording(this); | 171 handler_->OnRecording(this); |
167 } | 172 } |
168 | 173 |
169 void AudioInputController::DoClose() { | 174 void AudioInputController::DoClose() { |
170 DCHECK(message_loop_->BelongsToCurrentThread()); | 175 DCHECK(message_loop_->BelongsToCurrentThread()); |
171 | 176 |
177 if (no_data_timer_.get()) { | |
178 // Delete the timer on the same thread that created it. | |
179 no_data_timer_.reset(); | |
180 } | |
181 | |
172 if (state_ != kClosed) { | 182 if (state_ != kClosed) { |
173 DoStopCloseAndClearStream(NULL); | 183 DoStopCloseAndClearStream(NULL); |
184 SetDataIsActive(false); | |
174 | 185 |
175 if (LowLatencyMode()) { | 186 if (LowLatencyMode()) { |
176 sync_writer_->Close(); | 187 sync_writer_->Close(); |
177 } | 188 } |
178 | 189 |
179 state_ = kClosed; | 190 state_ = kClosed; |
180 } | 191 } |
181 } | 192 } |
182 | 193 |
183 void AudioInputController::DoReportError(int code) { | 194 void AudioInputController::DoReportError(int code) { |
(...skipping 28 matching lines...) Expand all Loading... | |
212 DCHECK(message_loop_->BelongsToCurrentThread()); | 223 DCHECK(message_loop_->BelongsToCurrentThread()); |
213 DCHECK_NE(state_, kRecording); | 224 DCHECK_NE(state_, kRecording); |
214 | 225 |
215 // Ensure that the AGC state only can be modified before streaming starts. | 226 // Ensure that the AGC state only can be modified before streaming starts. |
216 if (state_ != kCreated || state_ == kRecording) | 227 if (state_ != kCreated || state_ == kRecording) |
217 return; | 228 return; |
218 | 229 |
219 stream_->SetAutomaticGainControl(enabled); | 230 stream_->SetAutomaticGainControl(enabled); |
220 } | 231 } |
221 | 232 |
222 void AudioInputController::DoReportNoDataError() { | 233 void AudioInputController::DoCheckForNoData() { |
223 DCHECK(creator_loop_->BelongsToCurrentThread()); | 234 DCHECK(message_loop_->BelongsToCurrentThread()); |
224 | 235 |
225 // Error notifications should be sent on the audio-manager thread. | 236 if (!GetDataIsActive()) { |
226 int code = 0; | 237 // The data-is-active marker will be false only if it has been more than |
227 message_loop_->PostTask(FROM_HERE, base::Bind( | 238 // one second since a data packet was recorded. This can happen if a |
228 &AudioInputController::DoReportError, this, code)); | 239 // capture device has been removed or disabled. |
229 } | 240 handler_->OnError(this, 0); |
241 return; | |
242 } | |
230 | 243 |
231 void AudioInputController::DoResetNoDataTimer() { | 244 // Mark data as non-active. The flag will be re-enabled in OnData() each |
232 DCHECK(creator_loop_->BelongsToCurrentThread()); | 245 // time a data packet is received. Hence, under normal conditions, the |
233 if (no_data_timer_.get()) | 246 // flag will only be disabled during a very short period. |
234 no_data_timer_->Reset(); | 247 SetDataIsActive(false); |
248 | |
249 // Restart the timer to ensure that we check the flag in one second again. | |
250 no_data_timer_->Reset(); | |
235 } | 251 } |
236 | 252 |
237 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, | 253 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, |
238 uint32 size, uint32 hardware_delay_bytes, | 254 uint32 size, uint32 hardware_delay_bytes, |
239 double volume) { | 255 double volume) { |
240 { | 256 { |
241 base::AutoLock auto_lock(lock_); | 257 base::AutoLock auto_lock(lock_); |
242 if (state_ != kRecording) | 258 if (state_ != kRecording) |
243 return; | 259 return; |
244 } | 260 } |
245 | 261 |
246 creator_loop_->PostTask(FROM_HERE, base::Bind( | 262 // Mark data as active to ensure that the periodic calls to |
247 &AudioInputController::DoResetNoDataTimer, this)); | 263 // DoCheckForNoData() does not report an error to the event handler. |
264 SetDataIsActive(true); | |
248 | 265 |
249 // Use SyncSocket if we are in a low-latency mode. | 266 // Use SyncSocket if we are in a low-latency mode. |
250 if (LowLatencyMode()) { | 267 if (LowLatencyMode()) { |
251 sync_writer_->Write(data, size, volume); | 268 sync_writer_->Write(data, size, volume); |
252 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 269 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
253 return; | 270 return; |
254 } | 271 } |
255 | 272 |
256 handler_->OnData(this, data, size); | 273 handler_->OnData(this, data, size); |
257 } | 274 } |
(...skipping 20 matching lines...) Expand all Loading... | |
278 stream_->Stop(); | 295 stream_->Stop(); |
279 stream_->Close(); | 296 stream_->Close(); |
280 stream_ = NULL; | 297 stream_ = NULL; |
281 } | 298 } |
282 | 299 |
283 // Should be last in the method, do not touch "this" from here on. | 300 // Should be last in the method, do not touch "this" from here on. |
284 if (done != NULL) | 301 if (done != NULL) |
285 done->Signal(); | 302 done->Signal(); |
286 } | 303 } |
287 | 304 |
305 void AudioInputController::SetDataIsActive(bool enabled) { | |
306 base::subtle::Release_Store(&data_is_active_, enabled); | |
307 } | |
308 | |
309 bool AudioInputController::GetDataIsActive() { | |
310 return (base::subtle::Acquire_Load(&data_is_active_) != false); | |
311 } | |
312 | |
288 } // namespace media | 313 } // namespace media |
OLD | NEW |