Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1085)

Unified Diff: media/filters/audio_renderer_base.cc

Issue 9826023: Merge AudioRendererImpl and AudioRendererBase; add NullAudioSink (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase ToT + address CR comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/audio_renderer_base.h ('k') | media/filters/audio_renderer_base_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « media/filters/audio_renderer_base.h ('k') | media/filters/audio_renderer_base_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698