Chromium Code Reviews| Index: media/filters/audio_renderer_impl.cc |
| diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc |
| index 8fd5ae6267607e80f71f2ed3fb0e3ecd6b3f8a3c..ee2a64acf7d64251eedacb1ec13bcd13d741fbd3 100644 |
| --- a/media/filters/audio_renderer_impl.cc |
| +++ b/media/filters/audio_renderer_impl.cc |
| @@ -14,6 +14,7 @@ |
| #include "base/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/single_thread_task_runner.h" |
| +#include "base/time/default_tick_clock.h" |
| #include "media/base/audio_buffer.h" |
| #include "media/base/audio_buffer_converter.h" |
| #include "media/base/audio_hardware_config.h" |
| @@ -40,6 +41,14 @@ void HistogramRendererEvent(AudioRendererEvent event) { |
| } // namespace |
| +AudioRendererImpl::RenderResult::RenderResult() |
| + : requested_frames(0), |
| + delay_frames(0), |
| + frames_written(0), |
| + playback_rate(0), |
| + endpoint_timestamp(kNoTimestamp()) { |
| +} |
| + |
| AudioRendererImpl::AudioRendererImpl( |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| media::AudioRendererSink* sink, |
| @@ -52,7 +61,7 @@ AudioRendererImpl::AudioRendererImpl( |
| decoders.Pass(), |
| set_decryptor_ready_cb), |
| hardware_config_(hardware_config), |
| - now_cb_(base::Bind(&base::TimeTicks::Now)), |
| + tick_clock_(new base::DefaultTickClock()), |
| state_(kUninitialized), |
| buffering_state_(BUFFERING_HAVE_NOTHING), |
| rendering_(false), |
| @@ -176,7 +185,7 @@ void AudioRendererImpl::ResetDecoderDone() { |
| // Flush() may have been called while underflowed/not fully buffered. |
| if (buffering_state_ != BUFFERING_HAVE_NOTHING) |
| - SetBufferingState_Locked(BUFFERING_HAVE_NOTHING); |
| + SetBufferingState(BUFFERING_HAVE_NOTHING); |
| splicer_->Reset(); |
| if (buffer_converter_) |
| @@ -360,6 +369,11 @@ void AudioRendererImpl::SetVolume(float volume) { |
| sink_->SetVolume(volume); |
| } |
| +void AudioRendererImpl::SetTickClockForTesting( |
| + scoped_ptr<base::TickClock> tick_clock) { |
| + tick_clock_.swap(tick_clock); |
| +} |
| + |
| void AudioRendererImpl::DecodedAudioReady( |
| AudioBufferStream::Status status, |
| const scoped_refptr<AudioBuffer>& buffer) { |
| @@ -417,7 +431,7 @@ void AudioRendererImpl::DecodedAudioReady( |
| while (splicer_->HasNextBuffer()) |
| need_another_buffer = HandleSplicerBuffer_Locked(splicer_->GetNextBuffer()); |
| - if (!need_another_buffer && !CanRead_Locked()) |
| + if (!need_another_buffer) |
| return; |
| AttemptRead_Locked(); |
| @@ -463,7 +477,7 @@ bool AudioRendererImpl::HandleSplicerBuffer_Locked( |
| case kPlaying: |
| if (buffer->end_of_stream() || algorithm_->IsQueueFull()) { |
| if (buffering_state_ == BUFFERING_HAVE_NOTHING) |
| - SetBufferingState_Locked(BUFFERING_HAVE_ENOUGH); |
| + SetBufferingState(BUFFERING_HAVE_ENOUGH); |
| return false; |
| } |
| return true; |
| @@ -474,40 +488,28 @@ bool AudioRendererImpl::HandleSplicerBuffer_Locked( |
| return false; |
| } |
| -void AudioRendererImpl::AttemptRead() { |
| - base::AutoLock auto_lock(lock_); |
| - AttemptRead_Locked(); |
| -} |
| - |
| void AudioRendererImpl::AttemptRead_Locked() { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| lock_.AssertAcquired(); |
| - if (!CanRead_Locked()) |
| - return; |
| - |
| - pending_read_ = true; |
| - audio_buffer_stream_.Read(base::Bind(&AudioRendererImpl::DecodedAudioReady, |
| - weak_factory_.GetWeakPtr())); |
| -} |
| - |
| -bool AudioRendererImpl::CanRead_Locked() { |
| - lock_.AssertAcquired(); |
| - |
| switch (state_) { |
| case kUninitialized: |
| case kInitializing: |
| case kFlushing: |
| case kFlushed: |
| case kStopped: |
| - return false; |
| + return; |
| case kPlaying: |
| break; |
| } |
| - return !pending_read_ && !received_end_of_stream_ && |
| - !algorithm_->IsQueueFull(); |
| + if (pending_read_ || received_end_of_stream_ || algorithm_->IsQueueFull()) |
| + return; |
| + |
| + pending_read_ = true; |
| + audio_buffer_stream_.Read(base::Bind(&AudioRendererImpl::DecodedAudioReady, |
| + weak_factory_.GetWeakPtr())); |
| } |
| void AudioRendererImpl::SetPlaybackRate(float playback_rate) { |
| @@ -547,92 +549,83 @@ bool AudioRendererImpl::IsBeforeStartTime( |
| int AudioRendererImpl::Render(AudioBus* audio_bus, |
| int audio_delay_milliseconds) { |
| - const int requested_frames = audio_bus->frames(); |
| - base::TimeDelta playback_delay = base::TimeDelta::FromMilliseconds( |
| - audio_delay_milliseconds); |
| - const int delay_frames = static_cast<int>(playback_delay.InSecondsF() * |
| - audio_parameters_.sample_rate()); |
| - int frames_written = 0; |
| - base::Closure time_cb; |
| + DVLOG(2) << __FUNCTION__; |
| + base::TimeDelta playback_delay = |
| + base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); |
| + |
| + RenderResult result; |
| + result.ticks = tick_clock_->NowTicks(); |
| + result.requested_frames = audio_bus->frames(); |
| + result.delay_frames = static_cast<int>(playback_delay.InSecondsF() * |
| + audio_parameters_.sample_rate()); |
| + |
| { |
| base::AutoLock auto_lock(lock_); |
| - |
| - // Ensure Stop() hasn't destroyed our |algorithm_| on the pipeline thread. |
| - if (!algorithm_) { |
| - audio_clock_->WroteSilence(requested_frames, delay_frames); |
| - return 0; |
| + if (state_ == kPlaying && algorithm_->frames_buffered() > 0) { |
| + result.frames_written = |
| + algorithm_->FillBuffer(audio_bus, result.requested_frames); |
| + result.playback_rate = algorithm_->playback_rate(); |
| + result.endpoint_timestamp = algorithm_->GetTime(); |
| } |
| + } |
| - float playback_rate = algorithm_->playback_rate(); |
| - if (playback_rate == 0) { |
| - audio_clock_->WroteSilence(requested_frames, delay_frames); |
| - return 0; |
| - } |
| + task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &AudioRendererImpl::DidRender, weak_factory_.GetWeakPtr(), result)); |
| - // Mute audio by returning 0 when not playing. |
| - if (state_ != kPlaying) { |
| - audio_clock_->WroteSilence(requested_frames, delay_frames); |
| - return 0; |
| - } |
| + return result.frames_written; |
| +} |
| - // We use the following conditions to determine end of playback: |
| - // 1) Algorithm can not fill the audio callback buffer |
| - // 2) We received an end of stream buffer |
| - // 3) We haven't already signalled that we've ended |
| - // 4) We've played all known audio data sent to hardware |
| - // |
| - // We use the following conditions to determine underflow: |
| - // 1) Algorithm can not fill the audio callback buffer |
| - // 2) We have NOT received an end of stream buffer |
| - // 3) We are in the kPlaying state |
| - // |
| - // Otherwise the buffer has data we can send to the device. |
| - const base::TimeDelta media_timestamp_before_filling = |
| - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()); |
| - if (algorithm_->frames_buffered() > 0) { |
| - frames_written = algorithm_->FillBuffer(audio_bus, requested_frames); |
| - audio_clock_->WroteAudio( |
| - frames_written, delay_frames, playback_rate, algorithm_->GetTime()); |
| - } |
| - audio_clock_->WroteSilence(requested_frames - frames_written, delay_frames); |
| - |
| - if (frames_written == 0) { |
| - if (received_end_of_stream_ && !rendered_end_of_stream_ && |
| - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) == |
| - audio_clock_->last_endpoint_timestamp()) { |
| - rendered_end_of_stream_ = true; |
| - ended_cb_.Run(); |
| - } else if (!received_end_of_stream_ && state_ == kPlaying) { |
| - if (buffering_state_ != BUFFERING_HAVE_NOTHING) { |
| - algorithm_->IncreaseQueueCapacity(); |
| - SetBufferingState_Locked(BUFFERING_HAVE_NOTHING); |
| - } |
| - } |
| - } |
| +void AudioRendererImpl::DidRender(RenderResult result) { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__; |
| - if (CanRead_Locked()) { |
| - task_runner_->PostTask(FROM_HERE, |
| - base::Bind(&AudioRendererImpl::AttemptRead, |
| - weak_factory_.GetWeakPtr())); |
| - } |
| + base::AutoLock auto_lock(lock_); |
| + if (state_ == kStopped) |
| + return; |
| - // We only want to execute |time_cb_| if time has progressed and we haven't |
| - // signaled end of stream yet. |
| - if (media_timestamp_before_filling != |
| - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) && |
| - !rendered_end_of_stream_) { |
| - time_cb = |
| - base::Bind(time_cb_, |
| - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()), |
| - audio_clock_->last_endpoint_timestamp()); |
| + if (result.frames_written > 0) { |
| + audio_clock_->WroteAudio(result.frames_written, |
| + result.delay_frames, |
| + result.playback_rate, |
| + result.endpoint_timestamp); |
| + } |
| + audio_clock_->WroteSilence(result.requested_frames - result.frames_written, |
| + result.delay_frames); |
| + |
| + // We use the following conditions to determine end of playback: |
| + // 1) Algorithm can not fill the audio callback buffer |
| + // 2) We received an end of stream buffer |
| + // 3) We haven't already signalled that we've ended |
| + // 4) We've played all known audio data sent to hardware |
| + // |
| + // We use the following conditions to determine underflow: |
| + // 1) Algorithm can not fill the audio callback buffer |
| + // 2) We have NOT received an end of stream buffer |
| + // 3) We are in the kPlaying state |
| + if (result.frames_written == 0) { |
| + if (received_end_of_stream_ && !rendered_end_of_stream_ && |
| + audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) == |
|
DaleCurtis
2014/07/15 19:25:28
Shouldn't this be using the time since writing?
scherkus (not reviewing)
2014/07/15 20:14:22
Maybe ... I'm still thinking what the right option
|
| + audio_clock_->last_endpoint_timestamp()) { |
| + rendered_end_of_stream_ = true; |
| + ended_cb_.Run(); |
| + } else if (!received_end_of_stream_ && state_ == kPlaying) { |
| + if (buffering_state_ != BUFFERING_HAVE_NOTHING) { |
| + algorithm_->IncreaseQueueCapacity(); |
| + SetBufferingState(BUFFERING_HAVE_NOTHING); |
| + } |
| } |
| } |
| - if (!time_cb.is_null()) |
| - task_runner_->PostTask(FROM_HERE, time_cb); |
| + // Use adjusted media timestamp that takes time since writing into account. |
| + if (!rendered_end_of_stream_) { |
| + base::TimeDelta time_since_writing = tick_clock_->NowTicks() - result.ticks; |
| + time_cb_.Run(audio_clock_->CurrentMediaTimestamp(time_since_writing), |
| + audio_clock_->last_endpoint_timestamp()); |
| + } |
| - DCHECK_LE(frames_written, requested_frames); |
| - return frames_written; |
| + AttemptRead_Locked(); |
| } |
| void AudioRendererImpl::OnRenderError() { |
| @@ -695,12 +688,11 @@ void AudioRendererImpl::OnConfigChange() { |
| CHECK(splicer_->AddInput(buffer_converter_->GetNextBuffer())); |
| } |
| -void AudioRendererImpl::SetBufferingState_Locked( |
| - BufferingState buffering_state) { |
| +void AudioRendererImpl::SetBufferingState(BufferingState buffering_state) { |
| DVLOG(1) << __FUNCTION__ << " : " << buffering_state_ << " -> " |
| << buffering_state; |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| DCHECK_NE(buffering_state_, buffering_state); |
| - lock_.AssertAcquired(); |
| buffering_state_ = buffering_state; |
| task_runner_->PostTask(FROM_HERE, |