// 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. #include #include "base/message_loop.h" #include "base/synchronization/waitable_event.h" #include "media/audio/audio_manager.h" #include "media/audio/simple_sources.h" #include "media/audio/virtual_audio_input_stream.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { class MockInputCallback : public AudioInputStream::AudioInputCallback { public: MockInputCallback() {} virtual void OnData(AudioInputStream* stream, const uint8* data, uint32 size, uint32 hardware_delay_bytes, double volume) {} virtual void OnClose(AudioInputStream* stream) {} virtual void OnError(AudioInputStream* stream, int code) {} private: DISALLOW_COPY_AND_ASSIGN(MockInputCallback); }; class VirtualAudioInputStreamTest : public testing::Test { public: VirtualAudioInputStreamTest() : audio_manager_(AudioManager::Create()), params_( AudioParameters::AUDIO_VIRTUAL,CHANNEL_LAYOUT_MONO, 8000, 8, 128), output_params_( AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 8000, 8, 128), stream_(NULL), source_(CHANNEL_LAYOUT_STEREO, 200.0, 128), done_(false, false) { } void StartStreamAndRunTestsOnAudioThread(int num_output_streams, int num_callback_iterations, int num_streams_removed_per_round, int num_expected_source_callbacks) { ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); stream_->Open(); stream_->Start(&input_callback_); AddStreamsAndDoCallbacks(num_output_streams, num_callback_iterations, num_streams_removed_per_round, num_expected_source_callbacks); } void AddStreamsAndDoCallbacks(int num_output_streams, int num_callback_iterations, int num_streams_removed_per_round, int num_expected_source_callbacks) { ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); for (int i = 0; i < num_output_streams; ++i) { AudioOutputStream* output_stream = audio_manager_->MakeAudioOutputStream(output_params_); DCHECK(output_stream); output_streams_.push_back(output_stream); output_stream->Open(); output_stream->Start(&source_); } if (num_output_streams == 0 && num_streams_removed_per_round > 0) { AudioOutputStream* output_stream = output_streams_.back(); output_streams_.pop_back(); output_stream->Stop(); output_stream->Close(); } if (num_callback_iterations > 0) { // Force the next callback to be immediate. stream_->buffer_duration_ms_ = base::TimeDelta(); audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::AddStreamsAndDoCallbacks, base::Unretained(this), 0, --num_callback_iterations, num_streams_removed_per_round, num_expected_source_callbacks)); } else { // Finish the test. EXPECT_EQ(num_expected_source_callbacks, source_.callbacks()); EXPECT_EQ(0, source_.errors()); for (std::vector::iterator it = output_streams_.begin(); it != output_streams_.end(); ++it) (*it)->Stop(); stream_->Stop(); audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::EndTest, base::Unretained(this))); } } void OpenAndCloseOnAudioThread() { ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); stream_->Open(); // Create 2 output streams, which we just open and close without starting. const int num_output_stream = 2; for (int i = 0; i < num_output_stream; ++i) { AudioOutputStream* output_stream = audio_manager_->MakeAudioOutputStream(output_params_); DCHECK(output_stream); output_streams_.push_back(output_stream); output_stream->Open(); } audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::EndTest, base::Unretained(this))); } void StartStopOnAudioThread(int num_output_streams, int num_callback_iterations, int num_expected_source_callbacks) { ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); stream_->Open(); stream_->Start(&input_callback_); StartStopCallback(true, num_output_streams, num_callback_iterations, num_expected_source_callbacks); } void StartStopCallback(bool init, int num_output_streams, int num_callback_iterations, int num_expected_source_callbacks) { ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); if (init) { for (int i = 0; i < num_output_streams; ++i) { AudioOutputStream* output_stream = audio_manager_->MakeAudioOutputStream(output_params_); DCHECK(output_stream); output_streams_.push_back(output_stream); output_stream->Open(); output_stream->Start(&source_); } // Start with an odd iteration number so we call Stop() first below. DCHECK_NE(0, num_callback_iterations % 2); } // Start or stop half the streams. for (int i = 0; i < num_output_streams / 2; ++i) { if (num_callback_iterations % 2 != 0) output_streams_[i]->Stop(); else output_streams_[i]->Start(&source_); } if (num_callback_iterations > 0) { // Force the next callback to be immediate. stream_->buffer_duration_ms_ = base::TimeDelta::FromMilliseconds(0); audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::StartStopCallback, base::Unretained(this), false, num_output_streams, --num_callback_iterations, num_expected_source_callbacks)); } else { // Finish the test. EXPECT_EQ(num_expected_source_callbacks, source_.callbacks()); EXPECT_EQ(0, source_.errors()); for (std::vector::iterator it = output_streams_.begin(); it != output_streams_.end(); ++it) (*it)->Stop(); stream_->Stop(); audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::EndTest, base::Unretained(this))); } } void EndTest() { for (std::vector::iterator it = output_streams_.begin(); it != output_streams_.end(); ++it) (*it)->Close(); stream_->Close(); done_.Signal(); } protected: scoped_ptr audio_manager_; AudioParameters params_; AudioParameters output_params_; VirtualAudioInputStream* stream_; MockInputCallback input_callback_; std::vector output_streams_; SineWaveAudioSource source_; base::WaitableEvent done_; private: DISALLOW_COPY_AND_ASSIGN(VirtualAudioInputStreamTest); }; TEST_F(VirtualAudioInputStreamTest, AttachAndDriveSingleStream) { stream_ = static_cast( audio_manager_->MakeAudioInputStream(params_, "1")); DCHECK(stream_); const int num_output_streams = 1; const int num_callback_iterations = 1; const int num_streams_removed_per_round = 0; const int num_expected_source_callbacks = 1; audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::StartStreamAndRunTestsOnAudioThread, base::Unretained(this), num_output_streams, num_callback_iterations, num_streams_removed_per_round, num_expected_source_callbacks)); done_.Wait(); } TEST_F(VirtualAudioInputStreamTest, AttachAndDriveMultipleStreams) { stream_ = static_cast( audio_manager_->MakeAudioInputStream(params_, "1")); DCHECK(stream_); const int num_output_streams = 5; const int num_callback_iterations = 5; const int num_streams_removed_per_round = 0; const int num_expected_source_callbacks = 25; audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::StartStreamAndRunTestsOnAudioThread, base::Unretained(this), num_output_streams, num_callback_iterations, num_streams_removed_per_round, num_expected_source_callbacks)); done_.Wait(); } TEST_F(VirtualAudioInputStreamTest, AttachAndRemoveStreams) { stream_ = static_cast( audio_manager_->MakeAudioInputStream(params_, "1")); DCHECK(stream_); const int num_output_streams = 8; const int num_callback_iterations = 5; const int num_streams_removed_per_round = 1; const int num_expected_source_callbacks = 8 + 7 + 6 + 5 + 4; audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::StartStreamAndRunTestsOnAudioThread, base::Unretained(this), num_output_streams, num_callback_iterations, num_streams_removed_per_round, num_expected_source_callbacks)); done_.Wait(); } // Opens/closes a VirtualAudioInputStream and a number of attached // VirtualAudioOutputStreams without calling Start()/Stop(). TEST_F(VirtualAudioInputStreamTest, OpenAndClose) { stream_ = static_cast( audio_manager_->MakeAudioInputStream(params_, "1")); DCHECK(stream_); audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::OpenAndCloseOnAudioThread, base::Unretained(this))); done_.Wait(); } // Creates and closes and VirtualAudioInputStream. TEST_F(VirtualAudioInputStreamTest, CreateAndClose) { stream_ = static_cast( audio_manager_->MakeAudioInputStream(params_, "1")); DCHECK(stream_); audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::EndTest, base::Unretained(this))); done_.Wait(); } // Starts and stops VirtualAudioOutputStreams while attached to a // VirtualAudioInputStream. TEST_F(VirtualAudioInputStreamTest, AttachAndStartStopStreams) { stream_ = static_cast( audio_manager_->MakeAudioInputStream(params_, "1")); DCHECK(stream_); const int num_output_streams = 4; const int num_callback_iterations = 5; const int num_expected_source_callbacks = 2 + 4 + 2 + 4 + 2; audio_manager_->GetMessageLoop()->PostTask( FROM_HERE, base::Bind( &VirtualAudioInputStreamTest::StartStopOnAudioThread, base::Unretained(this), num_output_streams, num_callback_iterations, num_expected_source_callbacks)); done_.Wait(); } } // namespace media