// Copyright 2015 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 #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/sync_socket.h" #include "base/time/time.h" #include "build/build_config.h" #include "content/browser/renderer_host/media/audio_input_sync_writer.h" #include "content/public/test/test_browser_thread_bundle.h" #include "media/audio/audio_parameters.h" #include "media/base/audio_bus.h" #include "media/base/channel_layout.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using base::TimeDelta; using media::AudioBus; using media::AudioParameters; namespace content { namespace { // Number of audio buffers in the faked ring buffer. const int kSegments = 10; // Audio buffer parameters. const int channels = 1; const int sampling_frequency_hz = 16000; const int frames = sampling_frequency_hz / 100; // 10 ms const int bits_per_sample = 16; // Faked ring buffer. Must be aligned. #define DATA_ALIGNMENT 16 static_assert(AudioBus::kChannelAlignment == DATA_ALIGNMENT, "Data alignment not same as AudioBus"); ALIGNAS(DATA_ALIGNMENT) uint8_t data[kSegments * (sizeof(media::AudioInputBufferParameters) + frames * channels * sizeof(float))]; } // namespace // Mocked out sockets used for Send/ReceiveWithTimeout. Counts the number of // outstanding reads, i.e. the diff between send and receive calls. class MockCancelableSyncSocket : public base::CancelableSyncSocket { public: MockCancelableSyncSocket(int buffer_size) : in_failure_mode_(false), writes_(0), reads_(0), receives_(0), buffer_size_(buffer_size), read_buffer_index_(0) {} size_t Send(const void* buffer, size_t length) override { EXPECT_EQ(length, sizeof(uint32_t)); ++writes_; EXPECT_LE(NumberOfBuffersFilled(), buffer_size_); return length; } size_t Receive(void* buffer, size_t length) override { EXPECT_EQ(0u, length % sizeof(uint32_t)); if (in_failure_mode_) return 0; if (receives_ == reads_) return 0; uint32_t* ptr = static_cast(buffer); size_t received = 0; for (; received < length / sizeof(uint32_t) && receives_ < reads_; ++received, ++ptr) { ++receives_; EXPECT_LE(receives_, reads_); *ptr = ++read_buffer_index_; } return received * sizeof(uint32_t); } size_t Peek() override { return (reads_ - receives_) * sizeof(uint32_t); } // Simluates reading |buffers| number of buffers from the ring buffer. void Read(int buffers) { reads_ += buffers; EXPECT_LE(reads_, writes_); } // When |in_failure_mode_| == true, the socket fails to receive. void SetFailureMode(bool in_failure_mode) { in_failure_mode_ = in_failure_mode; } int NumberOfBuffersFilled() { return writes_ - reads_; } private: bool in_failure_mode_; int writes_; int reads_; int receives_; int buffer_size_; uint32_t read_buffer_index_; DISALLOW_COPY_AND_ASSIGN(MockCancelableSyncSocket); }; class AudioInputSyncWriterUnderTest : public AudioInputSyncWriter { public: AudioInputSyncWriterUnderTest(void* shared_memory, size_t shared_memory_size, int shared_memory_segment_count, const media::AudioParameters& params, base::CancelableSyncSocket* socket) : AudioInputSyncWriter(shared_memory, shared_memory_size, shared_memory_segment_count, params) { socket_.reset(socket); } ~AudioInputSyncWriterUnderTest() override {} MOCK_METHOD1(AddToNativeLog, void(const std::string& message)); }; class AudioInputSyncWriterTest : public testing::Test { public: AudioInputSyncWriterTest() : socket_(nullptr) { const media::ChannelLayout layout = media::GuessChannelLayout(channels); EXPECT_NE(media::ChannelLayout::CHANNEL_LAYOUT_UNSUPPORTED, layout); AudioParameters audio_params( AudioParameters::AUDIO_FAKE, layout, sampling_frequency_hz, bits_per_sample, frames); const uint32_t segment_size = sizeof(media::AudioInputBufferParameters) + AudioBus::CalculateMemorySize(audio_params); size_t data_size = kSegments * segment_size; EXPECT_LE(data_size, sizeof(data)); socket_ = new MockCancelableSyncSocket(kSegments); writer_.reset(new AudioInputSyncWriterUnderTest( &data[0], data_size, kSegments, audio_params, socket_)); audio_bus_ = AudioBus::Create(audio_params); } ~AudioInputSyncWriterTest() override { } // Get total number of expected log calls. On non-Android we expect one log // call at first Write() call, zero on Android. Besides that only for errors // and fifo info. int GetTotalNumberOfExpectedLogCalls(int expected_calls_due_to_error) { #if defined(OS_ANDROID) return expected_calls_due_to_error; #else return expected_calls_due_to_error + 1; #endif } // Tests expected numbers which are given as arguments. bool TestSocketAndFifoExpectations(int number_of_buffers_in_socket, size_t number_of_verifications_in_socket, size_t number_of_buffers_in_fifo) { EXPECT_EQ(number_of_buffers_in_socket, socket_->NumberOfBuffersFilled()); EXPECT_EQ(number_of_verifications_in_socket, socket_->Peek()); EXPECT_EQ(number_of_buffers_in_fifo, writer_->overflow_buses_.size()); return number_of_buffers_in_socket == socket_->NumberOfBuffersFilled() && number_of_verifications_in_socket == socket_->Peek() && number_of_buffers_in_fifo == writer_->overflow_buses_.size(); } protected: scoped_ptr writer_; MockCancelableSyncSocket* socket_; scoped_ptr audio_bus_; private: TestBrowserThreadBundle thread_bundle_; DISALLOW_COPY_AND_ASSIGN(AudioInputSyncWriterTest); }; TEST_F(AudioInputSyncWriterTest, SingleWriteAndRead) { EXPECT_CALL(*writer_.get(), AddToNativeLog(_)) .Times(GetTotalNumberOfExpectedLogCalls(0)); writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(1, 0, 0)); socket_->Read(1); EXPECT_TRUE(TestSocketAndFifoExpectations(0, 1 * sizeof(uint32_t), 0)); } TEST_F(AudioInputSyncWriterTest, MultipleWritesAndReads) { EXPECT_CALL(*writer_.get(), AddToNativeLog(_)) .Times(GetTotalNumberOfExpectedLogCalls(0)); for (int i = 1; i <= 2 * kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(1, 0, 0)); socket_->Read(1); EXPECT_TRUE(TestSocketAndFifoExpectations(0, 1 * sizeof(uint32_t), 0)); } } TEST_F(AudioInputSyncWriterTest, MultipleWritesNoReads) { EXPECT_CALL(*writer_.get(), AddToNativeLog(_)) .Times(GetTotalNumberOfExpectedLogCalls(1)); // Fill the ring buffer. for (int i = 1; i <= kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(i, 0, 0)); } // Now the ring buffer is full, do more writes. We should start filling the // fifo and should get one extra log call for that. for (size_t i = 1; i <= kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, i)); } } TEST_F(AudioInputSyncWriterTest, FillAndEmptyRingBuffer) { EXPECT_CALL(*writer_.get(), AddToNativeLog(_)) .Times(GetTotalNumberOfExpectedLogCalls(2)); // Fill the ring buffer. for (int i = 1; i <= kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); } EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0)); // Empty half of the ring buffer. const int buffers_to_read = kSegments / 2; socket_->Read(buffers_to_read); EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments - buffers_to_read, buffers_to_read * sizeof(uint32_t), 0)); // Fill up again. The first write should do receive until that queue is // empty. for (int i = kSegments - buffers_to_read + 1; i <= kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(i, 0, 0)); } // Another write, should put the data in the fifo, and render an extra log // call. writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 1)); // Empty the ring buffer. socket_->Read(kSegments); EXPECT_TRUE( TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), 1)); // Another write, should do receive until that queue is empty and write both // the data in the fifo and the new data, and render a log call. writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(2, 0, 0)); // Read the two data blocks. socket_->Read(2); EXPECT_TRUE(TestSocketAndFifoExpectations(0, 2 * sizeof(uint32_t), 0)); } TEST_F(AudioInputSyncWriterTest, FillRingBufferAndFifo) { EXPECT_CALL(*writer_.get(), AddToNativeLog(_)) .Times(GetTotalNumberOfExpectedLogCalls(2)); // Fill the ring buffer. for (int i = 1; i <= kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); } EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0)); // Fill the fifo. Should render one log call for starting filling it. const size_t max_fifo_size = AudioInputSyncWriter::kMaxOverflowBusesSize; for (size_t i = 1; i <= max_fifo_size; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); } EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, max_fifo_size)); // Another write, data should be dropped and render one log call. writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, max_fifo_size)); } TEST_F(AudioInputSyncWriterTest, MultipleFillAndEmptyRingBufferAndPartOfFifo) { EXPECT_CALL(*writer_.get(), AddToNativeLog(_)) .Times(GetTotalNumberOfExpectedLogCalls(4)); // Fill the ring buffer. for (int i = 1; i <= kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); } EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0)); // Write more data, should be put in the fifo and render one log call for // starting filling it. for (size_t i = 1; i <= 2 * kSegments; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); } EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 2 * kSegments)); // Empty the ring buffer. socket_->Read(kSegments); EXPECT_TRUE(TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), 2 * kSegments)); // Another write should fill up the ring buffer with data from the fifo and // put this data into the fifo. writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, kSegments + 1)); // Empty the ring buffer again. socket_->Read(kSegments); EXPECT_TRUE(TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), kSegments + 1)); // Another write should fill up the ring buffer with data from the fifo and // put this data into the fifo. writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 2)); // Empty the ring buffer again. socket_->Read(kSegments); EXPECT_TRUE( TestSocketAndFifoExpectations(0, kSegments * sizeof(uint32_t), 2)); // Another write should put the remaining data in the fifo in the ring buffer // together with this data. Should render a log call for emptying the fifo. writer_->Write(audio_bus_.get(), 0, false, 0); EXPECT_TRUE(TestSocketAndFifoExpectations(3, 0, 0)); // Read the remaining data. socket_->Read(3); EXPECT_TRUE(TestSocketAndFifoExpectations(0, 3 * sizeof(uint32_t), 0)); // Fill the ring buffer and part of the fifo. Should render one log call for // starting filling it. for (int i = 1; i <= kSegments + 2; ++i) { writer_->Write(audio_bus_.get(), 0, false, 0); } EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 2)); // Empty both. Should render a log call for emptying the fifo. socket_->Read(kSegments); writer_->Write(audio_bus_.get(), 0, false, 0); socket_->Read(3); EXPECT_TRUE(TestSocketAndFifoExpectations(0, 3 * sizeof(uint32_t), 0)); } } // namespace content