Chromium Code Reviews| 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_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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |