Commit 74f1ae4f authored by Tsuyoshi Horo's avatar Tsuyoshi Horo Committed by Commit Bot

Revert "Reland "[Chromecast] Update mixer unittests""

This reverts commit 07c7facb.

Reason for revert: Caused build error

https://ci.chromium.org/buildbot/chromium.linux/Fuchsia%20ARM64%20Cast%20Audio/5780
../../chromecast/media/cma/backend/stream_mixer_unittest.cc:423:13: error: unused function 'DeathRegex' [-Werror,-Wunused-function]
std::string DeathRegex(const std::string& regex) {
            ^
1 error generated.

Original change's description:
> Reland "[Chromecast] Update mixer unittests"
> 
> This reverts commit d0ca87de.
> 
> Added ifdef guard for death tests.
> 
> Bug: internal b/71559266
> Change-Id: I204a00d7a1759a4a18407f5162843366e9229da5
> Reviewed-on: https://chromium-review.googlesource.com/941789
> Reviewed-by: Scott Graham <scottmg@chromium.org>
> Reviewed-by: Luke Halliwell <halliwell@chromium.org>
> Commit-Queue: Kenneth MacKay <kmackay@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#539976}

TBR=halliwell@chromium.org,scottmg@chromium.org,kmackay@chromium.org

Change-Id: I05ba642b66d4bb1e2f1eb8fe8e86c705b361a9a8
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: internal b/71559266
Reviewed-on: https://chromium-review.googlesource.com/942422Reviewed-by: default avatarTsuyoshi Horo <horo@chromium.org>
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#539981}
parent 44f23ca2
...@@ -175,11 +175,11 @@ cast_source_set("for_mixer_audio") { ...@@ -175,11 +175,11 @@ cast_source_set("for_mixer_audio") {
test("cast_audio_backend_unittests") { test("cast_audio_backend_unittests") {
testonly = true testonly = true
sources = [ sources = [
"audio_fader_unittest.cc",
"filter_group_unittest.cc", "filter_group_unittest.cc",
"mock_mixer_source.cc", "mock_mixer_source.cc",
"mock_mixer_source.h", "mock_mixer_source.h",
"stream_mixer_unittest.cc",
# "stream_mixer_unittest.cc",
] ]
deps = [ deps = [
......
...@@ -84,8 +84,7 @@ void AudioFader::CompleteFill(::media::AudioBus* buffer, int filled_frames) { ...@@ -84,8 +84,7 @@ void AudioFader::CompleteFill(::media::AudioBus* buffer, int filled_frames) {
case State::kFadingOut: case State::kFadingOut:
// Fade back in. // Fade back in.
state_ = State::kFadingIn; state_ = State::kFadingIn;
fade_frames_remaining_ = fade_frames_remaining_ = fade_frames_ - fade_frames_remaining_;
std::max(0, fade_frames_ - fade_frames_remaining_ - 1);
break; break;
} }
FadeIn(buffer, filled_frames); FadeIn(buffer, filled_frames);
...@@ -101,8 +100,7 @@ void AudioFader::IncompleteFill(::media::AudioBus* buffer, int filled_frames) { ...@@ -101,8 +100,7 @@ void AudioFader::IncompleteFill(::media::AudioBus* buffer, int filled_frames) {
case State::kFadingIn: case State::kFadingIn:
// Fade back out. // Fade back out.
state_ = State::kFadingOut; state_ = State::kFadingOut;
fade_frames_remaining_ = fade_frames_remaining_ = fade_frames_ - fade_frames_remaining_;
std::max(0, fade_frames_ - fade_frames_remaining_ - 1);
break; break;
case State::kPlaying: case State::kPlaying:
// Fade out. // Fade out.
......
// Copyright 2018 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 <algorithm>
#include <limits>
#include "base/macros.h"
#include "chromecast/media/cma/backend/audio_fader.h"
#include "media/base/audio_bus.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
namespace chromecast {
namespace media {
namespace {
const int kNumChannels = 2;
const int kFadeFrames = 128;
class TestFaderSource : public AudioFader::Source {
public:
TestFaderSource()
: max_fill_frames_(std::numeric_limits<int>::max()),
total_requested_frames_(0),
last_requested_frames_(0),
last_filled_frames_(0) {}
// AudioFader::Source implementation:
int FillFaderFrames(::media::AudioBus* buffer,
int frame_offset,
int num_frames) override {
last_requested_frames_ = num_frames;
total_requested_frames_ += num_frames;
int count = std::min(num_frames, max_fill_frames_);
last_filled_frames_ = count;
for (int c = 0; c < buffer->channels(); ++c) {
float* channel_data = buffer->channel(c) + frame_offset;
std::fill_n(channel_data, count, 1.0f);
}
return count;
}
void set_max_fill_frames(int frames) { max_fill_frames_ = frames; }
int total_requested_frames() const { return total_requested_frames_; }
int last_requested_frames() const { return last_requested_frames_; }
int last_filled_frames() const { return last_filled_frames_; }
private:
int max_fill_frames_;
int total_requested_frames_;
int last_requested_frames_;
int last_filled_frames_;
DISALLOW_COPY_AND_ASSIGN(TestFaderSource);
};
} // namespace
TEST(AudioFaderTest, Startup) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2;
int frames_needed = fader.FramesNeededFromSource(kFillSize);
// The fader should fill its internal buffer, plus the size of the request.
EXPECT_EQ(frames_needed, kFadeFrames + kFillSize);
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Fader's internal buffer should be full.
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
// Data should be faded in.
EXPECT_EQ(dest->channel(0)[0], 0.0f);
EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f);
}
TEST(AudioFaderTest, FadeInOver2Buffers) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2 / 3;
int frames_needed = fader.FramesNeededFromSource(kFillSize);
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Fader's internal buffer should be full.
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
// Data should be partially faded in.
EXPECT_EQ(dest->channel(0)[0], 0.0f);
EXPECT_GT(dest->channel(0)[kFillSize - 1], 0.0f);
EXPECT_LT(dest->channel(0)[kFillSize - 1], 1.0f);
// Fill more data.
frames_needed += fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Fader's internal buffer should be full.
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
// Data should be faded in.
EXPECT_EQ(dest->channel(0)[kFillSize - 1], 1.0f);
}
TEST(AudioFaderTest, ContinuePlaying) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2;
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
int frames_needed = fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Data should be faded in.
EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f);
// Now request more data. Data should remain fully faded in.
frames_needed += fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
EXPECT_EQ(dest->channel(0)[0], 1.0f);
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Fader's internal buffer should be full.
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
}
TEST(AudioFaderTest, FadeOut) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2;
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
int frames_needed = fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Data should be faded in.
EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f);
// Now request more data. Data should remain fully faded in.
frames_needed += fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
EXPECT_EQ(dest->channel(0)[0], 1.0f);
// Now make the source not provide enough data.
EXPECT_GT(fader.FramesNeededFromSource(kFillSize), 0);
source.set_max_fill_frames(0);
frames_needed += fader.FramesNeededFromSource(kFillSize);
int filled = fader.FillFrames(kFillSize, dest.get());
EXPECT_EQ(filled, kFadeFrames);
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Data should be faded out.
EXPECT_EQ(dest->channel(0)[0], 1.0f);
EXPECT_LT(dest->channel(0)[filled - 1], 0.1f);
// Fader's internal buffer should be empty since we are fully faded out.
EXPECT_EQ(fader.buffered_frames(), 0);
}
TEST(AudioFaderTest, FadeOutPartially) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2;
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
int frames_needed = fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Data should be faded in.
EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f);
// Now request more data. Data should remain fully faded in.
frames_needed += fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
EXPECT_EQ(dest->channel(0)[0], 1.0f);
// Now make the source not provide enough data.
EXPECT_GT(fader.FramesNeededFromSource(kFillSize), 0);
source.set_max_fill_frames(0);
frames_needed += fader.FramesNeededFromSource(kFadeFrames / 3);
int filled = fader.FillFrames(kFadeFrames / 3, dest.get());
EXPECT_EQ(filled, kFadeFrames / 3);
// Data should be partially faded out.
EXPECT_EQ(dest->channel(0)[0], 1.0f);
EXPECT_LT(dest->channel(0)[filled - 1], 1.0f);
float fade_min = dest->channel(0)[filled - 1];
// Fader's internal buffer should be partially full.
EXPECT_LT(fader.buffered_frames(), kFadeFrames);
// Now let the source provide data again.
source.set_max_fill_frames(std::numeric_limits<int>::max());
frames_needed += fader.FramesNeededFromSource(kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Data should fade back in from the point it faded out to.
EXPECT_GE(dest->channel(0)[0], fade_min);
EXPECT_EQ(dest->channel(0)[kFillSize - 1], 1.0f);
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Fader's internal buffer should be full.
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
}
TEST(AudioFaderTest, IncompleteFadeIn) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2;
int frames_needed = fader.FramesNeededFromSource(kFillSize);
// The source only partially fills the fader request. Since we're fading in
// from silence, the fader should output silence.
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
source.set_max_fill_frames(10);
int filled = fader.FillFrames(kFillSize, dest.get());
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Fader's internal buffer should be empty.
EXPECT_EQ(fader.buffered_frames(), 0);
// Data should be silent.
for (int i = 0; i < filled; ++i) {
EXPECT_EQ(dest->channel(0)[i], 0.0f);
}
}
TEST(AudioFaderTest, FadeInPartially) {
TestFaderSource source;
AudioFader fader(&source, kNumChannels, kFadeFrames);
// Fader has no buffered frames initially.
EXPECT_EQ(fader.buffered_frames(), 0);
const int kFillSize = kFadeFrames * 2 / 3;
int frames_needed = fader.FramesNeededFromSource(kFillSize);
auto dest = ::media::AudioBus::Create(kNumChannels, kFillSize);
EXPECT_EQ(fader.FillFrames(kFillSize, dest.get()), kFillSize);
// Fader's internal buffer should be full.
EXPECT_EQ(fader.buffered_frames(), kFadeFrames);
// Data should be partially faded in.
EXPECT_EQ(dest->channel(0)[0], 0.0f);
EXPECT_GT(dest->channel(0)[kFillSize - 1], 0.0f);
EXPECT_LT(dest->channel(0)[kFillSize - 1], 1.0f);
float fade_max = dest->channel(0)[kFillSize - 1];
// Now tell the source not to provide any data. The fader output should fade
// back out to silence.
source.set_max_fill_frames(0);
frames_needed += fader.FramesNeededFromSource(kFillSize);
int filled = fader.FillFrames(kFillSize, dest.get());
// Data should be faded out.
EXPECT_LE(dest->channel(0)[0], fade_max);
EXPECT_EQ(dest->channel(0)[filled - 1], 0.0f);
// Test that FramesNeededFromSource() works correctly.
EXPECT_EQ(source.total_requested_frames(), frames_needed);
// Fader's internal buffer should be empty.
EXPECT_EQ(fader.buffered_frames(), 0);
}
} // namespace media
} // namespace chromecast
...@@ -17,9 +17,8 @@ ...@@ -17,9 +17,8 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/values.h" #include "base/values.h"
#include "chromecast/media/cma/backend/mixer_input.h" #include "chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.h"
#include "chromecast/media/cma/backend/mixer_output_stream.h" #include "chromecast/media/cma/backend/alsa/mock_alsa_wrapper.h"
#include "chromecast/media/cma/backend/mock_mixer_source.h"
#include "chromecast/media/cma/backend/post_processing_pipeline.h" #include "chromecast/media/cma/backend/post_processing_pipeline.h"
#include "chromecast/public/volume_control.h" #include "chromecast/public/volume_control.h"
#include "media/audio/audio_device_description.h" #include "media/audio/audio_device_description.h"
...@@ -30,7 +29,6 @@ ...@@ -30,7 +29,6 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using testing::_; using testing::_;
using testing::NiceMock;
namespace chromecast { namespace chromecast {
namespace media { namespace media {
...@@ -42,11 +40,16 @@ using FloatType = ::media::Float32SampleTypeTraits; ...@@ -42,11 +40,16 @@ using FloatType = ::media::Float32SampleTypeTraits;
// Testing constants that are common to multiple test cases. // Testing constants that are common to multiple test cases.
const size_t kBytesPerSample = sizeof(int32_t); const size_t kBytesPerSample = sizeof(int32_t);
const int kNumChannels = 2; const int kNumChannels = 2;
const int kTestMaxReadSize = 4096;
// kTestSamplesPerSecond needs to be higher than kLowSampleRateCutoff for the // kTestSamplesPerSecond needs to be higher than kLowSampleRateCutoff for the
// mixer to use it. // mixer to use it.
const int kTestSamplesPerSecond = 48000; const int kTestSamplesPerSecond = 48000;
// Stream mixer alsa will never pull more than this many frames at a time.
const int kMaxWriteSizeMs = 20;
const int kMaxChunkSize = kTestSamplesPerSecond * kMaxWriteSizeMs / 1000;
// This array holds |NUM_DATA_SETS| sets of arbitrary interleaved float data. // This array holds |NUM_DATA_SETS| sets of arbitrary interleaved float data.
// Each set holds |NUM_SAMPLES| / kNumChannels frames of data. // Each set holds |NUM_SAMPLES| / kNumChannels frames of data.
#define NUM_DATA_SETS 2u #define NUM_DATA_SETS 2u
...@@ -54,7 +57,7 @@ const int kTestSamplesPerSecond = 48000; ...@@ -54,7 +57,7 @@ const int kTestSamplesPerSecond = 48000;
// Note: Test data should be represented as 32-bit integers and copied into // Note: Test data should be represented as 32-bit integers and copied into
// ::media::AudioBus instances, rather than wrapping statically declared float // ::media::AudioBus instances, rather than wrapping statically declared float
// arrays. The latter method is brittle, as ::media::AudioBus requires 16-byte // arrays. The latter method is brittle, as ::media::AudioBus requires 16-bit
// alignment for internal data. // alignment for internal data.
const int32_t kTestData[NUM_DATA_SETS][NUM_SAMPLES] = { const int32_t kTestData[NUM_DATA_SETS][NUM_SAMPLES] = {
{ {
...@@ -88,7 +91,7 @@ const int32_t kTestData[NUM_DATA_SETS][NUM_SAMPLES] = { ...@@ -88,7 +91,7 @@ const int32_t kTestData[NUM_DATA_SETS][NUM_SAMPLES] = {
-877887021, -870241979, 1322678128, -344799975, -877887021, -870241979, 1322678128, -344799975,
}}; }};
// Compensate for integer arithmetic errors. // Compensate for integer arithmatic errors.
const int kMaxDelayErrorUs = 2; const int kMaxDelayErrorUs = 2;
const char kDelayModuleSolib[] = "delay.so"; const char kDelayModuleSolib[] = "delay.so";
...@@ -144,71 +147,161 @@ std::unique_ptr<::media::AudioBus> GetTestData(size_t index) { ...@@ -144,71 +147,161 @@ std::unique_ptr<::media::AudioBus> GetTestData(size_t index) {
return data; return data;
} }
class MockMixerOutput : public MixerOutputStream { class MockInputQueue : public StreamMixer::InputQueue {
public: public:
MockMixerOutput() { MockInputQueue(int samples_per_second,
ON_CALL(*this, Start(_, _)) const std::string& device_id =
.WillByDefault(testing::Invoke(this, &MockMixerOutput::StartImpl)); ::media::AudioDeviceDescription::kDefaultDeviceId)
ON_CALL(*this, GetSampleRate()) : paused_(true),
samples_per_second_(samples_per_second),
max_read_size_(kTestMaxReadSize),
multiplier_(1.0),
primary_(true),
deleting_(false),
device_id_(device_id),
filter_group_(nullptr) {
ON_CALL(*this, GetResampledData(_, _))
.WillByDefault( .WillByDefault(
testing::Invoke(this, &MockMixerOutput::GetSampleRateImpl)); testing::Invoke(this, &MockInputQueue::DoGetResampledData));
ON_CALL(*this, GetRenderingDelay()) ON_CALL(*this, VolumeScaleAccumulate(_, _, _, _))
.WillByDefault( .WillByDefault(
testing::Invoke(this, &MockMixerOutput::GetRenderingDelayImpl)); testing::Invoke(this, &MockInputQueue::DoVolumeScaleAccumulate));
ON_CALL(*this, OptimalWriteFramesCount()) ON_CALL(*this, PrepareToDelete(_))
.WillByDefault( .WillByDefault(
testing::Invoke(this, &MockMixerOutput::OptimalWriteFramesImpl)); testing::Invoke(this, &MockInputQueue::DoPrepareToDelete));
ON_CALL(*this, Write(_, _, _))
.WillByDefault(testing::Invoke(this, &MockMixerOutput::WriteImpl));
} }
~MockInputQueue() override {}
MOCK_METHOD2(Start, bool(int, int));
MOCK_METHOD0(GetSampleRate, int()); bool paused() const { return paused_; }
MOCK_METHOD0(GetRenderingDelay,
MediaPipelineBackend::AudioDecoder::RenderingDelay()); // StreamMixer::InputQueue implementation:
MOCK_METHOD0(OptimalWriteFramesCount, int()); int input_samples_per_second() const override { return samples_per_second_; }
MOCK_METHOD3(Write, bool(const float*, int, bool*)); bool primary() const override { return primary_; }
MOCK_METHOD0(Stop, void()); bool IsDeleting() const override { return deleting_; }
MOCK_METHOD1(Initialize,
int sample_rate() const { return sample_rate_; } void(const MediaPipelineBackend::AudioDecoder::RenderingDelay&
const std::vector<float>& data() const { return data_; } mixer_rendering_delay));
std::string device_id() const override { return device_id_; }
void ClearData() { data_.clear(); } AudioContentType content_type() const override {
return AudioContentType::kMedia;
}
void set_filter_group(FilterGroup* group) override { filter_group_ = group; }
FilterGroup* filter_group() override { return filter_group_; }
int MaxReadSize() override { return max_read_size_; }
MOCK_METHOD2(GetResampledData, void(::media::AudioBus* dest, int frames));
MOCK_METHOD4(
VolumeScaleAccumulate,
void(bool repeat_transition, const float* src, int frames, float* dest));
MOCK_METHOD0(OnSkipped, void());
MOCK_METHOD1(AfterWriteFrames,
void(const MediaPipelineBackend::AudioDecoder::RenderingDelay&
mixer_rendering_delay));
MOCK_METHOD1(SignalError, void(StreamMixerInput::MixerError error));
MOCK_METHOD1(PrepareToDelete, void(const OnReadyToDeleteCb& delete_cb));
void SetContentTypeVolume(float volume, int fade_ms) override {}
void SetMuted(bool muted) override {}
float TargetVolume() override { return multiplier_; }
float InstantaneousVolume() override { return multiplier_; }
// Setters and getters for test control.
void SetPaused(bool paused) { paused_ = paused; }
void SetMaxReadSize(int max_read_size) { max_read_size_ = max_read_size; }
void SetData(std::unique_ptr<::media::AudioBus> data) {
CHECK(!data_);
data_ = std::move(data);
max_read_size_ = data_->frames();
}
void SetVolumeMultiplier(float multiplier) {
CHECK(multiplier >= 0.0 && multiplier <= 1.0);
multiplier_ = multiplier;
}
void SetPrimary(bool primary) { primary_ = primary; }
const ::media::AudioBus& data() {
CHECK(data_);
return *data_;
}
float multiplier() const { return multiplier_; }
private: private:
bool StartImpl(int requested_sample_rate, int channels) { void DoGetResampledData(::media::AudioBus* dest, int frames) {
sample_rate_ = requested_sample_rate; CHECK(dest);
return true; CHECK_GE(dest->frames(), frames);
if (data_) {
data_->CopyPartialFramesTo(0, frames, 0, dest);
} else {
dest->ZeroFramesPartial(0, frames);
}
} }
int GetSampleRateImpl() { return sample_rate_; } void DoVolumeScaleAccumulate(bool repeat_transition,
const float* src,
int frames,
float* dest) {
CHECK(src);
CHECK(dest);
CHECK(multiplier_ >= 0.0 && multiplier_ <= 1.0);
::media::vector_math::FMAC(src, multiplier_, frames, dest);
}
MediaPipelineBackend::AudioDecoder::RenderingDelay GetRenderingDelayImpl() { void DoPrepareToDelete(const OnReadyToDeleteCb& delete_cb) {
return MediaPipelineBackend::AudioDecoder::RenderingDelay(); deleting_ = true;
delete_cb.Run(this);
} }
int OptimalWriteFramesImpl() { return 256; } bool paused_;
int samples_per_second_;
int max_read_size_;
float multiplier_;
bool primary_;
bool deleting_;
const std::string device_id_;
FilterGroup* filter_group_;
bool WriteImpl(const float* data, std::unique_ptr<::media::AudioBus> data_;
int data_size,
bool* out_playback_interrupted) {
*out_playback_interrupted = false;
data_.insert(data_.end(), data, data + data_size);
return true;
}
int sample_rate_ = 0; DISALLOW_COPY_AND_ASSIGN(MockInputQueue);
std::vector<float> data_;
}; };
class MockPostProcessorFactory;
class MockPostProcessor : public PostProcessingPipeline { class MockPostProcessor : public PostProcessingPipeline {
public: public:
MockPostProcessor(MockPostProcessorFactory* factory, MockPostProcessor(const std::string& name,
const std::string& name,
const base::ListValue* filter_description_list, const base::ListValue* filter_description_list,
int channels); int channels)
~MockPostProcessor() override; : name_(name), num_output_channels_(channels) {
CHECK(instances_.insert({name_, this}).second);
ON_CALL(*this, ProcessFrames(_, _, _, _))
.WillByDefault(
testing::Invoke(this, &MockPostProcessor::DoProcessFrames));
if (!filter_description_list) {
// This happens for PostProcessingPipeline with no post-processors.
return;
}
// Parse |filter_description_list| for parameters.
for (size_t i = 0; i < filter_description_list->GetSize(); ++i) {
const base::DictionaryValue* description_dict;
CHECK(filter_description_list->GetDictionary(i, &description_dict));
std::string solib;
CHECK(description_dict->GetString("processor", &solib));
// This will initially be called with the actual pipeline on creation.
// Ignore and wait for the call to ResetPostProcessorsForTest.
if (solib == kDelayModuleSolib) {
const base::DictionaryValue* processor_config_dict;
CHECK(
description_dict->GetDictionary("config", &processor_config_dict));
int module_delay;
CHECK(processor_config_dict->GetInteger("delay", &module_delay));
rendering_delay_ += module_delay;
processor_config_dict->GetBoolean("ringing", &ringing_);
processor_config_dict->GetInteger("output_channels",
&num_output_channels_);
}
}
}
~MockPostProcessor() override { instances_.erase(name_); }
MOCK_METHOD4( MOCK_METHOD4(
ProcessFrames, ProcessFrames,
int(float* data, int num_frames, float current_volume, bool is_silence)); int(float* data, int num_frames, float current_volume, bool is_silence));
...@@ -223,6 +316,9 @@ class MockPostProcessor : public PostProcessingPipeline { ...@@ -223,6 +316,9 @@ class MockPostProcessor : public PostProcessingPipeline {
MOCK_METHOD2(SetPostProcessorConfig, MOCK_METHOD2(SetPostProcessorConfig,
void(const std::string& name, const std::string& config)); void(const std::string& name, const std::string& config));
MOCK_METHOD1(UpdatePlayoutChannel, void(int)); MOCK_METHOD1(UpdatePlayoutChannel, void(int));
static std::unordered_map<std::string, MockPostProcessor*>* instances() {
return &instances_;
}
private: private:
int DoProcessFrames(float* data, int DoProcessFrames(float* data,
...@@ -233,8 +329,8 @@ class MockPostProcessor : public PostProcessingPipeline { ...@@ -233,8 +329,8 @@ class MockPostProcessor : public PostProcessingPipeline {
return rendering_delay_; return rendering_delay_;
} }
MockPostProcessorFactory* const factory_; static std::unordered_map<std::string, MockPostProcessor*> instances_;
const std::string name_; std::string name_;
int rendering_delay_ = 0; int rendering_delay_ = 0;
bool ringing_ = false; bool ringing_ = false;
float* output_buffer_ = nullptr; float* output_buffer_ = nullptr;
...@@ -243,6 +339,22 @@ class MockPostProcessor : public PostProcessingPipeline { ...@@ -243,6 +339,22 @@ class MockPostProcessor : public PostProcessingPipeline {
DISALLOW_COPY_AND_ASSIGN(MockPostProcessor); DISALLOW_COPY_AND_ASSIGN(MockPostProcessor);
}; };
std::unordered_map<std::string, MockPostProcessor*>
MockPostProcessor::instances_;
#define EXPECT_CALL_ALL_POSTPROCESSORS(call_sig) \
do { \
for (auto& itr : *MockPostProcessor::instances()) { \
EXPECT_CALL(*itr.second, call_sig); \
} \
} while (0);
void VerifyAndClearPostProcessors() {
for (auto& itr : *MockPostProcessor::instances()) {
testing::Mock::VerifyAndClearExpectations(itr.second);
}
}
class MockPostProcessorFactory : public PostProcessingPipelineFactory { class MockPostProcessorFactory : public PostProcessingPipelineFactory {
public: public:
MockPostProcessorFactory() = default; MockPostProcessorFactory() = default;
...@@ -252,68 +364,10 @@ class MockPostProcessorFactory : public PostProcessingPipelineFactory { ...@@ -252,68 +364,10 @@ class MockPostProcessorFactory : public PostProcessingPipelineFactory {
const base::ListValue* filter_description_list, const base::ListValue* filter_description_list,
int channels) override { int channels) override {
return std::make_unique<testing::NiceMock<MockPostProcessor>>( return std::make_unique<testing::NiceMock<MockPostProcessor>>(
this, name, filter_description_list, channels); name, filter_description_list, channels);
} }
std::unordered_map<std::string, MockPostProcessor*> instances;
}; };
#define EXPECT_CALL_ALL_POSTPROCESSORS(factory, call_sig) \
do { \
for (auto& itr : factory->instances) { \
EXPECT_CALL(*itr.second, call_sig); \
} \
} while (0);
void VerifyAndClearPostProcessors(MockPostProcessorFactory* factory) {
for (auto& itr : factory->instances) {
testing::Mock::VerifyAndClearExpectations(itr.second);
}
}
MockPostProcessor::MockPostProcessor(
MockPostProcessorFactory* factory,
const std::string& name,
const base::ListValue* filter_description_list,
int channels)
: factory_(factory), name_(name), num_output_channels_(channels) {
DCHECK(factory_);
CHECK(factory_->instances.insert({name_, this}).second);
ON_CALL(*this, ProcessFrames(_, _, _, _))
.WillByDefault(
testing::Invoke(this, &MockPostProcessor::DoProcessFrames));
if (!filter_description_list) {
// This happens for PostProcessingPipeline with no post-processors.
return;
}
// Parse |filter_description_list| for parameters.
for (size_t i = 0; i < filter_description_list->GetSize(); ++i) {
const base::DictionaryValue* description_dict;
CHECK(filter_description_list->GetDictionary(i, &description_dict));
std::string solib;
CHECK(description_dict->GetString("processor", &solib));
// This will initially be called with the actual pipeline on creation.
// Ignore and wait for the call to ResetPostProcessorsForTest.
if (solib == kDelayModuleSolib) {
const base::DictionaryValue* processor_config_dict;
CHECK(description_dict->GetDictionary("config", &processor_config_dict));
int module_delay;
CHECK(processor_config_dict->GetInteger("delay", &module_delay));
rendering_delay_ += module_delay;
processor_config_dict->GetBoolean("ringing", &ringing_);
processor_config_dict->GetInteger("output_channels",
&num_output_channels_);
}
}
}
MockPostProcessor::~MockPostProcessor() {
factory_->instances.erase(name_);
}
class MockLoopbackAudioObserver : public CastMediaShlib::LoopbackAudioObserver { class MockLoopbackAudioObserver : public CastMediaShlib::LoopbackAudioObserver {
public: public:
MockLoopbackAudioObserver() = default; MockLoopbackAudioObserver() = default;
...@@ -331,11 +385,11 @@ class MockLoopbackAudioObserver : public CastMediaShlib::LoopbackAudioObserver { ...@@ -331,11 +385,11 @@ class MockLoopbackAudioObserver : public CastMediaShlib::LoopbackAudioObserver {
// Given |inputs|, returns mixed audio data according to the mixing method used // Given |inputs|, returns mixed audio data according to the mixing method used
// by the mixer. // by the mixer.
std::unique_ptr<::media::AudioBus> GetMixedAudioData( std::unique_ptr<::media::AudioBus> GetMixedAudioData(
const std::vector<MockMixerSource*>& inputs) { const std::vector<testing::StrictMock<MockInputQueue>*>& inputs) {
int read_size = 0; int read_size = std::numeric_limits<int>::max();
for (auto* input : inputs) { for (auto* input : inputs) {
CHECK(input); CHECK(input);
read_size = std::max(input->data().frames(), read_size); read_size = std::min(input->MaxReadSize(), read_size);
} }
// Verify all inputs are the right size. // Verify all inputs are the right size.
...@@ -353,11 +407,8 @@ std::unique_ptr<::media::AudioBus> GetMixedAudioData( ...@@ -353,11 +407,8 @@ std::unique_ptr<::media::AudioBus> GetMixedAudioData(
// Sum the sample from each input stream, scaling each stream. // Sum the sample from each input stream, scaling each stream.
*result = 0.0; *result = 0.0;
for (auto* input : inputs) { for (auto* input : inputs)
if (input->data().frames() > f) { *result += *(input->data().channel(c) + f) * input->multiplier();
*result += *(input->data().channel(c) + f) * input->multiplier();
}
}
// Clamp the mixed sample between 1.0 and -1.0. // Clamp the mixed sample between 1.0 and -1.0.
*result = std::min(1.0f, std::max(-1.0f, *result)); *result = std::min(1.0f, std::max(-1.0f, *result));
...@@ -368,25 +419,19 @@ std::unique_ptr<::media::AudioBus> GetMixedAudioData( ...@@ -368,25 +419,19 @@ std::unique_ptr<::media::AudioBus> GetMixedAudioData(
// Like the method above, but accepts a single input. This returns an AudioBus // Like the method above, but accepts a single input. This returns an AudioBus
// with this input after it is scaled and clipped. // with this input after it is scaled and clipped.
std::unique_ptr<::media::AudioBus> GetMixedAudioData(MockMixerSource* input) {
return GetMixedAudioData(std::vector<MockMixerSource*>(1, input));
}
std::unique_ptr<::media::AudioBus> GetMixedAudioData( std::unique_ptr<::media::AudioBus> GetMixedAudioData(
const std::vector<std::unique_ptr<MockMixerSource>>& inputs) { testing::StrictMock<MockInputQueue>* input) {
std::vector<MockMixerSource*> ptrs; return GetMixedAudioData(
for (const auto& i : inputs) { std::vector<testing::StrictMock<MockInputQueue>*>(1, input));
ptrs.push_back(i.get());
}
return GetMixedAudioData(ptrs);
} }
void ToPlanar(const float* interleaved, void ToPlanar(const uint8_t* interleaved,
int num_frames, int num_frames,
::media::AudioBus* planar) { ::media::AudioBus* planar) {
ASSERT_GE(planar->frames(), num_frames); ASSERT_GE(planar->frames(), num_frames);
planar->FromInterleaved<FloatType>(interleaved, num_frames); planar->FromInterleaved<FloatType>(
reinterpret_cast<const float*>(interleaved), num_frames);
} }
// Asserts that |expected| matches |actual| exactly. // Asserts that |expected| matches |actual| exactly.
...@@ -397,9 +442,8 @@ void CompareAudioData(const ::media::AudioBus& expected, ...@@ -397,9 +442,8 @@ void CompareAudioData(const ::media::AudioBus& expected,
for (int c = 0; c < expected.channels(); ++c) { for (int c = 0; c < expected.channels(); ++c) {
const float* expected_data = expected.channel(c); const float* expected_data = expected.channel(c);
const float* actual_data = actual.channel(c); const float* actual_data = actual.channel(c);
for (int f = 0; f < expected.frames(); ++f) { for (int f = 0; f < expected.frames(); ++f)
EXPECT_FLOAT_EQ(*expected_data++, *actual_data++) << c << " " << f; ASSERT_FLOAT_EQ(*expected_data++, *actual_data++) << c << " " << f;
}
} }
} }
...@@ -433,276 +477,313 @@ std::string DeathRegex(const std::string& regex) { ...@@ -433,276 +477,313 @@ std::string DeathRegex(const std::string& regex) {
class StreamMixerTest : public testing::Test { class StreamMixerTest : public testing::Test {
protected: protected:
StreamMixerTest() : message_loop_(new base::MessageLoop()) { StreamMixerTest()
auto output = std::make_unique<NiceMock<MockMixerOutput>>(); : message_loop_(new base::MessageLoop()),
mock_output_ = output.get(); mock_alsa_(new testing::NiceMock<MockAlsaWrapper>()) {
mixer_ = std::make_unique<StreamMixer>(std::move(output), nullptr, StreamMixer::MakeSingleThreadedForTest();
base::ThreadTaskRunnerHandle::Get());
mixer_->SetVolume(AudioContentType::kMedia, 1.0f);
std::string test_pipeline_json = base::StringPrintf( std::string test_pipeline_json = base::StringPrintf(
kTestPipelineJsonTemplate, kDelayModuleSolib, kDefaultProcessorDelay, kTestPipelineJsonTemplate, kDelayModuleSolib, kDefaultProcessorDelay,
kDelayModuleSolib, kTtsProcessorDelay, kDelayModuleSolib, kDelayModuleSolib, kTtsProcessorDelay, kDelayModuleSolib,
kMixProcessorDelay, kDelayModuleSolib, kLinearizeProcessorDelay); kMixProcessorDelay, kDelayModuleSolib, kLinearizeProcessorDelay);
auto factory = std::make_unique<MockPostProcessorFactory>(); StreamMixer::Get()->ResetPostProcessorsForTest(
pp_factory_ = factory.get(); std::make_unique<MockPostProcessorFactory>(), test_pipeline_json);
mixer_->ResetPostProcessorsForTest(std::move(factory), test_pipeline_json); CHECK_EQ(MockPostProcessor::instances()->size(),
CHECK_EQ(pp_factory_->instances.size(),
static_cast<size_t>(kNumPostProcessors)); static_cast<size_t>(kNumPostProcessors));
}
void PlaybackOnce() { auto output = std::make_unique<MixerOutputStreamAlsa>();
// Run one playback iteration. output->SetAlsaWrapperForTest(base::WrapUnique(mock_alsa_));
EXPECT_CALL(*mock_output_, Write(_, StreamMixer::Get()->SetMixerOutputStreamForTest(std::move(output));
mock_output_->OptimalWriteFramesCount() *
mixer_->num_output_channels(),
_))
.Times(1);
base::RunLoop run_loop;
message_loop_->task_runner()->PostTask(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
} }
void WaitForRemoval() { ~StreamMixerTest() override { StreamMixer::Get()->ClearInputsForTest(); }
// Need to wait for the removal task (it is always posted).
base::RunLoop run_loop;
message_loop_->task_runner()->PostTask(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
protected: MockAlsaWrapper* mock_alsa() { return mock_alsa_; }
private:
const std::unique_ptr<base::MessageLoop> message_loop_; const std::unique_ptr<base::MessageLoop> message_loop_;
MockMixerOutput* mock_output_; testing::NiceMock<MockAlsaWrapper>* mock_alsa_;
std::unique_ptr<StreamMixer> mixer_;
MockPostProcessorFactory* pp_factory_;
DISALLOW_COPY_AND_ASSIGN(StreamMixerTest); DISALLOW_COPY_AND_ASSIGN(StreamMixerTest);
}; };
TEST_F(StreamMixerTest, AddSingleInput) { TEST_F(StreamMixerTest, AddSingleInput) {
MockMixerSource input(kTestSamplesPerSecond); auto* input = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(input, InitializeAudioPlayback(_, _)).Times(1);
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1);
EXPECT_CALL(*mock_output_, Stop()).Times(0);
mixer_->AddInput(&input);
mixer_.reset(); EXPECT_CALL(*input, Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(input));
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
} }
TEST_F(StreamMixerTest, AddMultipleInputs) { TEST_F(StreamMixerTest, AddMultipleInputs) {
MockMixerSource input1(kTestSamplesPerSecond); auto* input1 = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
MockMixerSource input2(kTestSamplesPerSecond * 2); auto* input2 =
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond * 2);
EXPECT_CALL(input1, InitializeAudioPlayback(_, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(input2, InitializeAudioPlayback(_, _)).Times(1);
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); EXPECT_CALL(*input1, Initialize(_)).Times(1);
EXPECT_CALL(*mock_output_, Stop()).Times(0); EXPECT_CALL(*input2, Initialize(_)).Times(1);
mixer_->AddInput(&input1); mixer->AddInput(base::WrapUnique(input1));
mixer_->AddInput(&input2); mixer->AddInput(base::WrapUnique(input2));
mixer_.reset(); // The mixer should be ready to play, and should sample to the initial
// sample rate.
EXPECT_EQ(kTestSamplesPerSecond, mixer->output_samples_per_second());
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
} }
TEST_F(StreamMixerTest, RemoveInput) { TEST_F(StreamMixerTest, RemoveInput) {
std::vector<std::unique_ptr<MockMixerSource>> inputs; std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 3; const int kNumInputs = 3;
for (int i = 0; i < kNumInputs; ++i) { for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back( inputs.push_back(new testing::StrictMock<MockInputQueue>(
std::make_unique<MockMixerSource>(kTestSamplesPerSecond * (i + 1))); kTestSamplesPerSecond * (i + 1)));
} }
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Stop()).Times(0);
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], InitializeAudioPlayback(_, _)).Times(1); EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer_->AddInput(inputs[i].get()); mixer->AddInput(base::WrapUnique(inputs[i]));
} }
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], FinalizeAudioPlayback()).Times(1); EXPECT_CALL(*inputs[i], PrepareToDelete(_)).Times(1);
mixer_->RemoveInput(inputs[i].get()); mixer->RemoveInput(inputs[i]);
} }
WaitForRemoval(); // Need to wait for the removal task (it is always posted).
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(mixer->empty());
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
} }
TEST_F(StreamMixerTest, WriteFrames) { TEST_F(StreamMixerTest, WriteFrames) {
std::vector<std::unique_ptr<MockMixerSource>> inputs; std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 3; const int kNumInputs = 3;
for (int i = 0; i < kNumInputs; ++i) { for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back(std::make_unique<MockMixerSource>(kTestSamplesPerSecond)); inputs.push_back(
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond));
inputs.back()->SetPaused(false);
} }
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Stop()).Times(0); mixer->SetFilterFrameAlignmentForTest(1);
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], InitializeAudioPlayback(_, _)).Times(1); EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer_->AddInput(inputs[i].get()); mixer->AddInput(base::WrapUnique(inputs[i]));
} }
for (size_t i = 0; i < inputs.size(); ++i) { ASSERT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
EXPECT_CALL(*inputs[i], FillAudioPlaybackFrames(_, _, _)).Times(1);
}
PlaybackOnce(); // The mixer should pull data from all streams, using the smallest
// MaxReadSize provided by any of the channels.
// TODO(slan): Check that the proper number of frames is pulled.
ASSERT_EQ(3u, inputs.size());
inputs[0]->SetMaxReadSize(kMaxChunkSize + 1);
inputs[1]->SetMaxReadSize(kMaxChunkSize - 1);
inputs[2]->SetMaxReadSize(kMaxChunkSize * 2);
for (auto* input : inputs) {
EXPECT_CALL(*input, GetResampledData(_, kMaxChunkSize - 1)).Times(1);
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kMaxChunkSize - 1, _))
.Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_)).Times(1);
}
mixer_.reset(); // TODO(slan): Verify that the data is mixed properly with math.
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kMaxChunkSize - 1)).Times(1);
mixer->WriteFramesForTest();
// Make two of these streams non-primary, and exhaust a non-primary stream.
// All non-empty streams shall be polled for data and the mixer shall write
// to ALSA.
inputs[1]->SetPrimary(false);
inputs[1]->SetMaxReadSize(0);
EXPECT_CALL(*inputs[1], OnSkipped());
inputs[2]->SetPrimary(false);
for (auto* input : inputs) {
if (input != inputs[1]) {
EXPECT_CALL(*input, GetResampledData(_, kMaxChunkSize)).Times(1);
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kMaxChunkSize, _))
.Times(kNumChannels);
}
EXPECT_CALL(*input, AfterWriteFrames(_)).Times(1);
}
// Note that the new smallest stream shall dictate the length of the write.
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kMaxChunkSize)).Times(1);
mixer->WriteFramesForTest();
// Exhaust a primary stream. No streams shall be polled for data, and no
// data shall be written to ALSA.
inputs[0]->SetMaxReadSize(0);
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, _)).Times(0);
mixer->WriteFramesForTest();
} }
TEST_F(StreamMixerTest, OneStreamMixesProperly) { TEST_F(StreamMixerTest, OneStreamMixesProperly) {
MockMixerSource input(kTestSamplesPerSecond); auto* input = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
input->SetPaused(false);
EXPECT_CALL(input, InitializeAudioPlayback(_, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); EXPECT_CALL(*input, Initialize(_)).Times(1);
EXPECT_CALL(*mock_output_, Stop()).Times(0); mixer->SetFilterFrameAlignmentForTest(4);
mixer_->AddInput(&input); mixer->AddInput(base::WrapUnique(input));
mock_output_->ClearData(); EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
// Populate the stream with data. // Populate the stream with data.
const int kNumFrames = 32; const int kNumFrames = 32;
input.SetData(GetTestData(0)); input->SetData(GetTestData(0));
EXPECT_CALL(input, FillAudioPlaybackFrames(_, _, _)).Times(1); ASSERT_EQ(mock_alsa()->data().size(), 0u);
PlaybackOnce();
// Get the actual mixed output, and compare it against the expected stream. // Write the stream to ALSA.
// The stream should match exactly. EXPECT_CALL(*input, GetResampledData(_, kNumFrames));
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames); EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kNumFrames, _))
ASSERT_GE(mock_output_->data().size(), static_cast<size_t>(kNumFrames)); .Times(kNumChannels);
ToPlanar(mock_output_->data().data(), kNumFrames, actual.get()); EXPECT_CALL(*input, AfterWriteFrames(_));
auto expected = GetMixedAudioData(&input); mixer->WriteFramesForTest();
CompareAudioData(*expected, *actual);
mixer_.reset(); // Get the actual stream rendered to ALSA, and compare it against the
// expected stream. The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ASSERT_GT(mock_alsa()->data().size(), 0u);
ToPlanar(&(mock_alsa()->data()[0]), kNumFrames, actual.get());
CompareAudioData(input->data(), *actual);
} }
TEST_F(StreamMixerTest, OneStreamIsScaledDownProperly) { TEST_F(StreamMixerTest, OneStreamIsScaledDownProperly) {
MockMixerSource input(kTestSamplesPerSecond); auto* input = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
input->SetPaused(false);
EXPECT_CALL(input, InitializeAudioPlayback(_, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); EXPECT_CALL(*input, Initialize(_)).Times(1);
EXPECT_CALL(*mock_output_, Stop()).Times(0); mixer->SetFilterFrameAlignmentForTest(4);
mixer_->AddInput(&input); mixer->AddInput(base::WrapUnique(input));
mock_output_->ClearData(); EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
// Populate the stream with data. // Populate the stream with data.
const int kNumFrames = 32; const int kNumFrames = 32;
ASSERT_EQ(sizeof(kTestData[0]), kNumChannels * kNumFrames * kBytesPerSample); ASSERT_EQ(sizeof(kTestData[0]), kNumChannels * kNumFrames * kBytesPerSample);
input.SetData(GetTestData(0)); auto data = GetTestData(0);
input->SetData(std::move(data));
// Set the volume multiplier. // Set a volume multiplier on the stream.
input.set_multiplier(0.75f); input->SetVolumeMultiplier(0.75);
mixer_->SetVolumeMultiplier(&input, input.multiplier());
EXPECT_CALL(input, FillAudioPlaybackFrames(_, _, _)).Times(1); // Write the stream to ALSA.
PlaybackOnce(); EXPECT_CALL(*input, GetResampledData(_, kNumFrames));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kNumFrames, _))
.Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
mixer->WriteFramesForTest();
// Get the actual mixed output, and compare it against the expected stream. // Check that the retrieved stream is scaled correctly.
// The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames); auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ASSERT_GE(mock_output_->data().size(), static_cast<size_t>(kNumFrames)); ToPlanar(&(mock_alsa()->data()[0]), kNumFrames, actual.get());
ToPlanar(mock_output_->data().data(), kNumFrames, actual.get()); auto expected = GetMixedAudioData(input);
auto expected = GetMixedAudioData(&input);
CompareAudioData(*expected, *actual); CompareAudioData(*expected, *actual);
mixer_.reset();
} }
TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperly) { TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperly) {
// Create a group of input streams. // Create a group of input streams.
std::vector<std::unique_ptr<MockMixerSource>> inputs; std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 2; const int kNumInputs = 2;
for (int i = 0; i < kNumInputs; ++i) { for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back(std::make_unique<MockMixerSource>(kTestSamplesPerSecond)); inputs.push_back(
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond));
inputs.back()->SetPaused(false);
} }
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Stop()).Times(0); mixer->SetFilterFrameAlignmentForTest(4);
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], InitializeAudioPlayback(_, _)).Times(1); EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer_->AddInput(inputs[i].get()); mixer->AddInput(base::WrapUnique(inputs[i]));
} }
mock_output_->ClearData();
// Populate the streams with data. // Poll the inputs for data.
const int kNumFrames = 32; const int kNumFrames = 32;
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i]->SetData(GetTestData(i)); inputs[i]->SetData(GetTestData(i));
EXPECT_CALL(*inputs[i], FillAudioPlaybackFrames(_, _, _)).Times(1); EXPECT_CALL(*inputs[i], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[i], VolumeScaleAccumulate(_, _, kNumFrames, _))
.Times(kNumChannels);
EXPECT_CALL(*inputs[i], AfterWriteFrames(_));
} }
PlaybackOnce(); EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
mixer->WriteFramesForTest();
// Get the actual mixed output, and compare it against the expected stream. // Mix the inputs manually.
// The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ASSERT_GE(mock_output_->data().size(), static_cast<size_t>(kNumFrames));
ToPlanar(mock_output_->data().data(), kNumFrames, actual.get());
auto expected = GetMixedAudioData(inputs); auto expected = GetMixedAudioData(inputs);
CompareAudioData(*expected, *actual);
mixer_.reset(); // Get the actual stream rendered to ALSA, and compare it against the
// expected stream. The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ToPlanar(&(mock_alsa()->data()[0]), kNumFrames, actual.get());
CompareAudioData(*expected, *actual);
} }
TEST_F(StreamMixerTest, TwoUnscaledStreamsWithDifferentIdsMixProperly) { TEST_F(StreamMixerTest, TwoUnscaledStreamsWithDifferentIdsMixProperly) {
// Create a group of input streams. // Create a group of input streams.
std::vector<std::unique_ptr<MockMixerSource>> inputs; std::vector<testing::StrictMock<MockInputQueue>*> inputs;
inputs.push_back(std::make_unique<MockMixerSource>( inputs.push_back(new testing::StrictMock<MockInputQueue>(
kTestSamplesPerSecond, kTestSamplesPerSecond,
::media::AudioDeviceDescription::kDefaultDeviceId)); ::media::AudioDeviceDescription::kDefaultDeviceId));
inputs.push_back(std::make_unique<MockMixerSource>( inputs.back()->SetPaused(false);
inputs.push_back(new testing::StrictMock<MockInputQueue>(
kTestSamplesPerSecond, kTestSamplesPerSecond,
::media::AudioDeviceDescription::kCommunicationsDeviceId)); ::media::AudioDeviceDescription::kCommunicationsDeviceId));
inputs.back()->SetPaused(false);
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Stop()).Times(0); mixer->SetFilterFrameAlignmentForTest(4);
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], InitializeAudioPlayback(_, _)).Times(1); EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer_->AddInput(inputs[i].get()); mixer->AddInput(base::WrapUnique(inputs[i]));
} }
mock_output_->ClearData();
// Populate the streams with data. // Poll the inputs for data.
const int kNumFrames = 32; const int kNumFrames = 32;
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i]->SetData(GetTestData(i)); inputs[i]->SetData(GetTestData(i));
EXPECT_CALL(*inputs[i], FillAudioPlaybackFrames(_, _, _)).Times(1); EXPECT_CALL(*inputs[i], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[i], VolumeScaleAccumulate(_, _, kNumFrames, _))
.Times(kNumChannels);
EXPECT_CALL(*inputs[i], AfterWriteFrames(_));
} }
PlaybackOnce(); EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
mixer->WriteFramesForTest();
// Get the actual mixed output, and compare it against the expected stream. // Mix the inputs manually.
// The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ASSERT_GE(mock_output_->data().size(), static_cast<size_t>(kNumFrames));
ToPlanar(mock_output_->data().data(), kNumFrames, actual.get());
auto expected = GetMixedAudioData(inputs); auto expected = GetMixedAudioData(inputs);
CompareAudioData(*expected, *actual);
mixer_.reset(); // Get the actual stream rendered to ALSA, and compare it against the
// expected stream. The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ToPlanar(&(mock_alsa()->data()[0]), kNumFrames, actual.get());
CompareAudioData(*expected, *actual);
} }
TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperlyWithEdgeCases) { TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperlyWithEdgeCases) {
// Create a group of input streams. // Create a group of input streams.
std::vector<std::unique_ptr<MockMixerSource>> inputs; std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 2; const int kNumInputs = 2;
for (int i = 0; i < kNumInputs; ++i) { for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back(std::make_unique<MockMixerSource>(kTestSamplesPerSecond)); inputs.push_back(
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond));
inputs.back()->SetPaused(false);
} }
EXPECT_CALL(*mock_output_, Start(kTestSamplesPerSecond, _)).Times(1); StreamMixer* mixer = StreamMixer::Get();
EXPECT_CALL(*mock_output_, Stop()).Times(0); mixer->SetFilterFrameAlignmentForTest(4);
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], InitializeAudioPlayback(_, _)).Times(1); EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer_->AddInput(inputs[i].get()); mixer->AddInput(base::WrapUnique(inputs[i]));
} }
mock_output_->ClearData();
// Create edge case data for the inputs. By mixing these two short streams, // Create edge case data for the inputs. By mixing these two short streams,
// every combination of {-(2^31), 0, 2^31-1} is tested. This test case is // every combination of {-(2^31), 0, 2^31-1} is tested. This test case is
...@@ -729,29 +810,174 @@ TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperlyWithEdgeCases) { ...@@ -729,29 +810,174 @@ TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperlyWithEdgeCases) {
kMinSample, kMinSample, 0.0, 0.0, kMaxSample, kMaxSample, 0.0, 0.0, kMinSample, kMinSample, 0.0, 0.0, kMaxSample, kMaxSample, 0.0, 0.0,
}; };
// Populate the streams with data.
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
auto test_data = ::media::AudioBus::Create(kNumChannels, kNumFrames); auto test_data = ::media::AudioBus::Create(kNumChannels, kNumFrames);
test_data->FromInterleaved(kEdgeData[i], kNumFrames, kBytesPerSample); test_data->FromInterleaved(kEdgeData[i], kNumFrames, kBytesPerSample);
inputs[i]->SetData(std::move(test_data)); inputs[i]->SetData(std::move(test_data));
EXPECT_CALL(*inputs[i], FillAudioPlaybackFrames(_, _, _)).Times(1); EXPECT_CALL(*inputs[i], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[i], VolumeScaleAccumulate(_, _, kNumFrames, _))
.Times(kNumChannels);
EXPECT_CALL(*inputs[i], AfterWriteFrames(_));
} }
PlaybackOnce(); EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
mixer->WriteFramesForTest();
// Get the actual mixed output, and compare it against the expected stream.
// The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ASSERT_GE(mock_output_->data().size(), static_cast<size_t>(kNumFrames));
ToPlanar(mock_output_->data().data(), kNumFrames, actual.get());
// Use the hand-calculated results above. // Use the hand-calculated results above.
auto expected = ::media::AudioBus::Create(kNumChannels, kNumFrames); auto expected = ::media::AudioBus::Create(kNumChannels, kNumFrames);
expected->FromInterleaved(kResult, kNumFrames, kBytesPerSample); expected->FromInterleaved(kResult, kNumFrames, kBytesPerSample);
// Get the actual stream rendered to ALSA, and compare it against the
// expected stream. The stream should match exactly.
auto actual = ::media::AudioBus::Create(kNumChannels, kNumFrames);
ToPlanar(&(mock_alsa()->data()[0]), kNumFrames, actual.get());
CompareAudioData(*expected, *actual); CompareAudioData(*expected, *actual);
}
TEST_F(StreamMixerTest, WriteBuffersOfVaryingLength) {
auto* input = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
input->SetPaused(false);
StreamMixer* mixer = StreamMixer::Get();
mixer->SetFilterFrameAlignmentForTest(4);
EXPECT_CALL(*input, Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(input));
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
// The input stream will provide buffers of several different lengths.
input->SetMaxReadSize(8);
EXPECT_CALL(*input, GetResampledData(_, 8));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, 8, _)).Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, 8)).Times(1);
mixer->WriteFramesForTest();
input->SetMaxReadSize(100);
EXPECT_CALL(*input, GetResampledData(_, 100));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, 100, _)).Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, 100)).Times(1);
mixer->WriteFramesForTest();
input->SetMaxReadSize(32);
EXPECT_CALL(*input, GetResampledData(_, 32));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, 32, _)).Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, 32)).Times(1);
mixer->WriteFramesForTest();
input->SetMaxReadSize(kMaxChunkSize + 1);
EXPECT_CALL(*input, GetResampledData(_, kMaxChunkSize));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kMaxChunkSize, _))
.Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kMaxChunkSize)).Times(1);
mixer->WriteFramesForTest();
}
TEST_F(StreamMixerTest, StuckStreamWithoutUnderrun) {
// Create a group of input streams.
std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 2;
for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back(
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond));
inputs.back()->SetMaxReadSize(0);
inputs.back()->SetPaused(false);
}
StreamMixer* mixer = StreamMixer::Get();
for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(inputs[i]));
}
// Poll the inputs for data. Should not pull any data since one input has none
// to give.
inputs[0]->SetData(GetTestData(0));
EXPECT_CALL(*inputs[0], GetResampledData(_, _)).Times(0);
EXPECT_CALL(*inputs[0], VolumeScaleAccumulate(_, _, _, _)).Times(0);
EXPECT_CALL(*inputs[0], AfterWriteFrames(_)).Times(0);
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, _)).Times(0);
mixer->WriteFramesForTest();
}
TEST_F(StreamMixerTest, StuckStreamWithUnderrun) {
// Create a group of input streams.
std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 2;
for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back(
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond));
inputs.back()->SetMaxReadSize(0);
inputs.back()->SetPaused(false);
}
StreamMixer* mixer = StreamMixer::Get();
mixer->SetFilterFrameAlignmentForTest(4);
for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(inputs[i]));
}
mock_alsa()->set_state(SND_PCM_STATE_XRUN);
mixer_.reset(); // Poll the inputs for data. The first input will provide data (since the
// output is in an underrun condition); the second input can't provide any
// data, but AfterWriteFrames() will still be called on it so that it has the
// correct rendering delay.
const int kNumFrames = 32;
inputs[0]->SetData(GetTestData(0));
EXPECT_CALL(*inputs[0], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[0], VolumeScaleAccumulate(_, _, kNumFrames, _))
.Times(kNumChannels);
EXPECT_CALL(*inputs[0], AfterWriteFrames(_));
EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0);
EXPECT_CALL(*inputs[1], VolumeScaleAccumulate(_, _, kNumFrames, _)).Times(0);
EXPECT_CALL(*inputs[1], OnSkipped());
EXPECT_CALL(*inputs[1], AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
mixer->WriteFramesForTest();
}
TEST_F(StreamMixerTest, StuckStreamWithLowBuffer) {
// Create a group of input streams.
std::vector<testing::StrictMock<MockInputQueue>*> inputs;
const int kNumInputs = 2;
for (int i = 0; i < kNumInputs; ++i) {
inputs.push_back(
new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond));
inputs.back()->SetMaxReadSize(0);
inputs.back()->SetPaused(false);
}
StreamMixer* mixer = StreamMixer::Get();
for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(inputs[i]));
}
mock_alsa()->set_avail(4086);
// Poll the inputs for data. The first input will provide data (since the
// output is in an low buffer condition); the second input can't provide any
// data, but AfterWriteFrames() will still be called on it so that it has the
// correct rendering delay.
const int kNumFrames = 32;
inputs[0]->SetData(GetTestData(0));
EXPECT_CALL(*inputs[0], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[0], VolumeScaleAccumulate(_, _, kNumFrames, _))
.Times(kNumChannels);
EXPECT_CALL(*inputs[0], AfterWriteFrames(_));
EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0);
EXPECT_CALL(*inputs[1], VolumeScaleAccumulate(_, _, _, _)).Times(0);
EXPECT_CALL(*inputs[1], OnSkipped());
EXPECT_CALL(*inputs[1], AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
mixer->WriteFramesForTest();
} }
#define EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(map, name, times, frames, \ #define EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(map, name, times, frames, \
...@@ -765,86 +991,162 @@ TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperlyWithEdgeCases) { ...@@ -765,86 +991,162 @@ TEST_F(StreamMixerTest, TwoUnscaledStreamsMixProperlyWithEdgeCases) {
TEST_F(StreamMixerTest, PostProcessorDelayListedDeviceId) { TEST_F(StreamMixerTest, PostProcessorDelayListedDeviceId) {
int common_delay = kMixProcessorDelay + kLinearizeProcessorDelay; int common_delay = kMixProcessorDelay + kLinearizeProcessorDelay;
std::vector<testing::StrictMock<MockInputQueue>*> inputs;
std::vector<std::unique_ptr<MockMixerSource>> inputs;
std::vector<int64_t> delays; std::vector<int64_t> delays;
inputs.push_back( inputs.push_back(new testing::StrictMock<MockInputQueue>(
std::make_unique<MockMixerSource>(kTestSamplesPerSecond, "default")); kTestSamplesPerSecond, "default"));
delays.push_back(common_delay + kDefaultProcessorDelay); delays.push_back(common_delay + kDefaultProcessorDelay);
inputs.push_back(std::make_unique<MockMixerSource>(kTestSamplesPerSecond, inputs.push_back(new testing::StrictMock<MockInputQueue>(
"communications")); kTestSamplesPerSecond, "communications"));
delays.push_back(common_delay); delays.push_back(common_delay);
inputs.push_back(std::make_unique<MockMixerSource>(kTestSamplesPerSecond, inputs.push_back(new testing::StrictMock<MockInputQueue>(
"assistant-tts")); kTestSamplesPerSecond, "assistant-tts"));
delays.push_back(common_delay + kTtsProcessorDelay); delays.push_back(common_delay + kTtsProcessorDelay);
// Convert delay from frames to microseconds. // Convert delay from frames to microseconds.
std::transform(delays.begin(), delays.end(), delays.begin(), std::transform(delays.begin(), delays.end(), delays.begin(),
&FramesToDelayUs); &FramesToDelayUs);
const int kNumFrames = 12;
for (auto* input : inputs) {
input->SetMaxReadSize(kNumFrames);
input->SetPaused(false);
}
StreamMixer* mixer = StreamMixer::Get();
mixer->SetFilterFrameAlignmentForTest(4);
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], InitializeAudioPlayback(_, _)).Times(1); EXPECT_CALL(*inputs[i], Initialize(_)).Times(1);
mixer_->AddInput(inputs[i].get()); mixer->AddInput(base::WrapUnique(inputs[i]));
} }
auto* post_processors = &pp_factory_->instances; mock_alsa()->set_avail(4086);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, _,
false); auto* post_processors = MockPostProcessor::instances();
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, _, false); EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1,
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, _, kNumFrames, false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, kNumFrames,
false); false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1,
kNumFrames, false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "communications", 1, EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "communications", 1,
_, false); kNumFrames, false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1, EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1,
_, false); kNumFrames, false);
// Poll the inputs for data. Each input will get a different // Poll the inputs for data. Each input will get a different
// rendering delay based on their device type. // rendering delay based on their device type.
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
EXPECT_CALL(*inputs[i], EXPECT_CALL(*inputs[i], GetResampledData(_, kNumFrames));
FillAudioPlaybackFrames( EXPECT_CALL(*inputs[i], VolumeScaleAccumulate(_, _, kNumFrames, _))
_, MatchDelay(delays[i], inputs[i]->device_id()), _)) .Times(kNumChannels);
.Times(1); EXPECT_CALL(*inputs[i], AfterWriteFrames(
MatchDelay(delays[i], inputs[i]->device_id())));
} }
mixer->WriteFramesForTest();
PlaybackOnce();
mixer_.reset();
} }
TEST_F(StreamMixerTest, PostProcessorDelayUnlistedDevice) { TEST_F(StreamMixerTest, PostProcessorDelayUnlistedDevice) {
const std::string device_id = "not-a-device-id"; const std::string device_id = "not-a-device-id";
MockMixerSource input(kTestSamplesPerSecond, device_id); testing::StrictMock<MockInputQueue>* input =
auto* post_processors = &pp_factory_->instances; new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond, device_id);
// Delay should be based on default processor
int64_t delay = FramesToDelayUs(
kDefaultProcessorDelay + kLinearizeProcessorDelay + kMixProcessorDelay);
const int kNumFrames = 12;
input->SetMaxReadSize(kNumFrames);
input->SetPaused(false);
auto* post_processors = MockPostProcessor::instances();
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1,
kNumFrames, false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, kNumFrames,
false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1,
kNumFrames, false);
// These will be called once to ensure their buffers are initialized. // These will be called once to ensure their buffers are initialized.
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, _, _);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, _, _);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, _,
_);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "communications", 1, EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "communications", 1,
_, _); _, _);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1, EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1,
_, _); _, _);
mixer_->AddInput(&input);
// Delay should be based on default processor. StreamMixer* mixer = StreamMixer::Get();
int64_t delay = FramesToDelayUs( EXPECT_CALL(*input, Initialize(_));
kDefaultProcessorDelay + kLinearizeProcessorDelay + kMixProcessorDelay); mixer->AddInput(base::WrapUnique(input));
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, _, EXPECT_CALL(*input, GetResampledData(_, kNumFrames));
false); EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kNumFrames, _))
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, _, false); .Times(kNumChannels);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, _, EXPECT_CALL(*input, AfterWriteFrames(MatchDelay(delay, device_id)));
false); mixer->SetFilterFrameAlignmentForTest(4);
mixer->WriteFramesForTest();
}
TEST_F(StreamMixerTest, OneStreamTruncatedProperlyFrameAlignment4) {
auto* input = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
input->SetPaused(false);
StreamMixer* mixer = StreamMixer::Get();
mixer->SetFilterFrameAlignmentForTest(4);
EXPECT_CALL(*input, Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(input));
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
// Precalculated output chunk sizes for given input chunk sizes assuming
// 4 frame alignment.
int in_out[6][2] = {{4, 4}, {7, 4}, {8, 8}, {9, 8}, {880, 880}, {882, 880}};
for (int i = 0; i < 6; ++i) {
int readSize = in_out[i][0];
int expectedSize = in_out[i][1];
input->SetMaxReadSize(readSize);
EXPECT_CALL(*input, GetResampledData(_, expectedSize));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, expectedSize, _))
.Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, expectedSize)).Times(1);
mixer->WriteFramesForTest();
}
}
TEST_F(StreamMixerTest, OneStreamTruncatedProperlyFrameAlignment256) {
auto* input = new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond);
input->SetPaused(false);
StreamMixer* mixer = StreamMixer::Get();
mixer->SetFilterFrameAlignmentForTest(256);
EXPECT_CALL(*input, Initialize(_)).Times(1);
mixer->AddInput(base::WrapUnique(input));
EXPECT_EQ(StreamMixer::kStateNormalPlayback, mixer->state());
// Precalculated output chunk sizes for given input chunk sizes assuming
// 256 frame alignment.
int in_out[4][2] = {{256, 256}, {511, 256}, {512, 512}, {513, 512}};
for (int i = 0; i < 4; ++i) {
int readSize = in_out[i][0];
int expectedSize = in_out[i][1];
input->SetMaxReadSize(readSize);
EXPECT_CALL(*input, GetResampledData(_, expectedSize));
EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, expectedSize, _))
.Times(kNumChannels);
EXPECT_CALL(*input, AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, expectedSize)).Times(1);
mixer->WriteFramesForTest();
}
EXPECT_CALL(input, // Do nothing if there isn't enough data.
FillAudioPlaybackFrames(_, MatchDelay(delay, device_id), _)) input->SetMaxReadSize(123); // 0 < 123 < filter frame alignment
.Times(1); EXPECT_CALL(*input, GetResampledData(_, _)).Times(0);
PlaybackOnce(); EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, _, _)).Times(0);
EXPECT_CALL(*input, AfterWriteFrames(_)).Times(0);
mixer_.reset(); EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, _)).Times(0);
mixer->WriteFramesForTest();
} }
TEST_F(StreamMixerTest, PostProcessorRingingWithoutInput) { TEST_F(StreamMixerTest, PostProcessorRingingWithoutInput) {
...@@ -868,41 +1170,48 @@ TEST_F(StreamMixerTest, PostProcessorRingingWithoutInput) { ...@@ -868,41 +1170,48 @@ TEST_F(StreamMixerTest, PostProcessorRingingWithoutInput) {
} }
)json"; )json";
MockMixerSource input(kTestSamplesPerSecond, "default"); const int kNumFrames = 32;
testing::NiceMock<MockInputQueue>* input =
new testing::NiceMock<MockInputQueue>(kTestSamplesPerSecond, "default");
input->SetMaxReadSize(kNumFrames);
input->SetPaused(false);
StreamMixer* mixer = StreamMixer::Get();
std::string test_pipeline_json = base::StringPrintf( std::string test_pipeline_json = base::StringPrintf(
kTestPipelineJson, kDelayModuleSolib, kDelayModuleSolib); kTestPipelineJson, kDelayModuleSolib, kDelayModuleSolib);
auto factory = std::make_unique<MockPostProcessorFactory>(); mixer->SetFilterFrameAlignmentForTest(4);
MockPostProcessorFactory* factory_ptr = factory.get(); mixer->ResetPostProcessorsForTest(
mixer_->ResetPostProcessorsForTest(std::move(factory), test_pipeline_json); std::make_unique<MockPostProcessorFactory>(), test_pipeline_json);
mixer_->AddInput(&input); mixer->AddInput(base::WrapUnique(input));
// "mix" + "linearize" should be automatic // "mix" + "linearize" should be automatic
CHECK_EQ(factory_ptr->instances.size(), 4u); CHECK_EQ(MockPostProcessor::instances()->size(), 4u);
auto* post_processors = &factory_ptr->instances; mock_alsa()->set_avail(4086);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, _,
false); auto* post_processors = MockPostProcessor::instances();
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, _, false); EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1,
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, _, kNumFrames, false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, kNumFrames,
false); false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1,
kNumFrames, false);
EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1, EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1,
_, true); kNumFrames, true);
PlaybackOnce(); mixer->WriteFramesForTest();
mixer_.reset();
} }
TEST_F(StreamMixerTest, PostProcessorProvidesDefaultPipeline) { TEST_F(StreamMixerTest, PostProcessorProvidesDefaultPipeline) {
auto factory = std::make_unique<MockPostProcessorFactory>(); StreamMixer* mixer = StreamMixer::Get();
MockPostProcessorFactory* factory_ptr = factory.get(); mixer->ResetPostProcessorsForTest(
mixer_->ResetPostProcessorsForTest(std::move(factory), "{}"); std::make_unique<MockPostProcessorFactory>(), "{}");
auto* instances = &factory_ptr->instances; auto* instances = MockPostProcessor::instances();
CHECK(instances->find("default") == instances->end()); CHECK(instances->find("default") == instances->end());
CHECK(instances->find("mix") != instances->end()); CHECK(instances->find("mix") != instances->end());
CHECK(instances->find("linearize") != instances->end()); CHECK(instances->find("linearize") != instances->end());
CHECK_EQ(instances->size(), 2u); CHECK_EQ(MockPostProcessor::instances()->size(), 2u);
} }
TEST_F(StreamMixerTest, MultiplePostProcessorsInOneStream) { TEST_F(StreamMixerTest, MultiplePostProcessorsInOneStream) {
...@@ -942,138 +1251,138 @@ TEST_F(StreamMixerTest, MultiplePostProcessorsInOneStream) { ...@@ -942,138 +1251,138 @@ TEST_F(StreamMixerTest, MultiplePostProcessorsInOneStream) {
kDelayModuleSolib // intentionally omitted processor name kDelayModuleSolib // intentionally omitted processor name
); );
auto factory = std::make_unique<MockPostProcessorFactory>(); StreamMixer* mixer = StreamMixer::Get();
MockPostProcessorFactory* factory_ptr = factory.get(); mixer->ResetPostProcessorsForTest(
mixer_->ResetPostProcessorsForTest(std::move(factory), json); std::make_unique<MockPostProcessorFactory>(), json);
// "mix" + "linearize" + "default" // "mix" + "linearize" + "default"
EXPECT_EQ(factory_ptr->instances.size(), 3u); CHECK_EQ(MockPostProcessor::instances()->size(), 3u);
auto* post_processors = &factory_ptr->instances; auto* post_processors = MockPostProcessor::instances();
EXPECT_EQ(post_processors->find("default")->second->delay(), 110); CHECK_EQ(post_processors->find("default")->second->delay(), 110);
EXPECT_EQ(post_processors->find("mix")->second->delay(), 11000); CHECK_EQ(post_processors->find("mix")->second->delay(), 11000);
EXPECT_EQ(post_processors->find("linearize")->second->delay(), 0); CHECK_EQ(post_processors->find("linearize")->second->delay(), 0);
mixer->WriteFramesForTest();
} }
TEST_F(StreamMixerTest, PicksPlayoutChannel) { TEST_F(StreamMixerTest, PicksPlayoutChannel) {
auto factory = std::make_unique<MockPostProcessorFactory>(); StreamMixer* mixer = StreamMixer::Get();
MockPostProcessorFactory* factory_ptr = factory.get(); mixer->ResetPostProcessorsForTest(
mixer_->ResetPostProcessorsForTest(std::move(factory), "{}"); std::make_unique<MockPostProcessorFactory>(), "{}");
MockMixerSource input1(kTestSamplesPerSecond); EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(kChannelAll));
MockMixerSource input2(kTestSamplesPerSecond);
MockMixerSource input3(kTestSamplesPerSecond); // Add an input to initialize the post processors.
MockMixerSource input4(kTestSamplesPerSecond); // Not necessary, but realistic.
input1.set_playout_channel(kChannelAll); testing::NiceMock<MockInputQueue>* input =
input2.set_playout_channel(0); new testing::NiceMock<MockInputQueue>(kTestSamplesPerSecond);
input3.set_playout_channel(1); mixer->AddInput(base::WrapUnique(input));
input4.set_playout_channel(1); VerifyAndClearPostProcessors();
// Requests: all = 0 ch0 = 0 ch1 = 1. // Requests: all = 0 ch0 = 0 ch1 = 1.
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, UpdatePlayoutChannel(1)); EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(1));
mixer_->AddInput(&input3); mixer->AddPlayoutChannelRequest(1);
VerifyAndClearPostProcessors(factory_ptr); VerifyAndClearPostProcessors();
// Requests: all = 0 ch0 = 0 ch1 = 2. // Requests: all = 0 ch0 = 0 ch1 = 2.
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, UpdatePlayoutChannel(1)); EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(1));
mixer_->AddInput(&input4); mixer->AddPlayoutChannelRequest(1);
VerifyAndClearPostProcessors(factory_ptr); VerifyAndClearPostProcessors();
// Requests: all = 1 ch0 = 0 ch1 = 2. // Requests: all = 1 ch0 = 0 ch1 = 2.
// Prioritizes all. // Prioritizes all.
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(kChannelAll));
UpdatePlayoutChannel(kChannelAll)); mixer->AddPlayoutChannelRequest(kChannelAll);
mixer_->AddInput(&input1); VerifyAndClearPostProcessors();
VerifyAndClearPostProcessors(factory_ptr);
// Requests: all = 1 ch0 = 0 ch1 = 1. // Requests: all = 1 ch0 = 0 ch1 = 1.
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(kChannelAll));
UpdatePlayoutChannel(kChannelAll)); mixer->RemovePlayoutChannelRequest(1);
mixer_->RemoveInput(&input3); VerifyAndClearPostProcessors();
WaitForRemoval();
VerifyAndClearPostProcessors(factory_ptr);
// Requests: all = 0 ch0 = 0 ch1 = 1. // Requests: all = 0 ch0 = 0 ch1 = 1.
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, UpdatePlayoutChannel(1)); EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(1));
mixer_->RemoveInput(&input1); mixer->RemovePlayoutChannelRequest(kChannelAll);
WaitForRemoval(); VerifyAndClearPostProcessors();
VerifyAndClearPostProcessors(factory_ptr);
// Requests: all = 0 ch0 = 0 ch1 = 0. // Requests: all = 0 ch0 = 0 ch1 = 0.
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(kChannelAll));
UpdatePlayoutChannel(kChannelAll)); mixer->RemovePlayoutChannelRequest(1);
mixer_->RemoveInput(&input4); VerifyAndClearPostProcessors();
WaitForRemoval();
VerifyAndClearPostProcessors(factory_ptr);
// Requests: all = 0 ch0 = 1 ch1 = 0 // Requests: all = 0 ch0 = 1 ch1 = 0
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, UpdatePlayoutChannel(0)); EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(0));
mixer_->AddInput(&input2); mixer->AddPlayoutChannelRequest(0);
VerifyAndClearPostProcessors(factory_ptr); VerifyAndClearPostProcessors();
// Requests: all = 1 ch0 = 1 ch1 = 0 // Requests: all = 1 ch0 = 1 ch1 = 0
EXPECT_CALL_ALL_POSTPROCESSORS(factory_ptr, EXPECT_CALL_ALL_POSTPROCESSORS(UpdatePlayoutChannel(kChannelAll));
UpdatePlayoutChannel(kChannelAll)); mixer->AddPlayoutChannelRequest(kChannelAll);
mixer_->AddInput(&input1); VerifyAndClearPostProcessors();
VerifyAndClearPostProcessors(factory_ptr);
mixer_->RemoveInput(&input1);
mixer_->RemoveInput(&input2);
WaitForRemoval();
} }
TEST_F(StreamMixerTest, SetPostProcessorConfig) { TEST_F(StreamMixerTest, SetPostProcessorConfig) {
std::string name = "ThisIsMyName"; std::string name = "ThisIsMyName";
std::string config = "ThisIsMyConfig"; std::string config = "ThisIsMyConfig";
StreamMixer* mixer = StreamMixer::Get();
auto* post_processors = MockPostProcessor::instances();
for (auto const& x : pp_factory_->instances) { for (auto const& x : *post_processors) {
EXPECT_CALL(*(x.second), SetPostProcessorConfig(name, config)); EXPECT_CALL(*(x.second), SetPostProcessorConfig(name, config));
} }
mixer_->SetPostProcessorConfig(name, config); mixer->SetPostProcessorConfig(name, config);
} }
TEST_F(StreamMixerTest, ObserverGets2ChannelsByDefault) { TEST_F(StreamMixerTest, ObserverGets2ChannelsByDefault) {
MockMixerSource input(kTestSamplesPerSecond); StreamMixer* mixer = StreamMixer::Get();
const int kNumFrames = 256;
const int kNumChannels = 2;
testing::NiceMock<MockInputQueue>* input =
new testing::NiceMock<MockInputQueue>(kTestSamplesPerSecond);
testing::StrictMock<MockLoopbackAudioObserver> observer; testing::StrictMock<MockLoopbackAudioObserver> observer;
mixer_->AddInput(&input); mixer->AddInput(base::WrapUnique(input));
mixer_->AddLoopbackAudioObserver(&observer);
EXPECT_CALL(observer, input->SetPaused(false);
OnLoopbackAudio(_, kSampleFormatF32, kTestSamplesPerSecond, input->SetMaxReadSize(kNumFrames);
kNumChannels, _, _)); mixer->AddLoopbackAudioObserver(&observer);
PlaybackOnce(); EXPECT_CALL(
observer,
OnLoopbackAudio(_, kSampleFormatF32, kTestSamplesPerSecond, kNumChannels,
_, kNumChannels * kNumFrames * sizeof(float)));
EXPECT_CALL(observer, OnRemoved()); mixer->WriteFramesForTest();
mixer_->RemoveLoopbackAudioObserver(&observer);
mixer_.reset(); EXPECT_CALL(observer, OnRemoved());
mixer->RemoveLoopbackAudioObserver(&observer);
} }
TEST_F(StreamMixerTest, ObserverGets1ChannelIfNumOutputChannelsIs1) { TEST_F(StreamMixerTest, ObserverGets1ChannelIfNumOutputChannelsIs1) {
StreamMixer* mixer = StreamMixer::Get();
const int kNumFrames = 256;
const int kNumOutputChannels = 1; const int kNumOutputChannels = 1;
mixer_->SetNumOutputChannelsForTest(kNumOutputChannels); mixer->SetNumOutputChannelsForTest(kNumOutputChannels);
testing::NiceMock<MockInputQueue>* input =
MockMixerSource input(kTestSamplesPerSecond); new testing::NiceMock<MockInputQueue>(kTestSamplesPerSecond);
testing::StrictMock<MockLoopbackAudioObserver> observer; testing::StrictMock<MockLoopbackAudioObserver> observer;
mixer_->AddInput(&input); mixer->AddInput(base::WrapUnique(input));
mixer_->AddLoopbackAudioObserver(&observer);
input->SetPaused(false);
input->SetMaxReadSize(kNumFrames);
mixer->AddLoopbackAudioObserver(&observer);
EXPECT_CALL(observer, EXPECT_CALL(observer,
OnLoopbackAudio(_, kSampleFormatF32, kTestSamplesPerSecond, OnLoopbackAudio(_, kSampleFormatF32, kTestSamplesPerSecond,
kNumOutputChannels, _, _)); kNumOutputChannels, _,
kNumOutputChannels * kNumFrames * sizeof(float)));
PlaybackOnce(); mixer->WriteFramesForTest();
EXPECT_CALL(observer, OnRemoved()); EXPECT_CALL(observer, OnRemoved());
mixer_->RemoveLoopbackAudioObserver(&observer); mixer->RemoveLoopbackAudioObserver(&observer);
mixer_.reset();
} }
#if GTEST_HAS_DEATH_TEST
using StreamMixerDeathTest = StreamMixerTest; using StreamMixerDeathTest = StreamMixerTest;
TEST_F(StreamMixerDeathTest, InvalidStreamTypeCrashes) { TEST_F(StreamMixerDeathTest, InvalidStreamTypeCrashes) {
...@@ -1091,21 +1400,21 @@ TEST_F(StreamMixerDeathTest, InvalidStreamTypeCrashes) { ...@@ -1091,21 +1400,21 @@ TEST_F(StreamMixerDeathTest, InvalidStreamTypeCrashes) {
} }
)json"; )json";
::testing::FLAGS_gtest_death_test_style = "threadsafe"; EXPECT_DEATH(StreamMixer::Get()->ResetPostProcessorsForTest(
EXPECT_DEATH(mixer_->ResetPostProcessorsForTest(
std::make_unique<MockPostProcessorFactory>(), json), std::make_unique<MockPostProcessorFactory>(), json),
DeathRegex("foobar is not a stream type")); DeathRegex("foobar is not a stream type"));
} }
TEST_F(StreamMixerDeathTest, BadJsonCrashes) { TEST_F(StreamMixerDeathTest, BadJsonCrashes) {
const std::string json("{{"); const std::string json("{{");
::testing::FLAGS_gtest_death_test_style = "threadsafe"; EXPECT_DEATH(StreamMixer::Get()->ResetPostProcessorsForTest(
EXPECT_DEATH(mixer_->ResetPostProcessorsForTest(
std::make_unique<MockPostProcessorFactory>(), json), std::make_unique<MockPostProcessorFactory>(), json),
DeathRegex("Invalid JSON")); DeathRegex("Invalid JSON"));
} }
TEST_F(StreamMixerDeathTest, CrashesIfChannelCountDoesNotMatchFlags) { TEST_F(StreamMixerDeathTest, CrashesIfChannelCountDoesNotMatchFlags) {
StreamMixer* mixer = StreamMixer::Get();
const int kNumOutputChannels = 2;
const std::string config = R"Json({ const std::string config = R"Json({
"postprocessors": { "postprocessors": {
"linearize": { "linearize": {
...@@ -1117,14 +1426,16 @@ TEST_F(StreamMixerDeathTest, CrashesIfChannelCountDoesNotMatchFlags) { ...@@ -1117,14 +1426,16 @@ TEST_F(StreamMixerDeathTest, CrashesIfChannelCountDoesNotMatchFlags) {
} }
}})Json"; }})Json";
::testing::FLAGS_gtest_death_test_style = "threadsafe"; mixer->SetNumOutputChannelsForTest(kNumOutputChannels);
ASSERT_DEATH(mixer_->ResetPostProcessorsForTest( ASSERT_DEATH(mixer->ResetPostProcessorsForTest(
std::make_unique<MockPostProcessorFactory>(), config), std::make_unique<MockPostProcessorFactory>(), config),
DeathRegex("PostProcessor configuration channel count does not " DeathRegex("PostProcessor configuration channel count does not "
"match command line flag")); "match command line flag"));
} }
TEST_F(StreamMixerDeathTest, CrashesIfMoreThan2LoopbackChannels) { TEST_F(StreamMixerDeathTest, CrashesIfMoreThan2LoopbackChannels) {
StreamMixer* mixer = StreamMixer::Get();
const int kNumOutputChannels = 2;
const std::string config = R"Json({ const std::string config = R"Json({
"postprocessors": { "postprocessors": {
"output_streams": [{ "output_streams": [{
...@@ -1144,13 +1455,11 @@ TEST_F(StreamMixerDeathTest, CrashesIfMoreThan2LoopbackChannels) { ...@@ -1144,13 +1455,11 @@ TEST_F(StreamMixerDeathTest, CrashesIfMoreThan2LoopbackChannels) {
} }
}})Json"; }})Json";
::testing::FLAGS_gtest_death_test_style = "threadsafe"; mixer->SetNumOutputChannelsForTest(kNumOutputChannels);
ASSERT_DEATH(mixer_->ResetPostProcessorsForTest( ASSERT_DEATH(mixer->ResetPostProcessorsForTest(
std::make_unique<MockPostProcessorFactory>(), config), std::make_unique<MockPostProcessorFactory>(), config),
DeathRegex("loopback_channel_count <= 2")); DeathRegex("loopback_channel_count <= 2"));
} }
#endif // GTEST_HAS_DEATH_TEST
} // namespace media } // namespace media
} // namespace chromecast } // namespace chromecast
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment