diff options
author | enal@chromium.org <enal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-28 03:18:46 +0000 |
---|---|---|
committer | enal@chromium.org <enal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-28 03:18:46 +0000 |
commit | f35ea452204e6faa852020fe15c702a90d6e1849 (patch) | |
tree | 7d28629f257079606b3c62ab7a595ef327238981 /media | |
parent | 6048d519459f0d665d6887c1eee07b4f15c061c4 (diff) | |
download | chromium_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.cc | 27 | ||||
-rw-r--r-- | media/filters/audio_renderer_base.h | 26 | ||||
-rw-r--r-- | media/filters/audio_renderer_base_unittest.cc | 7 | ||||
-rw-r--r-- | media/filters/null_audio_renderer.cc | 9 | ||||
-rw-r--r-- | media/filters/null_audio_renderer.h | 1 | ||||
-rw-r--r-- | media/filters/reference_audio_renderer.cc | 15 | ||||
-rw-r--r-- | media/filters/reference_audio_renderer.h | 2 |
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_; |