| Index: media/audio/android/opensles_output.cc
|
| diff --git a/media/audio/android/opensles_output.cc b/media/audio/android/opensles_output.cc
|
| index 2974e1418ca57454bfeff4c5abbfba4631860015..8587182efb4e67766097bf1b8aa76f8867e80ddb 100644
|
| --- a/media/audio/android/opensles_output.cc
|
| +++ b/media/audio/android/opensles_output.cc
|
| @@ -5,6 +5,8 @@
|
| #include "media/audio/android/opensles_output.h"
|
|
|
| #include "base/logging.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/thread_task_runner_handle.h"
|
| #include "base/trace_event/trace_event.h"
|
| #include "media/audio/android/audio_manager_android.h"
|
|
|
| @@ -19,6 +21,11 @@
|
|
|
| namespace media {
|
|
|
| +// Threshold for consecutive number of empty buffers to let the player enter
|
| +// idle state.
|
| +// TODO(qinmin): change this threshold to count the number of frames.
|
| +static const int kEmptyBufferThreasholdForIdleMode = 2;
|
| +
|
| OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
|
| const AudioParameters& params,
|
| SLint32 stream_type)
|
| @@ -31,7 +38,10 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
|
| buffer_size_bytes_(0),
|
| started_(false),
|
| muted_(false),
|
| - volume_(1.0) {
|
| + volume_(1.0),
|
| + consecutive_empty_buffer_count_(0),
|
| + task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| + weak_factory_(this) {
|
| DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream("
|
| << "stream_type=" << stream_type << ")";
|
| format_.formatType = SL_DATAFORMAT_PCM;
|
| @@ -52,6 +62,8 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
|
| audio_bus_ = AudioBus::Create(params);
|
|
|
| memset(&audio_data_, 0, sizeof(audio_data_));
|
| + weak_this_ = weak_factory_.GetWeakPtr();
|
| + audio_timestamp_helper_.reset(new AudioTimestampHelper(params.sample_rate()));
|
| }
|
|
|
| OpenSLESOutputStream::~OpenSLESOutputStream() {
|
| @@ -95,7 +107,7 @@ void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
|
|
|
| // Avoid start-up glitches by filling up one buffer queue before starting
|
| // the stream.
|
| - FillBufferQueueNoLock();
|
| + FillBufferQueueNoLock(false);
|
|
|
| // Start streaming data by setting the play state to SL_PLAYSTATE_PLAYING.
|
| // For a player object, when the object is in the SL_PLAYSTATE_PLAYING
|
| @@ -134,6 +146,8 @@ void OpenSLESOutputStream::Stop() {
|
|
|
| callback_ = NULL;
|
| started_ = false;
|
| + CancelIdleCheck();
|
| + consecutive_empty_buffer_count_ = 0;
|
| }
|
|
|
| void OpenSLESOutputStream::Close() {
|
| @@ -167,14 +181,12 @@ void OpenSLESOutputStream::SetVolume(double volume) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
| double volume_override = 0;
|
| - if (audio_manager_->HasOutputVolumeOverride(&volume_override)) {
|
| + if (audio_manager_->HasOutputVolumeOverride(&volume_override))
|
| volume = volume_override;
|
| - }
|
|
|
| float volume_float = static_cast<float>(volume);
|
| - if (volume_float < 0.0f || volume_float > 1.0f) {
|
| + if (volume_float < 0.0f || volume_float > 1.0f)
|
| return;
|
| - }
|
| volume_ = volume_float;
|
| }
|
|
|
| @@ -319,10 +331,10 @@ void OpenSLESOutputStream::FillBufferQueue() {
|
|
|
| // Fill up one buffer in the queue by asking the registered source for
|
| // data using the OnMoreData() callback.
|
| - FillBufferQueueNoLock();
|
| + FillBufferQueueNoLock(false);
|
| }
|
|
|
| -void OpenSLESOutputStream::FillBufferQueueNoLock() {
|
| +void OpenSLESOutputStream::FillBufferQueueNoLock(bool in_idle_mode) {
|
| // Ensure that the calling thread has acquired the lock since it is not
|
| // done in this method.
|
| lock_.AssertAcquired();
|
| @@ -331,13 +343,24 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() {
|
| // TODO(henrika): Investigate if it is possible to get a more accurate
|
| // delay estimation.
|
| const uint32 hardware_delay = buffer_size_bytes_;
|
| - int frames_filled = callback_->OnMoreData(
|
| - audio_bus_.get(), hardware_delay);
|
| + int frames_filled = callback_->OnMoreData(audio_bus_.get(), hardware_delay);
|
| if (frames_filled <= 0) {
|
| // Audio source is shutting down, or halted on error.
|
| return;
|
| }
|
|
|
| + // Skip enqueueing data if the output is empty to save power.
|
| + if (audio_bus_->AreFramesZero()) {
|
| + // TODO(qinmin): check if this variable overflows.
|
| + consecutive_empty_buffer_count_++;
|
| + if (consecutive_empty_buffer_count_ >= kEmptyBufferThreasholdForIdleMode) {
|
| + WaitingForAudio(in_idle_mode, frames_filled, base::TimeTicks::Now());
|
| + return;
|
| + }
|
| + } else {
|
| + consecutive_empty_buffer_count_ = 0;
|
| + }
|
| +
|
| // Note: If the internal representation ever changes from 16-bit PCM to
|
| // raw float, the data must be clipped and sanitized since it may come
|
| // from an untrusted source such as NaCl.
|
| @@ -364,9 +387,8 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() {
|
| void OpenSLESOutputStream::SetupAudioBuffer() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| DCHECK(!audio_data_[0]);
|
| - for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
|
| + for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i)
|
| audio_data_[i] = new uint8[buffer_size_bytes_];
|
| - }
|
| }
|
|
|
| void OpenSLESOutputStream::ReleaseAudioBuffer() {
|
| @@ -383,6 +405,55 @@ void OpenSLESOutputStream::HandleError(SLresult error) {
|
| DLOG(ERROR) << "OpenSLES Output error " << error;
|
| if (callback_)
|
| callback_->OnError(this);
|
| + CancelIdleCheck();
|
| +}
|
| +
|
| +void OpenSLESOutputStream::CheckForIdle() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + DCHECK(!idle_check_callback_.IsCancelled());
|
| +
|
| + if (!started_)
|
| + return;
|
| + base::AutoLock lock(lock_);
|
| + FillBufferQueueNoLock(true);
|
| +}
|
| +
|
| +void OpenSLESOutputStream::CancelIdleCheck() {
|
| + if (!task_runner_->BelongsToCurrentThread()) {
|
| + task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&OpenSLESOutputStream::CancelIdleCheck, weak_this_));
|
| + return;
|
| + }
|
| + idle_check_callback_.Cancel();
|
| +}
|
| +
|
| +void OpenSLESOutputStream::WaitingForAudio(
|
| + bool in_idle_mode, int frames_filled, base::TimeTicks idle_start_time) {
|
| + if (!task_runner_->BelongsToCurrentThread()) {
|
| + task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&OpenSLESOutputStream::WaitingForAudio,
|
| + weak_this_, in_idle_mode, frames_filled, idle_start_time));
|
| + return;
|
| + }
|
| +
|
| + if (!in_idle_mode) {
|
| + audio_timestamp_helper_->SetBaseTimestamp(base::TimeDelta());
|
| + idle_start_time_ = idle_start_time;
|
| + idle_check_callback_.Reset(
|
| + base::Bind(&OpenSLESOutputStream::CheckForIdle, weak_this_));
|
| + }
|
| +
|
| + audio_timestamp_helper_->AddFrames(frames_filled);
|
| + base::TimeDelta delay_to_next_read = audio_timestamp_helper_->GetTimestamp() +
|
| + (idle_start_time_ - base::TimeTicks::Now());
|
| + if (delay_to_next_read < base::TimeDelta())
|
| + delay_to_next_read = base::TimeDelta();
|
| + task_runner_->PostDelayedTask(
|
| + FROM_HERE,
|
| + idle_check_callback_.callback(),
|
| + delay_to_next_read);
|
| }
|
|
|
| } // namespace media
|
|
|