summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorenal@chromium.org <enal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-28 03:18:46 +0000
committerenal@chromium.org <enal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-28 03:18:46 +0000
commitf35ea452204e6faa852020fe15c702a90d6e1849 (patch)
tree7d28629f257079606b3c62ab7a595ef327238981 /media
parent6048d519459f0d665d6887c1eee07b4f15c061c4 (diff)
downloadchromium_src-f35ea452204e6faa852020fe15c702a90d6e1849.zip
chromium_src-f35ea452204e6faa852020fe15c702a90d6e1849.tar.gz
chromium_src-f35ea452204e6faa852020fe15c702a90d6e1849.tar.bz2
Minor code refactoring: move 'signal send end of stream' from
AudioRendererBase::FillBuffer(), to allow making it asynchronous in the future. First step in implementing software audio mixing. BUG=61293 Review URL: http://codereview.chromium.org/9129004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119578 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/audio_renderer_base.cc27
-rw-r--r--media/filters/audio_renderer_base.h26
-rw-r--r--media/filters/audio_renderer_base_unittest.cc7
-rw-r--r--media/filters/null_audio_renderer.cc9
-rw-r--r--media/filters/null_audio_renderer.h1
-rw-r--r--media/filters/reference_audio_renderer.cc15
-rw-r--r--media/filters/reference_audio_renderer.h2
7 files changed, 63 insertions, 24 deletions
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
index 049a39a..b80536a 100644
--- a/media/filters/audio_renderer_base.cc
+++ b/media/filters/audio_renderer_base.cc
@@ -189,8 +189,7 @@ void AudioRendererBase::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
uint32 AudioRendererBase::FillBuffer(uint8* dest,
uint32 dest_len,
- const base::TimeDelta& playback_delay,
- bool buffers_empty) {
+ const base::TimeDelta& playback_delay) {
// The timestamp of the last buffer written during the last call to
// FillBuffer().
base::TimeDelta last_fill_buffer_time;
@@ -222,21 +221,21 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest,
// Use three conditions to determine the end of playback:
// 1. Algorithm has no audio data. (algorithm_->IsQueueEmpty() == true)
- // 2. Browser process has no audio data. (buffers_empty == true)
- // 3. We've recieved an end of stream buffer.
+ // 2. We've recieved an end of stream buffer.
// (recieved_end_of_stream_ == true)
+ // 3. Browser process has no audio data being played.
+ // There is no way to check that condition that would work for all
+ // derived classes, so call virtual method that would either render
+ // end of stream or schedule such rendering.
//
// Three conditions determine when an underflow occurs:
// 1. Algorithm has no audio data.
// 2. Currently in the kPlaying state.
// 3. Have not received an end of stream buffer.
if (algorithm_->IsQueueEmpty()) {
- if (buffers_empty && recieved_end_of_stream_) {
- if (!rendered_end_of_stream_) {
- rendered_end_of_stream_ = true;
- host()->NotifyEnded();
- }
- } else if (state_ == kPlaying && !recieved_end_of_stream_) {
+ if (recieved_end_of_stream_) {
+ OnRenderEndOfStream();
+ } else if (state_ == kPlaying) {
state_ = kUnderflow;
underflow_cb = underflow_callback_;
}
@@ -269,6 +268,14 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest,
return dest_written;
}
+void AudioRendererBase::SignalEndOfStream() {
+ DCHECK(recieved_end_of_stream_);
+ if (!rendered_end_of_stream_) {
+ rendered_end_of_stream_ = true;
+ host()->NotifyEnded();
+ }
+}
+
void AudioRendererBase::ScheduleRead_Locked() {
lock_.AssertAcquired();
if (pending_read_)
diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h
index a578204..f6218c7 100644
--- a/media/filters/audio_renderer_base.h
+++ b/media/filters/audio_renderer_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,6 +8,7 @@
// must also implement the following methods:
// OnInitialized
// OnStop
+// OnRenderEndOfStream
//
// The general assumption is that subclasses start a callback-based audio thread
// which needs to be filled with decoded audio data. AudioDecoderBase provides
@@ -47,6 +48,9 @@ class MEDIA_EXPORT AudioRendererBase : public AudioRenderer {
virtual void ResumeAfterUnderflow(bool buffer_more_audio) OVERRIDE;
protected:
+ FRIEND_TEST_ALL_PREFIXES(AudioRendererBaseTest, EndOfStream);
+ FRIEND_TEST_ALL_PREFIXES(AudioRendererBaseTest, Underflow_EndOfStream);
+
// Subclasses should return true if they were able to initialize, false
// otherwise.
virtual bool OnInitialize(int bits_per_channel,
@@ -57,6 +61,11 @@ class MEDIA_EXPORT AudioRendererBase : public AudioRenderer {
// this time, such as stopping any running threads.
virtual void OnStop() = 0;
+ // Method called by FillBuffer() when it finds that it reached end of stream.
+ // FillBuffer() cannot immediately signal end of stream event because browser
+ // may have buffered data.
+ virtual void OnRenderEndOfStream() = 0;
+
// Callback from the audio decoder delivering decoded audio samples.
void DecodedAudioReady(scoped_refptr<Buffer> buffer);
@@ -76,15 +85,20 @@ class MEDIA_EXPORT AudioRendererBase : public AudioRenderer {
// should the filled buffer be played. If FillBuffer() is called as the audio
// hardware plays the buffer, then |playback_delay| should be zero.
//
- // |buffers_empty| is set to true when all the hardware buffers become empty.
- // This is an indication that all the data written to the device has been
- // played.
+ // FillBuffer() calls OnRenderEndOfStream() when it reaches end of stream.
+ // It is responsibility of derived class to provide implementation of
+ // OnRenderEndOfStream() that calls SignalEndOfStream() when all the hardware
+ // buffers become empty (i.e. when all the data written to the device has
+ // been played).
//
// Safe to call on any thread.
uint32 FillBuffer(uint8* dest,
uint32 len,
- const base::TimeDelta& playback_delay,
- bool buffers_empty);
+ const base::TimeDelta& playback_delay);
+
+ // Called by OnRenderEndOfStream() or some callback scheduled by derived class
+ // to signal end of stream.
+ void SignalEndOfStream();
// Get/Set the playback rate of |algorithm_|.
virtual void SetPlaybackRate(float playback_rate) OVERRIDE;
diff --git a/media/filters/audio_renderer_base_unittest.cc b/media/filters/audio_renderer_base_unittest.cc
index 93fe785..e07d524 100644
--- a/media/filters/audio_renderer_base_unittest.cc
+++ b/media/filters/audio_renderer_base_unittest.cc
@@ -38,6 +38,7 @@ class MockAudioRendererBase : public AudioRendererBase {
// AudioRendererBase implementation.
MOCK_METHOD3(OnInitialize, bool(int, ChannelLayout, int));
MOCK_METHOD0(OnStop, void());
+ MOCK_METHOD0(OnRenderEndOfStream, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioRendererBase);
@@ -148,7 +149,7 @@ class AudioRendererBaseTest : public ::testing::Test {
bool ConsumeBufferedData(uint32 size, bool* muted) {
scoped_array<uint8> buffer(new uint8[size]);
uint32 bytes_read = renderer_->FillBuffer(buffer.get(), size,
- base::TimeDelta(), true);
+ base::TimeDelta());
if (bytes_read > 0 && muted) {
*muted = (buffer[0] == kMutedAudio);
}
@@ -240,6 +241,8 @@ TEST_F(AudioRendererBaseTest, EndOfStream) {
EXPECT_FALSE(renderer_->HasEnded());
// Drain internal buffer, now we should report ended.
+ EXPECT_CALL(*renderer_, OnRenderEndOfStream())
+ .WillOnce(Invoke(renderer_.get(), &AudioRendererBase::SignalEndOfStream));
EXPECT_CALL(host_, NotifyEnded());
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
EXPECT_TRUE(renderer_->HasEnded());
@@ -320,6 +323,8 @@ TEST_F(AudioRendererBaseTest, Underflow_EndOfStream) {
// stop reading after receiving an end of stream buffer. It should have also
// called NotifyEnded() http://crbug.com/106641
DeliverEndOfStream();
+ EXPECT_CALL(*renderer_, OnRenderEndOfStream())
+ .WillOnce(Invoke(renderer_.get(), &AudioRendererBase::SignalEndOfStream));
EXPECT_CALL(host_, NotifyEnded());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
diff --git a/media/filters/null_audio_renderer.cc b/media/filters/null_audio_renderer.cc
index bb93db2..6168bd9 100644
--- a/media/filters/null_audio_renderer.cc
+++ b/media/filters/null_audio_renderer.cc
@@ -61,10 +61,7 @@ void NullAudioRenderer::FillBufferTask() {
// Only consume buffers when actually playing.
if (GetPlaybackRate() > 0.0f) {
- size_t bytes = FillBuffer(buffer_.get(),
- buffer_size_,
- base::TimeDelta(),
- true);
+ size_t bytes = FillBuffer(buffer_.get(), buffer_size_, base::TimeDelta());
// Calculate our sleep duration, taking playback rate into consideration.
delay = base::TimeDelta::FromMilliseconds(
@@ -81,4 +78,8 @@ void NullAudioRenderer::FillBufferTask() {
std::max(delay, base::TimeDelta::FromMilliseconds(1)));
}
+void NullAudioRenderer::OnRenderEndOfStream() {
+ SignalEndOfStream();
+}
+
} // namespace media
diff --git a/media/filters/null_audio_renderer.h b/media/filters/null_audio_renderer.h
index 9832367..45c8af6 100644
--- a/media/filters/null_audio_renderer.h
+++ b/media/filters/null_audio_renderer.h
@@ -37,6 +37,7 @@ class MEDIA_EXPORT NullAudioRenderer : public AudioRendererBase {
ChannelLayout channel_layout,
int sample_rate) OVERRIDE;
virtual void OnStop() OVERRIDE;
+ virtual void OnRenderEndOfStream() OVERRIDE;
private:
// Audio thread task that periodically calls FillBuffer() to consume
diff --git a/media/filters/reference_audio_renderer.cc b/media/filters/reference_audio_renderer.cc
index 3c80ccb..558fcda 100644
--- a/media/filters/reference_audio_renderer.cc
+++ b/media/filters/reference_audio_renderer.cc
@@ -15,6 +15,7 @@ ReferenceAudioRenderer::ReferenceAudioRenderer(AudioManager* audio_manager)
: AudioRendererBase(),
audio_manager_(audio_manager),
bytes_per_second_(0),
+ has_buffered_data_(true),
buffer_capacity_(0) {
}
@@ -67,12 +68,20 @@ void ReferenceAudioRenderer::OnMoreData(AudioOutputController* controller,
base::TimeDelta delay = base::TimeDelta::FromMicroseconds(
base::Time::kMicrosecondsPerSecond * pending_bytes /
bytes_per_second_);
- bool buffers_empty = buffers_state.pending_bytes == 0;
- uint32 read = FillBuffer(buffer_.get(), buffer_capacity_, delay,
- buffers_empty);
+ has_buffered_data_ = buffers_state.pending_bytes != 0;
+ uint32 read = FillBuffer(buffer_.get(), buffer_capacity_, delay);
controller->EnqueueData(buffer_.get(), read);
}
+void ReferenceAudioRenderer::OnRenderEndOfStream() {
+ // We cannot signal end of stream as long as we have buffered data.
+ // In such case eventually host would playback all the data, and OnMoreData()
+ // would be called with buffers_state.pending_bytes == 0. At that moment
+ // we'll call SignalEndOfStream();
+ if (!has_buffered_data_)
+ SignalEndOfStream();
+}
+
bool ReferenceAudioRenderer::OnInitialize(int bits_per_channel,
ChannelLayout channel_layout,
int sample_rate) {
diff --git a/media/filters/reference_audio_renderer.h b/media/filters/reference_audio_renderer.h
index 273aa0d..3985dfb 100644
--- a/media/filters/reference_audio_renderer.h
+++ b/media/filters/reference_audio_renderer.h
@@ -49,10 +49,12 @@ class MEDIA_EXPORT ReferenceAudioRenderer
ChannelLayout channel_layout,
int sample_rate) OVERRIDE;
virtual void OnStop() OVERRIDE;
+ virtual void OnRenderEndOfStream() OVERRIDE;
private:
scoped_refptr<AudioManager> audio_manager_;
int bytes_per_second_;
+ bool has_buffered_data_;
// Audio output controller.
scoped_refptr<media::AudioOutputController> controller_;