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 state_(kEmpty), | 26 state_(kEmpty), |
27 sync_writer_(sync_writer), | 27 sync_writer_(sync_writer), |
28 max_volume_(0.0) { | 28 max_volume_(0.0), |
29 data_is_active_(false) { | |
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 // Create the data timer which will call DoCheckForNoData() after a delay |
148 &AudioInputController::DoResetNoDataTimer, this)); | 142 // of |kTimerResetInterval| seconds. The timer is started in DoRecord() |
143 // and restarted in each DoCheckForNoData() callback. | |
144 no_data_timer_.reset(new base::DelayTimer<AudioInputController>(FROM_HERE, | |
tommi (sloooow) - chröme
2012/04/17 11:58:31
should we first that no_data_timer_ is NULL?
no longer working on chromium
2012/04/17 12:20:41
can we move these code to DoRecord instead, since
henrika (OOO until Aug 14)
2012/04/17 13:03:27
Done.
henrika (OOO until Aug 14)
2012/04/17 13:03:27
It is possible but I felt it was more symmetric to
| |
145 base::TimeDelta::FromSeconds(kTimerResetInterval), | |
146 this, | |
147 &AudioInputController::DoCheckForNoData)); | |
149 | 148 |
150 state_ = kCreated; | 149 state_ = kCreated; |
151 handler_->OnCreated(this); | 150 handler_->OnCreated(this); |
152 } | 151 } |
153 | 152 |
154 void AudioInputController::DoRecord() { | 153 void AudioInputController::DoRecord() { |
155 DCHECK(message_loop_->BelongsToCurrentThread()); | 154 DCHECK(message_loop_->BelongsToCurrentThread()); |
156 | 155 |
157 if (state_ != kCreated) | 156 if (state_ != kCreated) |
158 return; | 157 return; |
159 | 158 |
160 { | 159 { |
161 base::AutoLock auto_lock(lock_); | 160 base::AutoLock auto_lock(lock_); |
162 state_ = kRecording; | 161 state_ = kRecording; |
163 } | 162 } |
164 | 163 |
164 // Start the data timer. Once |kTimerResetInterval| seconds have passed, | |
165 // a callback to DoCheckForNoData() made. | |
166 DoResetNoDataTimer(); | |
no longer working on chromium
2012/04/17 12:20:41
We can skip this call if we move the code here: no
henrika (OOO until Aug 14)
2012/04/17 13:03:27
Actually no since start requires: create + reset.
| |
167 | |
165 stream_->Start(this); | 168 stream_->Start(this); |
166 handler_->OnRecording(this); | 169 handler_->OnRecording(this); |
167 } | 170 } |
168 | 171 |
169 void AudioInputController::DoClose() { | 172 void AudioInputController::DoClose() { |
170 DCHECK(message_loop_->BelongsToCurrentThread()); | 173 DCHECK(message_loop_->BelongsToCurrentThread()); |
171 | 174 |
175 if (no_data_timer_.get()) { | |
no longer working on chromium
2012/04/17 12:20:41
maybe we can skip the check, and set it regardless
henrika (OOO until Aug 14)
2012/04/17 13:03:27
Not sure why you don't like the non-NULL check. Ca
tommi (sloooow) - chröme
2012/04/17 13:12:49
There is already a NULL check inside reset(), so w
henrika (OOO until Aug 14)
2012/04/17 13:38:31
Done.
| |
176 // Delete the timer on the same thread that created it. | |
177 no_data_timer_.reset(); | |
178 } | |
179 | |
172 if (state_ != kClosed) { | 180 if (state_ != kClosed) { |
173 DoStopCloseAndClearStream(NULL); | 181 DoStopCloseAndClearStream(NULL); |
182 SetDataIsActive(false); | |
174 | 183 |
175 if (LowLatencyMode()) { | 184 if (LowLatencyMode()) { |
176 sync_writer_->Close(); | 185 sync_writer_->Close(); |
177 } | 186 } |
178 | 187 |
179 state_ = kClosed; | 188 state_ = kClosed; |
180 } | 189 } |
181 } | 190 } |
182 | 191 |
183 void AudioInputController::DoReportError(int code) { | 192 void AudioInputController::DoReportError(int code) { |
(...skipping 28 matching lines...) Expand all Loading... | |
212 DCHECK(message_loop_->BelongsToCurrentThread()); | 221 DCHECK(message_loop_->BelongsToCurrentThread()); |
213 DCHECK_NE(state_, kRecording); | 222 DCHECK_NE(state_, kRecording); |
214 | 223 |
215 // Ensure that the AGC state only can be modified before streaming starts. | 224 // Ensure that the AGC state only can be modified before streaming starts. |
216 if (state_ != kCreated || state_ == kRecording) | 225 if (state_ != kCreated || state_ == kRecording) |
217 return; | 226 return; |
218 | 227 |
219 stream_->SetAutomaticGainControl(enabled); | 228 stream_->SetAutomaticGainControl(enabled); |
220 } | 229 } |
221 | 230 |
222 void AudioInputController::DoReportNoDataError() { | 231 void AudioInputController::DoCheckForNoData() { |
223 DCHECK(creator_loop_->BelongsToCurrentThread()); | 232 DCHECK(message_loop_->BelongsToCurrentThread()); |
224 | 233 |
225 // Error notifications should be sent on the audio-manager thread. | 234 if (!GetDataIsActive()) { |
226 int code = 0; | 235 // The data-is-active marker will be false only if it has been more than |
227 message_loop_->PostTask(FROM_HERE, base::Bind( | 236 // one second since a data packet was recorded. This can happen if a |
228 &AudioInputController::DoReportError, this, code)); | 237 // capture device has been removed or disabled. |
238 handler_->OnError(this, 0); | |
no longer working on chromium
2012/04/17 12:20:41
Shall we delete the timer here?
henrika (OOO until Aug 14)
2012/04/17 13:03:27
No. You must call Close() to delete it. That is do
| |
239 return; | |
240 } | |
241 | |
242 // Mark data as non-active. The flag will be re-enabled in OnData() each | |
243 // time a data packet is received. Hence, under normal conditions, the | |
244 // flag will only be disabled during a very short period. | |
245 SetDataIsActive(false); | |
246 | |
247 // Restart the timer to ensure that we check the flag in one second again. | |
248 DoResetNoDataTimer(); | |
no longer working on chromium
2012/04/17 12:20:41
can we just called no_data_timer_->Reset() here, a
henrika (OOO until Aug 14)
2012/04/17 13:03:27
Done.
| |
229 } | 249 } |
230 | 250 |
231 void AudioInputController::DoResetNoDataTimer() { | 251 void AudioInputController::DoResetNoDataTimer() { |
tommi (sloooow) - chröme
2012/04/17 11:58:31
Can we just remove this method and call no_data_ti
henrika (OOO until Aug 14)
2012/04/17 13:03:27
Done.
| |
232 DCHECK(creator_loop_->BelongsToCurrentThread()); | 252 DCHECK(message_loop_->BelongsToCurrentThread()); |
233 if (no_data_timer_.get()) | 253 if (no_data_timer_.get()) |
234 no_data_timer_->Reset(); | 254 no_data_timer_->Reset(); |
235 } | 255 } |
236 | 256 |
237 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, | 257 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, |
238 uint32 size, uint32 hardware_delay_bytes, | 258 uint32 size, uint32 hardware_delay_bytes, |
239 double volume) { | 259 double volume) { |
240 { | 260 { |
241 base::AutoLock auto_lock(lock_); | 261 base::AutoLock auto_lock(lock_); |
242 if (state_ != kRecording) | 262 if (state_ != kRecording) |
243 return; | 263 return; |
244 } | 264 } |
245 | 265 |
246 creator_loop_->PostTask(FROM_HERE, base::Bind( | 266 // Mark data as active to ensure that the periodic calls to |
247 &AudioInputController::DoResetNoDataTimer, this)); | 267 // DoCheckForNoData() does not report an error to the event handler. |
268 SetDataIsActive(true); | |
248 | 269 |
249 // Use SyncSocket if we are in a low-latency mode. | 270 // Use SyncSocket if we are in a low-latency mode. |
250 if (LowLatencyMode()) { | 271 if (LowLatencyMode()) { |
251 sync_writer_->Write(data, size, volume); | 272 sync_writer_->Write(data, size, volume); |
252 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 273 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
253 return; | 274 return; |
254 } | 275 } |
255 | 276 |
256 handler_->OnData(this, data, size); | 277 handler_->OnData(this, data, size); |
257 } | 278 } |
(...skipping 20 matching lines...) Expand all Loading... | |
278 stream_->Stop(); | 299 stream_->Stop(); |
279 stream_->Close(); | 300 stream_->Close(); |
280 stream_ = NULL; | 301 stream_ = NULL; |
281 } | 302 } |
282 | 303 |
283 // Should be last in the method, do not touch "this" from here on. | 304 // Should be last in the method, do not touch "this" from here on. |
284 if (done != NULL) | 305 if (done != NULL) |
285 done->Signal(); | 306 done->Signal(); |
286 } | 307 } |
287 | 308 |
309 void AudioInputController::SetDataIsActive(bool enabled) { | |
310 base::subtle::Release_Store(&data_is_active_, enabled); | |
311 } | |
312 | |
313 bool AudioInputController::GetDataIsActive() { | |
314 return (base::subtle::Acquire_Load(&data_is_active_) == 1); | |
tommi (sloooow) - chröme
2012/04/17 11:58:31
nit: Since in the Set method you use bool, check a
henrika (OOO until Aug 14)
2012/04/17 13:03:27
Done.
| |
315 } | |
316 | |
288 } // namespace media | 317 } // namespace media |
OLD | NEW |