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..53b19c572595c845420971c3d2504f13eab3462a 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,55 @@ 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(); |
+ } else { |
+ state_ = kPaused; |
enal1
2012/04/03 16:12:20
We already set paused state in line 68.
vrk (LEFT CHROMIUM)
2012/04/03 18:27:52
Deleted this else branch.
|
+ } |
} |
+ |
+ if (stopped_) |
+ return; |
+ |
+ DoPause(); |
+} |
+ |
+void AudioRendererBase::DoPause() { |
+ DCHECK(sink_.get()); |
+ sink_->Pause(false); |
} |
void AudioRendererBase::Flush(const base::Closure& callback) { |
@@ -58,7 +92,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 +121,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 +171,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 +219,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 || |
@@ -206,9 +279,12 @@ void AudioRendererBase::DecodedAudioReady(scoped_refptr<Buffer> buffer) { |
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 +308,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 +323,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 +334,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()) |
@@ -296,6 +370,23 @@ void AudioRendererBase::ScheduleRead_Locked() { |
} |
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); |
} |
@@ -310,4 +401,85 @@ bool AudioRendererBase::IsBeforeSeekTime(const scoped_refptr<Buffer>& buffer) { |
(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; |
+} |
+ |
+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); |
+ } |
+} |
+ |
+base::TimeDelta AudioRendererBase::ConvertToDuration(int bytes) { |
+ if (bytes_per_second_) { |
+ return base::TimeDelta::FromMicroseconds( |
+ base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_); |
+ } |
+ return base::TimeDelta(); |
+} |
+ |
+void AudioRendererBase::OnRenderError() { |
+ host()->DisableAudioRenderer(); |
+} |
+ |
} // namespace media |