Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(324)

Side by Side Diff: media/audio/audio_output_controller.cc

Issue 14600025: Replace AudioSilenceDetector with an AudioPowerMonitor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix subtle shutdown race condition. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/audio/audio_output_controller.h ('k') | media/audio/audio_output_controller_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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_output_controller.h" 5 #include "media/audio/audio_output_controller.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/debug/trace_event.h" 8 #include "base/debug/trace_event.h"
9 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/threading/platform_thread.h" 11 #include "base/threading/platform_thread.h"
12 #include "base/time/time.h" 12 #include "base/time/time.h"
13 #include "build/build_config.h" 13 #include "build/build_config.h"
14 #include "media/audio/audio_silence_detector.h" 14 #include "media/audio/audio_power_monitor.h"
15 #include "media/audio/audio_util.h" 15 #include "media/audio/audio_util.h"
16 #include "media/audio/shared_memory_util.h" 16 #include "media/audio/shared_memory_util.h"
17 #include "media/base/bind_to_loop.h"
17 #include "media/base/scoped_histogram_timer.h" 18 #include "media/base/scoped_histogram_timer.h"
18 19
19 using base::Time; 20 using base::Time;
20 using base::TimeDelta; 21 using base::TimeDelta;
21 22
22 namespace media { 23 namespace media {
23 24
24 // Amount of contiguous time where all audio is silent before considering the 25 // Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments for
25 // stream to have transitioned and EventHandler::OnAudible() should be called. 26 // semantics. This value was arbitrarily chosen, but seems to work well.
26 static const int kQuestionableSilencePeriodMillis = 50; 27 static const int kPowerMeasurementTimeConstantMillis = 10;
27 28
28 // Sample value range below which audio is considered indistinguishably silent. 29 // Desired frequency of calls to EventHandler::OnPowerMeasured() for reporting
29 // 30 // power levels in the audio signal.
30 // TODO(miu): This value should be specified in dbFS units rather than full 31 static const int kPowerMeasurementsPerSecond = 30;
31 // scale. See TODO in audio_silence_detector.h.
32 static const float kIndistinguishableSilenceThreshold =
33 1.0f / 4096.0f; // Note: This is approximately -72 dbFS.
34 32
35 // Polling-related constants. 33 // Polling-related constants.
36 const int AudioOutputController::kPollNumAttempts = 3; 34 const int AudioOutputController::kPollNumAttempts = 3;
37 const int AudioOutputController::kPollPauseInMilliseconds = 3; 35 const int AudioOutputController::kPollPauseInMilliseconds = 3;
38 36
39 AudioOutputController::AudioOutputController(AudioManager* audio_manager, 37 AudioOutputController::AudioOutputController(AudioManager* audio_manager,
40 EventHandler* handler, 38 EventHandler* handler,
41 const AudioParameters& params, 39 const AudioParameters& params,
42 const std::string& input_device_id, 40 const std::string& input_device_id,
43 SyncReader* sync_reader) 41 SyncReader* sync_reader)
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 &AudioOutputController::DoPlay, this)); 86 &AudioOutputController::DoPlay, this));
89 } 87 }
90 88
91 void AudioOutputController::Pause() { 89 void AudioOutputController::Pause() {
92 message_loop_->PostTask(FROM_HERE, base::Bind( 90 message_loop_->PostTask(FROM_HERE, base::Bind(
93 &AudioOutputController::DoPause, this)); 91 &AudioOutputController::DoPause, this));
94 } 92 }
95 93
96 void AudioOutputController::Close(const base::Closure& closed_task) { 94 void AudioOutputController::Close(const base::Closure& closed_task) {
97 DCHECK(!closed_task.is_null()); 95 DCHECK(!closed_task.is_null());
98 message_loop_->PostTaskAndReply(FROM_HERE, base::Bind( 96 // See comment in DoClose(), which explains why this can't be a simple
99 &AudioOutputController::DoClose, this), closed_task); 97 // PostTaskAndReply() operation.
98 message_loop_->PostTask(FROM_HERE, base::Bind(
99 &AudioOutputController::DoClose, this, BindToCurrentLoop(closed_task)));
100 } 100 }
101 101
102 void AudioOutputController::SetVolume(double volume) { 102 void AudioOutputController::SetVolume(double volume) {
103 message_loop_->PostTask(FROM_HERE, base::Bind( 103 message_loop_->PostTask(FROM_HERE, base::Bind(
104 &AudioOutputController::DoSetVolume, this, volume)); 104 &AudioOutputController::DoSetVolume, this, volume));
105 } 105 }
106 106
107 void AudioOutputController::DoCreate(bool is_for_device_change) { 107 void AudioOutputController::DoCreate(bool is_for_device_change) {
108 DCHECK(message_loop_->BelongsToCurrentThread()); 108 DCHECK(message_loop_->BelongsToCurrentThread());
109 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); 109 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime");
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); 151 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime");
152 152
153 // We can start from created or paused state. 153 // We can start from created or paused state.
154 if (state_ != kCreated && state_ != kPaused) 154 if (state_ != kCreated && state_ != kPaused)
155 return; 155 return;
156 156
157 // Ask for first packet. 157 // Ask for first packet.
158 sync_reader_->UpdatePendingBytes(0); 158 sync_reader_->UpdatePendingBytes(0);
159 159
160 state_ = kPlaying; 160 state_ = kPlaying;
161 silence_detector_.reset(new AudioSilenceDetector( 161
162 // Start monitoring power levels and send an initial notification that we're
163 // starting in silence.
164 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false);
165 power_monitor_.reset(new AudioPowerMonitor(
162 params_.sample_rate(), 166 params_.sample_rate(),
163 TimeDelta::FromMilliseconds(kQuestionableSilencePeriodMillis), 167 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis),
164 kIndistinguishableSilenceThreshold)); 168 TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond,
169 base::MessageLoop::current(),
170 base::Bind(&EventHandler::OnPowerMeasured, base::Unretained(handler_))));
165 171
166 // We start the AudioOutputStream lazily. 172 // We start the AudioOutputStream lazily.
167 AllowEntryToOnMoreIOData(); 173 AllowEntryToOnMoreIOData();
168 stream_->Start(this); 174 stream_->Start(this);
169 175
170 // Tell the event handler that we are now playing, and also start the silence
171 // detection notifications.
172 handler_->OnPlaying(); 176 handler_->OnPlaying();
173 silence_detector_->Start(
174 base::Bind(&EventHandler::OnAudible, base::Unretained(handler_)));
175 } 177 }
176 178
177 void AudioOutputController::StopStream() { 179 void AudioOutputController::StopStream() {
178 DCHECK(message_loop_->BelongsToCurrentThread()); 180 DCHECK(message_loop_->BelongsToCurrentThread());
179 181
180 if (state_ == kPlaying) { 182 if (state_ == kPlaying) {
181 stream_->Stop(); 183 stream_->Stop();
182 DisallowEntryToOnMoreIOData(); 184 DisallowEntryToOnMoreIOData();
183 silence_detector_->Stop(true); 185 power_monitor_.reset();
184 silence_detector_.reset(); 186 // Send a final notification that we're ending in silence.
187 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false);
185 state_ = kPaused; 188 state_ = kPaused;
186 } 189 }
187 } 190 }
188 191
189 void AudioOutputController::DoPause() { 192 void AudioOutputController::DoPause() {
190 DCHECK(message_loop_->BelongsToCurrentThread()); 193 DCHECK(message_loop_->BelongsToCurrentThread());
191 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); 194 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime");
192 195
193 StopStream(); 196 StopStream();
194 197
195 if (state_ != kPaused) 198 if (state_ != kPaused)
196 return; 199 return;
197 200
198 // Send a special pause mark to the low-latency audio thread. 201 // Send a special pause mark to the low-latency audio thread.
199 sync_reader_->UpdatePendingBytes(kPauseMark); 202 sync_reader_->UpdatePendingBytes(kPauseMark);
200 203
201 handler_->OnPaused(); 204 handler_->OnPaused();
202 } 205 }
203 206
204 void AudioOutputController::DoClose() { 207 void AudioOutputController::DoClose(const base::Closure& done_callback) {
205 DCHECK(message_loop_->BelongsToCurrentThread()); 208 DCHECK(message_loop_->BelongsToCurrentThread());
206 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); 209 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime");
207 210
208 if (state_ != kClosed) { 211 if (state_ != kClosed) {
209 DoStopCloseAndClearStream(); 212 DoStopCloseAndClearStream();
210 sync_reader_->Close(); 213 sync_reader_->Close();
211 state_ = kClosed; 214 state_ = kClosed;
212 } 215 }
216
217 // The AudioOutputController is now closed. Until a playing stream was fully
DaleCurtis 2013/07/22 17:27:21 Is it really necessary to send a zero during close
miu 2013/07/22 21:10:48 You're right, this is unnecessary since the final
218 // stopped, however, there may have been additional tasks posted to
219 // |message_loop_| (e.g., by the AudioPowerMonitor). Such tasks may attempt
220 // to invoke EventHandler methods. Therefore, ensure that |done_callback| is
221 // run *after* those tasks to guarantee EventHandler remains alive for them.
222 message_loop_->PostTask(FROM_HERE, done_callback);
213 } 223 }
214 224
215 void AudioOutputController::DoSetVolume(double volume) { 225 void AudioOutputController::DoSetVolume(double volume) {
216 DCHECK(message_loop_->BelongsToCurrentThread()); 226 DCHECK(message_loop_->BelongsToCurrentThread());
217 227
218 // Saves the volume to a member first. We may not be able to set the volume 228 // Saves the volume to a member first. We may not be able to set the volume
219 // right away but when the stream is created we'll set the volume. 229 // right away but when the stream is created we'll set the volume.
220 volume_ = volume; 230 volume_ = volume;
221 231
222 switch (state_) { 232 switch (state_) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 const bool kShouldBlock = true; 273 const bool kShouldBlock = true;
264 #else 274 #else
265 const bool kShouldBlock = diverting_to_stream_ != NULL; 275 const bool kShouldBlock = diverting_to_stream_ != NULL;
266 #endif 276 #endif
267 277
268 const int frames = sync_reader_->Read(kShouldBlock, source, dest); 278 const int frames = sync_reader_->Read(kShouldBlock, source, dest);
269 DCHECK_LE(0, frames); 279 DCHECK_LE(0, frames);
270 sync_reader_->UpdatePendingBytes( 280 sync_reader_->UpdatePendingBytes(
271 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); 281 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
272 282
273 silence_detector_->Scan(dest, frames); 283 power_monitor_->Scan(*dest, frames);
274 284
275 AllowEntryToOnMoreIOData(); 285 AllowEntryToOnMoreIOData();
276 return frames; 286 return frames;
277 } 287 }
278 288
279 void AudioOutputController::OnError(AudioOutputStream* stream) { 289 void AudioOutputController::OnError(AudioOutputStream* stream) {
280 // Handle error on the audio controller thread. 290 // Handle error on the audio controller thread.
281 message_loop_->PostTask(FROM_HERE, base::Bind( 291 message_loop_->PostTask(FROM_HERE, base::Bind(
282 &AudioOutputController::DoReportError, this)); 292 &AudioOutputController::DoReportError, this));
283 } 293 }
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); 387 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_));
378 base::AtomicRefCountInc(&num_allowed_io_); 388 base::AtomicRefCountInc(&num_allowed_io_);
379 } 389 }
380 390
381 void AudioOutputController::DisallowEntryToOnMoreIOData() { 391 void AudioOutputController::DisallowEntryToOnMoreIOData() {
382 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); 392 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_);
383 DCHECK(is_zero); 393 DCHECK(is_zero);
384 } 394 }
385 395
386 } // namespace media 396 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/audio_output_controller.h ('k') | media/audio/audio_output_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698