blob: e7d34c69b1cdeb1175d4c1b0032d7132a06e1fbe [file] [log] [blame]
// 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 "services/audio/input_sync_writer.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/sync_socket.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_parameters.h"
#include "media/base/channel_layout.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace audio {
namespace {
// Number of audio buffers in the faked ring buffer.
const int kSegments = 10;
} // 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:
explicit 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<uint32_t*>(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 InputSyncWriterTest : public testing::Test {
public:
InputSyncWriterTest() {
const int sampling_frequency_hz = 16000;
const int frames = sampling_frequency_hz / 100; // 10 ms
const media::AudioParameters audio_params(
media::AudioParameters::AUDIO_FAKE, media::CHANNEL_LAYOUT_MONO,
sampling_frequency_hz, frames);
const uint32_t data_size =
ComputeAudioInputBufferSize(audio_params, kSegments);
auto shared_memory = base::ReadOnlySharedMemoryRegion::Create(data_size);
EXPECT_TRUE(shared_memory.IsValid());
auto socket = std::make_unique<MockCancelableSyncSocket>(kSegments);
socket_ = socket.get();
writer_ = std::make_unique<InputSyncWriter>(
mock_logger_.Get(), std::move(shared_memory), std::move(socket),
kSegments, audio_params);
audio_bus_ = media::AudioBus::Create(audio_params);
}
~InputSyncWriterTest() override {}
// Get total number of expected log calls. On non-Android we expect one log
// call at first Write() call, zero on Android. We also expect all call in the
// with a glitch summary from the destructor. 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 + 1;
#else
return expected_calls_due_to_error + 2;
#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_data_.size());
return number_of_buffers_in_socket == socket_->NumberOfBuffersFilled() &&
number_of_verifications_in_socket == socket_->Peek() &&
number_of_buffers_in_fifo == writer_->overflow_data_.size();
}
protected:
using MockLogger =
base::MockCallback<base::RepeatingCallback<void(const std::string&)>>;
base::test::ScopedTaskEnvironment env_;
MockLogger mock_logger_;
std::unique_ptr<InputSyncWriter> writer_;
MockCancelableSyncSocket* socket_;
std::unique_ptr<media::AudioBus> audio_bus_;
private:
DISALLOW_COPY_AND_ASSIGN(InputSyncWriterTest);
};
TEST_F(InputSyncWriterTest, SingleWriteAndRead) {
EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(0));
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
EXPECT_TRUE(TestSocketAndFifoExpectations(1, 0, 0));
socket_->Read(1);
EXPECT_TRUE(TestSocketAndFifoExpectations(0, 1 * sizeof(uint32_t), 0));
}
TEST_F(InputSyncWriterTest, MultipleWritesAndReads) {
EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(0));
for (int i = 1; i <= 2 * kSegments; ++i) {
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
EXPECT_TRUE(TestSocketAndFifoExpectations(1, 0, 0));
socket_->Read(1);
EXPECT_TRUE(TestSocketAndFifoExpectations(0, 1 * sizeof(uint32_t), 0));
}
}
TEST_F(InputSyncWriterTest, MultipleWritesNoReads) {
EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(1));
// Fill the ring buffer.
for (int i = 1; i <= kSegments; ++i) {
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
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, base::TimeTicks::Now());
EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, i));
}
}
TEST_F(InputSyncWriterTest, FillAndEmptyRingBuffer) {
EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(2));
// Fill the ring buffer.
for (int i = 1; i <= kSegments; ++i) {
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
}
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, base::TimeTicks::Now());
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, base::TimeTicks::Now());
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, base::TimeTicks::Now());
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(InputSyncWriterTest, FillRingBufferAndFifo) {
EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(2));
// Fill the ring buffer.
for (int i = 1; i <= kSegments; ++i) {
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
}
EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, 0));
// Fill the fifo. Should render one log call for starting filling it.
const size_t max_fifo_size = InputSyncWriter::kMaxOverflowBusesSize;
for (size_t i = 1; i <= max_fifo_size; ++i) {
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
}
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, base::TimeTicks::Now());
EXPECT_TRUE(TestSocketAndFifoExpectations(kSegments, 0, max_fifo_size));
}
TEST_F(InputSyncWriterTest, MultipleFillAndEmptyRingBufferAndPartOfFifo) {
EXPECT_CALL(mock_logger_, Run(_)).Times(GetTotalNumberOfExpectedLogCalls(4));
// Fill the ring buffer.
for (int i = 1; i <= kSegments; ++i) {
writer_->Write(audio_bus_.get(), 0, false, base::TimeTicks::Now());
}
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, base::TimeTicks::Now());
}
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, base::TimeTicks::Now());
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, base::TimeTicks::Now());
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, base::TimeTicks::Now());
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, base::TimeTicks::Now());
}
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, base::TimeTicks::Now());
socket_->Read(3);
EXPECT_TRUE(TestSocketAndFifoExpectations(0, 3 * sizeof(uint32_t), 0));
}
} // namespace audio