| Index: media/filters/audio_renderer_base.cc
|
| diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
|
| index eda4d9eca6532b191fb62581f64332f967eb2176..21d9bc5980e473753cb7c12361f10b8dafb9cf97 100644
|
| --- a/media/filters/audio_renderer_base.cc
|
| +++ b/media/filters/audio_renderer_base.cc
|
| @@ -4,23 +4,27 @@
|
|
|
| #include "media/filters/audio_renderer_base.h"
|
|
|
| -#include <algorithm>
|
| -#include <string>
|
| +#include <math.h>
|
|
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| #include "base/callback_helpers.h"
|
| #include "base/logging.h"
|
| #include "media/base/filter_host.h"
|
| +#include "media/audio/audio_util.h"
|
|
|
| namespace media {
|
|
|
| -AudioRendererBase::AudioRendererBase()
|
| +AudioRendererBase::AudioRendererBase(media::AudioRendererSink* sink)
|
| : state_(kUninitialized),
|
| pending_read_(false),
|
| received_end_of_stream_(false),
|
| rendered_end_of_stream_(false),
|
| bytes_per_frame_(0),
|
| + bytes_per_second_(0),
|
| + stopped_(false),
|
| + sink_(sink),
|
| + is_initialized_(false),
|
| read_cb_(base::Bind(&AudioRendererBase::DecodedAudioReady,
|
| base::Unretained(this))) {
|
| }
|
| @@ -32,25 +36,53 @@ AudioRendererBase::~AudioRendererBase() {
|
| }
|
|
|
| void AudioRendererBase::Play(const base::Closure& callback) {
|
| - base::AutoLock auto_lock(lock_);
|
| - DCHECK_EQ(kPaused, state_);
|
| - state_ = kPlaying;
|
| - callback.Run();
|
| -}
|
| + {
|
| + base::AutoLock auto_lock(lock_);
|
| + DCHECK_EQ(kPaused, state_);
|
| + state_ = kPlaying;
|
| + callback.Run();
|
| + }
|
|
|
| -void AudioRendererBase::Pause(const base::Closure& callback) {
|
| - base::AutoLock auto_lock(lock_);
|
| - DCHECK(state_ == kPlaying || state_ == kUnderflow || state_ == kRebuffering);
|
| - pause_cb_ = callback;
|
| - state_ = kPaused;
|
| + if (stopped_)
|
| + return;
|
|
|
| - // Pause only when we've completed our pending read.
|
| - if (!pending_read_) {
|
| - pause_cb_.Run();
|
| - pause_cb_.Reset();
|
| + if (GetPlaybackRate() != 0.0f) {
|
| + DoPlay();
|
| } else {
|
| + DoPause();
|
| + }
|
| +}
|
| +
|
| +void AudioRendererBase::DoPlay() {
|
| + earliest_end_time_ = base::Time::Now();
|
| + DCHECK(sink_.get());
|
| + sink_->Play();
|
| +}
|
| +
|
| +void AudioRendererBase::Pause(const base::Closure& callback) {
|
| + {
|
| + base::AutoLock auto_lock(lock_);
|
| + DCHECK(state_ == kPlaying || state_ == kUnderflow ||
|
| + state_ == kRebuffering);
|
| + pause_cb_ = callback;
|
| state_ = kPaused;
|
| +
|
| + // Pause only when we've completed our pending read.
|
| + if (!pending_read_) {
|
| + pause_cb_.Run();
|
| + pause_cb_.Reset();
|
| + }
|
| }
|
| +
|
| + if (stopped_)
|
| + return;
|
| +
|
| + DoPause();
|
| +}
|
| +
|
| +void AudioRendererBase::DoPause() {
|
| + DCHECK(sink_.get());
|
| + sink_->Pause(false);
|
| }
|
|
|
| void AudioRendererBase::Flush(const base::Closure& callback) {
|
| @@ -58,7 +90,12 @@ void AudioRendererBase::Flush(const base::Closure& callback) {
|
| }
|
|
|
| void AudioRendererBase::Stop(const base::Closure& callback) {
|
| - OnStop();
|
| + if (!stopped_) {
|
| + DCHECK(sink_.get());
|
| + sink_->Stop();
|
| +
|
| + stopped_ = true;
|
| + }
|
| {
|
| base::AutoLock auto_lock(lock_);
|
| state_ = kStopped;
|
| @@ -82,12 +119,24 @@ void AudioRendererBase::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
|
| seek_timestamp_ = time;
|
|
|
| // Throw away everything and schedule our reads.
|
| - last_fill_buffer_time_ = base::TimeDelta();
|
| + audio_time_buffered_ = base::TimeDelta();
|
| received_end_of_stream_ = false;
|
| rendered_end_of_stream_ = false;
|
|
|
| // |algorithm_| will request more reads.
|
| algorithm_->FlushBuffers();
|
| +
|
| + if (stopped_)
|
| + return;
|
| +
|
| + DoSeek();
|
| +}
|
| +
|
| +void AudioRendererBase::DoSeek() {
|
| + earliest_end_time_ = base::Time::Now();
|
| +
|
| + // Pause and flush the stream when we seek to a new location.
|
| + sink_->Pause(true);
|
| }
|
|
|
| void AudioRendererBase::Initialize(const scoped_refptr<AudioDecoder>& decoder,
|
| @@ -120,16 +169,32 @@ void AudioRendererBase::Initialize(const scoped_refptr<AudioDecoder>& decoder,
|
|
|
| bool config_ok = algorithm_->ValidateConfig(channels, sample_rate,
|
| bits_per_channel);
|
| - if (config_ok)
|
| - algorithm_->Initialize(channels, sample_rate, bits_per_channel, 0.0f, cb);
|
| -
|
| - // Give the subclass an opportunity to initialize itself.
|
| - if (!config_ok || !OnInitialize(bits_per_channel, channel_layout,
|
| - sample_rate)) {
|
| + if (!config_ok || is_initialized_) {
|
| init_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
|
| return;
|
| }
|
|
|
| + if (config_ok)
|
| + algorithm_->Initialize(channels, sample_rate, bits_per_channel, 0.0f, cb);
|
| +
|
| + // We use the AUDIO_PCM_LINEAR flag because AUDIO_PCM_LOW_LATENCY
|
| + // does not currently support all the sample-rates that we require.
|
| + // Please see: http://code.google.com/p/chromium/issues/detail?id=103627
|
| + // for more details.
|
| + audio_parameters_ = AudioParameters(
|
| + AudioParameters::AUDIO_PCM_LINEAR, channel_layout, sample_rate,
|
| + bits_per_channel, GetHighLatencyOutputBufferSize(sample_rate));
|
| +
|
| + bytes_per_second_ = audio_parameters_.GetBytesPerSecond();
|
| +
|
| + DCHECK(sink_.get());
|
| + DCHECK(!is_initialized_);
|
| +
|
| + sink_->Initialize(audio_parameters_, this);
|
| +
|
| + sink_->Start();
|
| + is_initialized_ = true;
|
| +
|
| // Finally, execute the start callback.
|
| state_ = kPaused;
|
| init_cb.Run(PIPELINE_OK);
|
| @@ -152,6 +217,12 @@ void AudioRendererBase::ResumeAfterUnderflow(bool buffer_more_audio) {
|
| }
|
| }
|
|
|
| +void AudioRendererBase::SetVolume(float volume) {
|
| + if (stopped_)
|
| + return;
|
| + sink_->SetVolume(volume);
|
| +}
|
| +
|
| void AudioRendererBase::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
|
| base::AutoLock auto_lock(lock_);
|
| DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying ||
|
| @@ -203,12 +274,115 @@ void AudioRendererBase::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
|
| }
|
| }
|
|
|
| +void AudioRendererBase::SignalEndOfStream() {
|
| + DCHECK(received_end_of_stream_);
|
| + if (!rendered_end_of_stream_) {
|
| + rendered_end_of_stream_ = true;
|
| + host()->NotifyEnded();
|
| + }
|
| +}
|
| +
|
| +void AudioRendererBase::ScheduleRead_Locked() {
|
| + lock_.AssertAcquired();
|
| + if (pending_read_ || state_ == kPaused)
|
| + return;
|
| + pending_read_ = true;
|
| + decoder_->Read(read_cb_);
|
| +}
|
| +
|
| +void AudioRendererBase::SetPlaybackRate(float playback_rate) {
|
| + DCHECK_LE(0.0f, playback_rate);
|
| +
|
| + if (!stopped_) {
|
| + // Notify sink of new playback rate.
|
| + sink_->SetPlaybackRate(playback_rate);
|
| +
|
| + // We have two cases here:
|
| + // Play: GetPlaybackRate() == 0.0 && playback_rate != 0.0
|
| + // Pause: GetPlaybackRate() != 0.0 && playback_rate == 0.0
|
| + if (GetPlaybackRate() == 0.0f && playback_rate != 0.0f) {
|
| + DoPlay();
|
| + } else if (GetPlaybackRate() != 0.0f && playback_rate == 0.0f) {
|
| + // Pause is easy, we can always pause.
|
| + DoPause();
|
| + }
|
| + }
|
| +
|
| + base::AutoLock auto_lock(lock_);
|
| + algorithm_->SetPlaybackRate(playback_rate);
|
| +}
|
| +
|
| +float AudioRendererBase::GetPlaybackRate() {
|
| + base::AutoLock auto_lock(lock_);
|
| + return algorithm_->playback_rate();
|
| +}
|
| +
|
| +bool AudioRendererBase::IsBeforeSeekTime(const scoped_refptr<Buffer>& buffer) {
|
| + return (state_ == kSeeking) && buffer && !buffer->IsEndOfStream() &&
|
| + (buffer->GetTimestamp() + buffer->GetDuration()) < seek_timestamp_;
|
| +}
|
| +
|
| +int AudioRendererBase::Render(const std::vector<float*>& audio_data,
|
| + int number_of_frames,
|
| + int audio_delay_milliseconds) {
|
| + if (stopped_ || GetPlaybackRate() == 0.0f) {
|
| + // Output silence if stopped.
|
| + for (size_t i = 0; i < audio_data.size(); ++i)
|
| + memset(audio_data[i], 0, sizeof(float) * number_of_frames);
|
| + return 0;
|
| + }
|
| +
|
| + // Adjust the playback delay.
|
| + base::TimeDelta request_delay =
|
| + base::TimeDelta::FromMilliseconds(audio_delay_milliseconds);
|
| +
|
| + // Finally we need to adjust the delay according to playback rate.
|
| + if (GetPlaybackRate() != 1.0f) {
|
| + request_delay = base::TimeDelta::FromMicroseconds(
|
| + static_cast<int64>(ceil(request_delay.InMicroseconds() *
|
| + GetPlaybackRate())));
|
| + }
|
| +
|
| + int bytes_per_frame = audio_parameters_.GetBytesPerFrame();
|
| +
|
| + const int buf_size = number_of_frames * bytes_per_frame;
|
| + scoped_array<uint8> buf(new uint8[buf_size]);
|
| +
|
| + int frames_filled = FillBuffer(buf.get(), number_of_frames, request_delay);
|
| + int bytes_filled = frames_filled * bytes_per_frame;
|
| + DCHECK_LE(bytes_filled, buf_size);
|
| + UpdateEarliestEndTime(bytes_filled, request_delay, base::Time::Now());
|
| +
|
| + // Deinterleave each audio channel.
|
| + int channels = audio_data.size();
|
| + for (int channel_index = 0; channel_index < channels; ++channel_index) {
|
| + media::DeinterleaveAudioChannel(buf.get(),
|
| + audio_data[channel_index],
|
| + channels,
|
| + channel_index,
|
| + bytes_per_frame / channels,
|
| + frames_filled);
|
| +
|
| + // If FillBuffer() didn't give us enough data then zero out the remainder.
|
| + if (frames_filled < number_of_frames) {
|
| + int frames_to_zero = number_of_frames - frames_filled;
|
| + memset(audio_data[channel_index] + frames_filled,
|
| + 0,
|
| + sizeof(float) * frames_to_zero);
|
| + }
|
| + }
|
| + return frames_filled;
|
| +}
|
| +
|
| uint32 AudioRendererBase::FillBuffer(uint8* dest,
|
| uint32 requested_frames,
|
| const base::TimeDelta& playback_delay) {
|
| - // The timestamp of the last buffer written during the last call to
|
| - // FillBuffer().
|
| - base::TimeDelta last_fill_buffer_time;
|
| + // The |audio_time_buffered_| is the ending timestamp of the last frame
|
| + // buffered at the audio device. |playback_delay| is the amount of time
|
| + // buffered at the audio device. The current time can be computed by their
|
| + // difference.
|
| + base::TimeDelta current_time = audio_time_buffered_ - playback_delay;
|
| +
|
| size_t frames_written = 0;
|
| base::Closure underflow_cb;
|
| {
|
| @@ -232,10 +406,6 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest,
|
| return zeros_to_write / bytes_per_frame_;
|
| }
|
|
|
| - // Save a local copy of last fill buffer time and reset the member.
|
| - last_fill_buffer_time = last_fill_buffer_time_;
|
| - last_fill_buffer_time_ = base::TimeDelta();
|
| -
|
| // Use three conditions to determine the end of playback:
|
| // 1. Algorithm needs more audio data.
|
| // 2. We've received an end of stream buffer.
|
| @@ -251,7 +421,9 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest,
|
| // 3. Have not received an end of stream buffer.
|
| if (algorithm_->NeedsMoreData()) {
|
| if (received_end_of_stream_) {
|
| - OnRenderEndOfStream();
|
| + // TODO(enal): schedule callback instead of polling.
|
| + if (base::Time::Now() >= earliest_end_time_)
|
| + SignalEndOfStream();
|
| } else if (state_ == kPlaying) {
|
| state_ = kUnderflow;
|
| underflow_cb = underflow_cb_;
|
| @@ -260,17 +432,17 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest,
|
| // Otherwise fill the buffer.
|
| frames_written = algorithm_->FillBuffer(dest, requested_frames);
|
| }
|
| -
|
| - // Get the current time.
|
| - last_fill_buffer_time_ = algorithm_->GetTime();
|
| }
|
|
|
| - // Update the pipeline's time if it was set last time.
|
| - base::TimeDelta new_current_time = last_fill_buffer_time - playback_delay;
|
| - if (last_fill_buffer_time.InMicroseconds() > 0 &&
|
| - (last_fill_buffer_time != last_fill_buffer_time_ ||
|
| - new_current_time > host()->GetTime())) {
|
| - time_cb_.Run(new_current_time, last_fill_buffer_time);
|
| + base::TimeDelta previous_time_buffered = audio_time_buffered_;
|
| + // The call to FillBuffer() on |algorithm_| has increased the amount of
|
| + // buffered audio data. Update the new amount of time buffered.
|
| + audio_time_buffered_ = algorithm_->GetTime();
|
| +
|
| + if (previous_time_buffered.InMicroseconds() > 0 &&
|
| + (previous_time_buffered != audio_time_buffered_ ||
|
| + current_time > host()->GetTime())) {
|
| + time_cb_.Run(current_time, audio_time_buffered_);
|
| }
|
|
|
| if (!underflow_cb.is_null())
|
| @@ -279,35 +451,33 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest,
|
| return frames_written;
|
| }
|
|
|
| -void AudioRendererBase::SignalEndOfStream() {
|
| - DCHECK(received_end_of_stream_);
|
| - if (!rendered_end_of_stream_) {
|
| - rendered_end_of_stream_ = true;
|
| - host()->NotifyEnded();
|
| +void AudioRendererBase::UpdateEarliestEndTime(int bytes_filled,
|
| + base::TimeDelta request_delay,
|
| + base::Time time_now) {
|
| + if (bytes_filled != 0) {
|
| + base::TimeDelta predicted_play_time = ConvertToDuration(bytes_filled);
|
| + float playback_rate = GetPlaybackRate();
|
| + if (playback_rate != 1.0f) {
|
| + predicted_play_time = base::TimeDelta::FromMicroseconds(
|
| + static_cast<int64>(ceil(predicted_play_time.InMicroseconds() *
|
| + playback_rate)));
|
| + }
|
| + earliest_end_time_ =
|
| + std::max(earliest_end_time_,
|
| + time_now + request_delay + predicted_play_time);
|
| }
|
| }
|
|
|
| -void AudioRendererBase::ScheduleRead_Locked() {
|
| - lock_.AssertAcquired();
|
| - if (pending_read_ || state_ == kPaused)
|
| - return;
|
| - pending_read_ = true;
|
| - decoder_->Read(read_cb_);
|
| -}
|
| -
|
| -void AudioRendererBase::SetPlaybackRate(float playback_rate) {
|
| - base::AutoLock auto_lock(lock_);
|
| - algorithm_->SetPlaybackRate(playback_rate);
|
| -}
|
| -
|
| -float AudioRendererBase::GetPlaybackRate() {
|
| - base::AutoLock auto_lock(lock_);
|
| - return algorithm_->playback_rate();
|
| +base::TimeDelta AudioRendererBase::ConvertToDuration(int bytes) {
|
| + if (bytes_per_second_) {
|
| + return base::TimeDelta::FromMicroseconds(
|
| + base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_);
|
| + }
|
| + return base::TimeDelta();
|
| }
|
|
|
| -bool AudioRendererBase::IsBeforeSeekTime(const scoped_refptr<Buffer>& buffer) {
|
| - return (state_ == kSeeking) && buffer && !buffer->IsEndOfStream() &&
|
| - (buffer->GetTimestamp() + buffer->GetDuration()) < seek_timestamp_;
|
| +void AudioRendererBase::OnRenderError() {
|
| + host()->DisableAudioRenderer();
|
| }
|
|
|
| } // namespace media
|
|
|