| // Copyright (c) 2011 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/basictypes.h" |
| #include "base/environment.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/threading/platform_thread.h" |
| #include "media/audio/audio_io.h" |
| #include "media/audio/audio_manager.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| static const int kSamplingRate = 8000; |
| static const int kSamplesPerPacket = kSamplingRate / 20; |
| |
| // This class allows to find out if the callbacks are occurring as |
| // expected and if any error has been reported. |
| class TestInputCallback : public AudioInputStream::AudioInputCallback { |
| public: |
| explicit TestInputCallback(int max_data_bytes) |
| : callback_count_(0), |
| had_error_(0), |
| was_closed_(0), |
| max_data_bytes_(max_data_bytes) { |
| } |
| virtual void OnData(AudioInputStream* stream, const uint8* data, |
| uint32 size) { |
| ++callback_count_; |
| // Read the first byte to make sure memory is good. |
| if (size) { |
| ASSERT_LE(static_cast<int>(size), max_data_bytes_); |
| int value = data[0]; |
| EXPECT_TRUE(value >= 0); |
| } |
| } |
| virtual void OnClose(AudioInputStream* stream) { |
| ++was_closed_; |
| } |
| virtual void OnError(AudioInputStream* stream, int code) { |
| ++had_error_; |
| } |
| // Returns how many times OnData() has been called. |
| int callback_count() const { |
| return callback_count_; |
| } |
| // Returns how many times the OnError callback was called. |
| int had_error() const { |
| return had_error_; |
| } |
| |
| void set_error(bool error) { |
| had_error_ += error ? 1 : 0; |
| } |
| // Returns how many times the OnClose callback was called. |
| int was_closed() const { |
| return was_closed_; |
| } |
| |
| private: |
| int callback_count_; |
| int had_error_; |
| int was_closed_; |
| int max_data_bytes_; |
| }; |
| |
| // Specializes TestInputCallback to simulate a sink that blocks for some time |
| // in the OnData callback. |
| class TestInputCallbackBlocking : public TestInputCallback { |
| public: |
| TestInputCallbackBlocking(int max_data_bytes, int block_after_callback, |
| int block_for_ms) |
| : TestInputCallback(max_data_bytes), |
| block_after_callback_(block_after_callback), |
| block_for_ms_(block_for_ms) { |
| } |
| virtual void OnData(AudioInputStream* stream, const uint8* data, |
| uint32 size) { |
| // Call the base, which increments the callback_count_. |
| TestInputCallback::OnData(stream, data, size); |
| if (callback_count() > block_after_callback_) |
| base::PlatformThread::Sleep(block_for_ms_); |
| } |
| |
| private: |
| int block_after_callback_; |
| int block_for_ms_; |
| }; |
| |
| static bool CanRunAudioTests() { |
| scoped_ptr<base::Environment> env(base::Environment::Create()); |
| if (env->HasVar("CHROME_HEADLESS")) |
| return false; |
| |
| AudioManager* audio_man = AudioManager::GetAudioManager(); |
| if (NULL == audio_man) |
| return false; |
| |
| return audio_man->HasAudioInputDevices(); |
| } |
| |
| static AudioInputStream* CreateTestAudioInputStream() { |
| AudioManager* audio_man = AudioManager::GetAudioManager(); |
| AudioInputStream* ais = audio_man->MakeAudioInputStream( |
| AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, 2, kSamplingRate, |
| 16, kSamplesPerPacket)); |
| EXPECT_TRUE(NULL != ais); |
| return ais; |
| } |
| |
| // Test that AudioInputStream rejects out of range parameters. |
| TEST(AudioInputTest, SanityOnMakeParams) { |
| if (!CanRunAudioTests()) |
| return; |
| AudioManager* audio_man = AudioManager::GetAudioManager(); |
| AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR; |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 8, 8000, 16, kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 1, 1024 * 1024, 16, kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 2, 8000, 80, kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 2, 8000, 80, 1000 * kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, -2, 8000, 16, kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 2, -8000, 16, kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 2, 8000, -16, kSamplesPerPacket))); |
| EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream( |
| AudioParameters(fmt, 2, 8000, 16, -1024))); |
| } |
| |
| // Test create and close of an AudioInputStream without recording audio. |
| TEST(AudioInputTest, CreateAndClose) { |
| if (!CanRunAudioTests()) |
| return; |
| AudioInputStream* ais = CreateTestAudioInputStream(); |
| ais->Close(); |
| } |
| |
| // Test create, open and close of an AudioInputStream without recording audio. |
| TEST(AudioInputTest, OpenAndClose) { |
| if (!CanRunAudioTests()) |
| return; |
| AudioInputStream* ais = CreateTestAudioInputStream(); |
| EXPECT_TRUE(ais->Open()); |
| ais->Close(); |
| } |
| |
| // Test create, open, stop and close of an AudioInputStream without recording. |
| TEST(AudioInputTest, OpenStopAndClose) { |
| if (!CanRunAudioTests()) |
| return; |
| AudioInputStream* ais = CreateTestAudioInputStream(); |
| EXPECT_TRUE(ais->Open()); |
| ais->Stop(); |
| ais->Close(); |
| } |
| |
| // Test a normal recording sequence using an AudioInputStream. |
| TEST(AudioInputTest, Record) { |
| if (!CanRunAudioTests()) |
| return; |
| MessageLoop message_loop(MessageLoop::TYPE_DEFAULT); |
| AudioInputStream* ais = CreateTestAudioInputStream(); |
| EXPECT_TRUE(ais->Open()); |
| |
| TestInputCallback test_callback(kSamplesPerPacket * 4); |
| ais->Start(&test_callback); |
| // Verify at least 500ms worth of audio was recorded, after giving sufficient |
| // extra time. |
| message_loop.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), 590); |
| message_loop.Run(); |
| EXPECT_GE(test_callback.callback_count(), 10); |
| EXPECT_FALSE(test_callback.had_error()); |
| |
| ais->Stop(); |
| ais->Close(); |
| } |
| |
| // Test a recording sequence with delays in the audio callback. |
| TEST(AudioInputTest, RecordWithSlowSink) { |
| if (!CanRunAudioTests()) |
| return; |
| MessageLoop message_loop(MessageLoop::TYPE_DEFAULT); |
| AudioInputStream* ais = CreateTestAudioInputStream(); |
| EXPECT_TRUE(ais->Open()); |
| |
| // We should normally get a callback every 50ms, and a 20ms delay inside each |
| // callback should not change this sequence. |
| TestInputCallbackBlocking test_callback(kSamplesPerPacket * 4, 0, 20); |
| ais->Start(&test_callback); |
| // Verify at least 500ms worth of audio was recorded, after giving sufficient |
| // extra time. |
| message_loop.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), 590); |
| message_loop.Run(); |
| EXPECT_GE(test_callback.callback_count(), 10); |
| EXPECT_FALSE(test_callback.had_error()); |
| |
| ais->Stop(); |
| ais->Close(); |
| } |