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 DCHECK(!no_data_timer_.get()); |
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 state_ = kCreated; | 150 state_ = kCreated; |
151 handler_->OnCreated(this); | 151 handler_->OnCreated(this); |
152 } | 152 } |
153 | 153 |
154 void AudioInputController::DoRecord() { | 154 void AudioInputController::DoRecord() { |
155 DCHECK(message_loop_->BelongsToCurrentThread()); | 155 DCHECK(message_loop_->BelongsToCurrentThread()); |
156 | 156 |
157 if (state_ != kCreated) | 157 if (state_ != kCreated) |
158 return; | 158 return; |
159 | 159 |
160 { | 160 { |
161 base::AutoLock auto_lock(lock_); | 161 base::AutoLock auto_lock(lock_); |
162 state_ = kRecording; | 162 state_ = kRecording; |
163 } | 163 } |
164 | 164 |
| 165 // Start the data timer. Once |kTimerResetInterval| seconds have passed, |
| 166 // a callback to DoCheckForNoData() is made. |
| 167 no_data_timer_->Reset(); |
| 168 |
165 stream_->Start(this); | 169 stream_->Start(this); |
166 handler_->OnRecording(this); | 170 handler_->OnRecording(this); |
167 } | 171 } |
168 | 172 |
169 void AudioInputController::DoClose() { | 173 void AudioInputController::DoClose() { |
170 DCHECK(message_loop_->BelongsToCurrentThread()); | 174 DCHECK(message_loop_->BelongsToCurrentThread()); |
171 | 175 |
| 176 // Delete the timer on the same thread that created it. |
| 177 no_data_timer_.reset(); |
| 178 |
172 if (state_ != kClosed) { | 179 if (state_ != kClosed) { |
173 DoStopCloseAndClearStream(NULL); | 180 DoStopCloseAndClearStream(NULL); |
| 181 SetDataIsActive(false); |
174 | 182 |
175 if (LowLatencyMode()) { | 183 if (LowLatencyMode()) { |
176 sync_writer_->Close(); | 184 sync_writer_->Close(); |
177 } | 185 } |
178 | 186 |
179 state_ = kClosed; | 187 state_ = kClosed; |
180 } | 188 } |
181 } | 189 } |
182 | 190 |
183 void AudioInputController::DoReportError(int code) { | 191 void AudioInputController::DoReportError(int code) { |
(...skipping 28 matching lines...) Expand all Loading... |
212 DCHECK(message_loop_->BelongsToCurrentThread()); | 220 DCHECK(message_loop_->BelongsToCurrentThread()); |
213 DCHECK_NE(state_, kRecording); | 221 DCHECK_NE(state_, kRecording); |
214 | 222 |
215 // Ensure that the AGC state only can be modified before streaming starts. | 223 // Ensure that the AGC state only can be modified before streaming starts. |
216 if (state_ != kCreated || state_ == kRecording) | 224 if (state_ != kCreated || state_ == kRecording) |
217 return; | 225 return; |
218 | 226 |
219 stream_->SetAutomaticGainControl(enabled); | 227 stream_->SetAutomaticGainControl(enabled); |
220 } | 228 } |
221 | 229 |
222 void AudioInputController::DoReportNoDataError() { | 230 void AudioInputController::DoCheckForNoData() { |
223 DCHECK(creator_loop_->BelongsToCurrentThread()); | 231 DCHECK(message_loop_->BelongsToCurrentThread()); |
224 | 232 |
225 // Error notifications should be sent on the audio-manager thread. | 233 if (!GetDataIsActive()) { |
226 int code = 0; | 234 // The data-is-active marker will be false only if it has been more than |
227 message_loop_->PostTask(FROM_HERE, base::Bind( | 235 // one second since a data packet was recorded. This can happen if a |
228 &AudioInputController::DoReportError, this, code)); | 236 // capture device has been removed or disabled. |
229 } | 237 handler_->OnError(this, 0); |
| 238 return; |
| 239 } |
230 | 240 |
231 void AudioInputController::DoResetNoDataTimer() { | 241 // Mark data as non-active. The flag will be re-enabled in OnData() each |
232 DCHECK(creator_loop_->BelongsToCurrentThread()); | 242 // time a data packet is received. Hence, under normal conditions, the |
233 if (no_data_timer_.get()) | 243 // flag will only be disabled during a very short period. |
234 no_data_timer_->Reset(); | 244 SetDataIsActive(false); |
| 245 |
| 246 // Restart the timer to ensure that we check the flag in one second again. |
| 247 no_data_timer_->Reset(); |
235 } | 248 } |
236 | 249 |
237 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, | 250 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, |
238 uint32 size, uint32 hardware_delay_bytes, | 251 uint32 size, uint32 hardware_delay_bytes, |
239 double volume) { | 252 double volume) { |
240 { | 253 { |
241 base::AutoLock auto_lock(lock_); | 254 base::AutoLock auto_lock(lock_); |
242 if (state_ != kRecording) | 255 if (state_ != kRecording) |
243 return; | 256 return; |
244 } | 257 } |
245 | 258 |
246 creator_loop_->PostTask(FROM_HERE, base::Bind( | 259 // Mark data as active to ensure that the periodic calls to |
247 &AudioInputController::DoResetNoDataTimer, this)); | 260 // DoCheckForNoData() does not report an error to the event handler. |
| 261 SetDataIsActive(true); |
248 | 262 |
249 // Use SyncSocket if we are in a low-latency mode. | 263 // Use SyncSocket if we are in a low-latency mode. |
250 if (LowLatencyMode()) { | 264 if (LowLatencyMode()) { |
251 sync_writer_->Write(data, size, volume); | 265 sync_writer_->Write(data, size, volume); |
252 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 266 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
253 return; | 267 return; |
254 } | 268 } |
255 | 269 |
256 handler_->OnData(this, data, size); | 270 handler_->OnData(this, data, size); |
257 } | 271 } |
(...skipping 20 matching lines...) Expand all Loading... |
278 stream_->Stop(); | 292 stream_->Stop(); |
279 stream_->Close(); | 293 stream_->Close(); |
280 stream_ = NULL; | 294 stream_ = NULL; |
281 } | 295 } |
282 | 296 |
283 // Should be last in the method, do not touch "this" from here on. | 297 // Should be last in the method, do not touch "this" from here on. |
284 if (done != NULL) | 298 if (done != NULL) |
285 done->Signal(); | 299 done->Signal(); |
286 } | 300 } |
287 | 301 |
| 302 void AudioInputController::SetDataIsActive(bool enabled) { |
| 303 base::subtle::Release_Store(&data_is_active_, enabled); |
| 304 } |
| 305 |
| 306 bool AudioInputController::GetDataIsActive() { |
| 307 return (base::subtle::Acquire_Load(&data_is_active_) != false); |
| 308 } |
| 309 |
288 } // namespace media | 310 } // namespace media |
OLD | NEW |