// Copyright (c) 2010 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. #include "base/callback.h" #include "base/stl_util-inl.h" #include "media/base/data_buffer.h" #include "media/base/mock_filter_host.h" #include "media/base/mock_filters.h" #include "media/filters/audio_renderer_base.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest_prod.h" using ::testing::_; using ::testing::InSequence; using ::testing::Invoke; using ::testing::NotNull; using ::testing::Return; using ::testing::ReturnRef; using ::testing::StrictMock; namespace media { // Mocked subclass of AudioRendererBase for testing purposes. class MockAudioRendererBase : public AudioRendererBase { public: MockAudioRendererBase() : AudioRendererBase() {} virtual ~MockAudioRendererBase() {} // AudioRenderer implementation. MOCK_METHOD1(SetVolume, void(float volume)); // AudioRendererBase implementation. MOCK_METHOD1(OnInitialize, bool(const MediaFormat& media_format)); MOCK_METHOD0(OnStop, void()); // Used for verifying check points during tests. MOCK_METHOD1(CheckPoint, void(int id)); private: FRIEND_TEST(AudioRendererBaseTest, OneCompleteReadCycle); DISALLOW_COPY_AND_ASSIGN(MockAudioRendererBase); }; class AudioRendererBaseTest : public ::testing::Test { public: // Give the decoder some non-garbage media properties. AudioRendererBaseTest() : renderer_(new MockAudioRendererBase()), decoder_(new MockAudioDecoder()), pending_reads_(0) { renderer_->set_host(&host_); // Queue all reads from the decoder. EXPECT_CALL(*decoder_, ProduceAudioSamples(_)) .WillRepeatedly(Invoke(this, &AudioRendererBaseTest::EnqueueCallback)); // Sets the essential media format keys for this decoder. decoder_media_format_.SetAsString(MediaFormat::kMimeType, mime_type::kUncompressedAudio); decoder_media_format_.SetAsInteger(MediaFormat::kChannels, 1); decoder_media_format_.SetAsInteger(MediaFormat::kSampleRate, 44100); decoder_media_format_.SetAsInteger(MediaFormat::kSampleBits, 16); EXPECT_CALL(*decoder_, media_format()) .WillRepeatedly(ReturnRef(decoder_media_format_)); } virtual ~AudioRendererBaseTest() { // Expect a call into the subclass. EXPECT_CALL(*renderer_, OnStop()); EXPECT_CALL(callback_, OnFilterCallback()); EXPECT_CALL(callback_, OnCallbackDestroyed()); renderer_->Stop(callback_.NewCallback()); } protected: static const size_t kMaxQueueSize; // Fixture members. scoped_refptr renderer_; scoped_refptr decoder_; StrictMock host_; StrictMock callback_; MediaFormat decoder_media_format_; // Number of asynchronous read requests sent to |decoder_|. size_t pending_reads_; private: void EnqueueCallback(scoped_refptr buffer) { ++pending_reads_; } DISALLOW_COPY_AND_ASSIGN(AudioRendererBaseTest); }; const size_t AudioRendererBaseTest::kMaxQueueSize = 1u; TEST_F(AudioRendererBaseTest, Initialize_Failed) { InSequence s; // Our subclass will fail when asked to initialize. EXPECT_CALL(*renderer_, OnInitialize(_)) .WillOnce(Return(false)); // We expect to receive an error. EXPECT_CALL(host_, SetError(PIPELINE_ERROR_INITIALIZATION_FAILED)); // We expect our callback to be executed. EXPECT_CALL(callback_, OnFilterCallback()); EXPECT_CALL(callback_, OnCallbackDestroyed()); // Initialize, we expect to have no reads. renderer_->Initialize(decoder_, callback_.NewCallback()); EXPECT_EQ(0u, pending_reads_); } TEST_F(AudioRendererBaseTest, Initialize_Successful) { InSequence s; // Then our subclass will be asked to initialize. EXPECT_CALL(*renderer_, OnInitialize(_)) .WillOnce(Return(true)); // After finishing initialization, we expect our callback to be executed. EXPECT_CALL(callback_, OnFilterCallback()); EXPECT_CALL(callback_, OnCallbackDestroyed()); // Verify the following expectations haven't run until we complete the reads. EXPECT_CALL(*renderer_, CheckPoint(0)); MockFilterCallback seek_callback; EXPECT_CALL(seek_callback, OnFilterCallback()); EXPECT_CALL(seek_callback, OnCallbackDestroyed()); // Initialize, we shouldn't have any reads. renderer_->Initialize(decoder_, callback_.NewCallback()); EXPECT_EQ(0u, pending_reads_); // Now seek to trigger prerolling. renderer_->Seek(base::TimeDelta(), seek_callback.NewCallback()); EXPECT_EQ(kMaxQueueSize, pending_reads_); // Verify our seek callback hasn't been executed yet. renderer_->CheckPoint(0); // Now satisfy the read requests. Our callback should be executed after // exiting this loop. while (pending_reads_) { scoped_refptr buffer(new DataBuffer(1024)); buffer->SetDataSize(1024); --pending_reads_; decoder_->consume_audio_samples_callback()->Run(buffer); } } TEST_F(AudioRendererBaseTest, OneCompleteReadCycle) { InSequence s; // Then our subclass will be asked to initialize. EXPECT_CALL(*renderer_, OnInitialize(_)) .WillOnce(Return(true)); // After finishing initialization, we expect our callback to be executed. EXPECT_CALL(callback_, OnFilterCallback()); EXPECT_CALL(callback_, OnCallbackDestroyed()); // Verify the following expectations haven't run until we complete the reads. EXPECT_CALL(*renderer_, CheckPoint(0)); MockFilterCallback seek_callback; EXPECT_CALL(seek_callback, OnFilterCallback()); EXPECT_CALL(seek_callback, OnCallbackDestroyed()); // Initialize, we shouldn't have any reads. renderer_->Initialize(decoder_, callback_.NewCallback()); EXPECT_EQ(0u, pending_reads_); // Now seek to trigger prerolling. renderer_->Seek(base::TimeDelta(), seek_callback.NewCallback()); EXPECT_EQ(kMaxQueueSize, pending_reads_); // Verify our seek callback hasn't been executed yet. renderer_->CheckPoint(0); // Now satisfy the read requests. Our callback should be executed after // exiting this loop. const uint32 kDataSize = 1024; uint32 bytes_buffered = 0; while (pending_reads_) { scoped_refptr buffer(new DataBuffer(kDataSize)); buffer->SetDataSize(kDataSize); decoder_->consume_audio_samples_callback()->Run(buffer); --pending_reads_; bytes_buffered += kDataSize; } MockFilterCallback play_callback; EXPECT_CALL(play_callback, OnFilterCallback()); EXPECT_CALL(play_callback, OnCallbackDestroyed()); // Then set the renderer to play state. renderer_->Play(play_callback.NewCallback()); renderer_->SetPlaybackRate(1.0f); EXPECT_EQ(1.0f, renderer_->GetPlaybackRate()); // Then flush the data in the renderer by reading from it. uint8 buffer[kDataSize]; for (size_t i = 0; i < kMaxQueueSize; ++i) { EXPECT_EQ(kDataSize, renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta(), true)); bytes_buffered -= kDataSize; } // Make sure the read request queue is full. EXPECT_EQ(kMaxQueueSize, pending_reads_); // Fulfill the read with an end-of-stream packet. scoped_refptr last_buffer(new DataBuffer(0)); decoder_->consume_audio_samples_callback()->Run(last_buffer); --pending_reads_; // We shouldn't report ended until all data has been flushed out. EXPECT_FALSE(renderer_->HasEnded()); // We should have one less read request in the queue. EXPECT_EQ(kMaxQueueSize - 1, pending_reads_); // Flush the entire internal buffer and verify NotifyEnded() isn't called // right away. EXPECT_CALL(*renderer_, CheckPoint(1)); EXPECT_EQ(0u, bytes_buffered % kDataSize); while (bytes_buffered > 0) { EXPECT_EQ(kDataSize, renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta(), true)); bytes_buffered -= kDataSize; } // Although we've emptied the buffer, we don't consider ourselves ended until // we request another buffer. This way we know the last of the audio has // played. EXPECT_FALSE(renderer_->HasEnded()); renderer_->CheckPoint(1); // Do an additional read to trigger NotifyEnded(). EXPECT_CALL(host_, NotifyEnded()); EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta(), true)); // We should now report ended. EXPECT_TRUE(renderer_->HasEnded()); // Further reads should return muted audio and not notify any more. EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta(), true)); } } // namespace media