OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" | 5 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <limits> | 9 #include <limits> |
| 10 #include <unordered_map> |
10 #include <utility> | 11 #include <utility> |
11 | 12 |
12 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
13 #include "base/memory/scoped_vector.h" | 14 #include "base/memory/scoped_vector.h" |
14 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
15 #include "base/run_loop.h" | 16 #include "base/run_loop.h" |
16 #include "base/threading/thread_task_runner_handle.h" | 17 #include "base/threading/thread_task_runner_handle.h" |
| 18 #include "base/values.h" |
17 #include "chromecast/media/cma/backend/alsa/mock_alsa_wrapper.h" | 19 #include "chromecast/media/cma/backend/alsa/mock_alsa_wrapper.h" |
| 20 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline.h" |
18 #include "media/audio/audio_device_description.h" | 21 #include "media/audio/audio_device_description.h" |
19 #include "media/base/audio_bus.h" | 22 #include "media/base/audio_bus.h" |
20 #include "media/base/vector_math.h" | 23 #include "media/base/vector_math.h" |
21 #include "testing/gmock/include/gmock/gmock.h" | 24 #include "testing/gmock/include/gmock/gmock.h" |
22 #include "testing/gtest/include/gtest/gtest.h" | 25 #include "testing/gtest/include/gtest/gtest.h" |
23 | 26 |
24 using testing::_; | 27 using testing::_; |
25 | 28 |
26 namespace chromecast { | 29 namespace chromecast { |
27 namespace media { | 30 namespace media { |
28 | 31 |
29 namespace { | 32 namespace { |
30 | 33 |
31 // Testing constants that are common to multiple test cases. | 34 // Testing constants that are common to multiple test cases. |
32 const size_t kBytesPerSample = sizeof(int32_t); | 35 const size_t kBytesPerSample = sizeof(int32_t); |
33 const int kNumChannels = 2; | 36 const int kNumChannels = 2; |
34 const int kTestMaxReadSize = 4096; | 37 const int kTestMaxReadSize = 4096; |
35 | 38 |
36 // kTestSamplesPerSecond needs to be higher than kLowSampleRateCutoff for the | 39 // kTestSamplesPerSecond needs to be higher than kLowSampleRateCutoff for the |
37 // mixer to use it. | 40 // mixer to use it. |
38 const int kTestSamplesPerSecond = 48000; | 41 const int kTestSamplesPerSecond = 48000; |
39 | 42 |
40 // Stream mixer alsa will never pull more than this many frames at a time. | 43 // Stream mixer alsa will never pull more than this many frames at a time. |
41 const int kMaxWriteSizeMs = 20; | 44 const int kMaxWriteSizeMs = 20; |
42 const int kMaxChunkSize = kTestSamplesPerSecond * kMaxWriteSizeMs / 1000; | 45 const int kMaxChunkSize = kTestSamplesPerSecond * kMaxWriteSizeMs / 1000; |
43 | 46 |
| 47 // The number of frames of silence to write (to prevent underrun) when no inputs |
| 48 // are present. |
| 49 const int kPreventUnderrunChunkSize = 512; |
| 50 |
44 // This array holds |NUM_DATA_SETS| sets of arbitrary interleaved float data. | 51 // This array holds |NUM_DATA_SETS| sets of arbitrary interleaved float data. |
45 // Each set holds |NUM_SAMPLES| / kNumChannels frames of data. | 52 // Each set holds |NUM_SAMPLES| / kNumChannels frames of data. |
46 #define NUM_DATA_SETS 2u | 53 #define NUM_DATA_SETS 2u |
47 #define NUM_SAMPLES 64u | 54 #define NUM_SAMPLES 64u |
48 | 55 |
49 // Note: Test data should be represented as 32-bit integers and copied into | 56 // Note: Test data should be represented as 32-bit integers and copied into |
50 // ::media::AudioBus instances, rather than wrapping statically declared float | 57 // ::media::AudioBus instances, rather than wrapping statically declared float |
51 // arrays. The latter method is brittle, as ::media::AudioBus requires 16-bit | 58 // arrays. The latter method is brittle, as ::media::AudioBus requires 16-bit |
52 // alignment for internal data. | 59 // alignment for internal data. |
53 const int32_t kTestData[NUM_DATA_SETS][NUM_SAMPLES] = { | 60 const int32_t kTestData[NUM_DATA_SETS][NUM_SAMPLES] = { |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
113 1340850135, -1616803932, | 120 1340850135, -1616803932, |
114 -850779335, 1666492408, | 121 -850779335, 1666492408, |
115 1290349909, -492418001, | 122 1290349909, -492418001, |
116 659200170, -542374913, | 123 659200170, -542374913, |
117 -120005682, 1030923147, | 124 -120005682, 1030923147, |
118 -877887021, -870241979, | 125 -877887021, -870241979, |
119 1322678128, -344799975, | 126 1322678128, -344799975, |
120 } | 127 } |
121 }; | 128 }; |
122 | 129 |
| 130 // Compensate for integer arithmatic errors. |
| 131 const int kMaxDelayErrorUs = 2; |
| 132 |
| 133 const char kDelayModuleSolib[] = "delay.so"; |
| 134 |
| 135 // Should match # of "processors" blocks below. |
| 136 const int kNumPostProcessors = 5; |
| 137 const char kTestPipelineJsonTemplate[] = R"json( |
| 138 { |
| 139 "output_streams": [{ |
| 140 "streams": [ "default" ], |
| 141 "processors": [{ |
| 142 "processor": "%s", |
| 143 "config": { "delay": %d } |
| 144 }] |
| 145 }, { |
| 146 "streams": [ "assistant-tts" ], |
| 147 "processors": [{ |
| 148 "processor": "%s", |
| 149 "config": { "delay": %d } |
| 150 }] |
| 151 }, { |
| 152 "streams": [ "communications" ], |
| 153 "processors": [] |
| 154 }], |
| 155 "mix": { |
| 156 "processors": [{ |
| 157 "processor": "%s", |
| 158 "config": { "delay": %d } |
| 159 }] |
| 160 }, |
| 161 "linearize": { |
| 162 "processors": [{ |
| 163 "processor": "%s", |
| 164 "config": { "delay": %d } |
| 165 }] |
| 166 } |
| 167 } |
| 168 )json"; |
| 169 |
| 170 const int kDefaultProcessorDelay = 10; |
| 171 const int kTtsProcessorDelay = 100; |
| 172 const int kMixProcessorDelay = 1000; |
| 173 const int kLinearizeProcessorDelay = 10000; |
| 174 |
123 // Return a scoped pointer filled with the data laid out at |index| above. | 175 // Return a scoped pointer filled with the data laid out at |index| above. |
124 std::unique_ptr<::media::AudioBus> GetTestData(size_t index) { | 176 std::unique_ptr<::media::AudioBus> GetTestData(size_t index) { |
125 CHECK_LT(index, NUM_DATA_SETS); | 177 CHECK_LT(index, NUM_DATA_SETS); |
126 int frames = NUM_SAMPLES / kNumChannels; | 178 int frames = NUM_SAMPLES / kNumChannels; |
127 auto data = ::media::AudioBus::Create(kNumChannels, frames); | 179 auto data = ::media::AudioBus::Create(kNumChannels, frames); |
128 data->FromInterleaved(kTestData[index], frames, kBytesPerSample); | 180 data->FromInterleaved(kTestData[index], frames, kBytesPerSample); |
129 return data; | 181 return data; |
130 } | 182 } |
131 | 183 |
132 class MockInputQueue : public StreamMixerAlsa::InputQueue { | 184 class MockInputQueue : public StreamMixerAlsa::InputQueue { |
133 public: | 185 public: |
134 explicit MockInputQueue(int samples_per_second, | 186 MockInputQueue(int samples_per_second, |
135 const std::string& device_id = | 187 const std::string& device_id = |
136 ::media::AudioDeviceDescription::kDefaultDeviceId) | 188 ::media::AudioDeviceDescription::kDefaultDeviceId) |
137 : paused_(true), | 189 : paused_(true), |
138 samples_per_second_(samples_per_second), | 190 samples_per_second_(samples_per_second), |
139 max_read_size_(kTestMaxReadSize), | 191 max_read_size_(kTestMaxReadSize), |
140 multiplier_(1.0), | 192 multiplier_(1.0), |
141 primary_(true), | 193 primary_(true), |
142 deleting_(false), | 194 deleting_(false), |
143 device_id_(device_id), | 195 device_id_(device_id), |
144 filter_group_(nullptr) { | 196 filter_group_(nullptr) { |
145 ON_CALL(*this, GetResampledData(_, _)).WillByDefault( | 197 ON_CALL(*this, GetResampledData(_, _)).WillByDefault( |
146 testing::Invoke(this, &MockInputQueue::DoGetResampledData)); | 198 testing::Invoke(this, &MockInputQueue::DoGetResampledData)); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
234 bool primary_; | 286 bool primary_; |
235 bool deleting_; | 287 bool deleting_; |
236 const std::string device_id_; | 288 const std::string device_id_; |
237 FilterGroup* filter_group_; | 289 FilterGroup* filter_group_; |
238 | 290 |
239 std::unique_ptr<::media::AudioBus> data_; | 291 std::unique_ptr<::media::AudioBus> data_; |
240 | 292 |
241 DISALLOW_COPY_AND_ASSIGN(MockInputQueue); | 293 DISALLOW_COPY_AND_ASSIGN(MockInputQueue); |
242 }; | 294 }; |
243 | 295 |
| 296 class MockPostProcessor : public PostProcessingPipeline { |
| 297 public: |
| 298 MockPostProcessor(const std::string& name, |
| 299 const base::ListValue* filter_description_list, |
| 300 int channels) |
| 301 : name_(name) { |
| 302 CHECK(instances_.insert({name_, this}).second); |
| 303 |
| 304 if (!filter_description_list) { |
| 305 // This happens for PostProcessingPipeline with no post-processors. |
| 306 return; |
| 307 } |
| 308 |
| 309 // Parse |filter_description_list| for parameters. |
| 310 for (size_t i = 0; i < filter_description_list->GetSize(); ++i) { |
| 311 const base::DictionaryValue* description_dict; |
| 312 CHECK(filter_description_list->GetDictionary(i, &description_dict)); |
| 313 std::string solib; |
| 314 CHECK(description_dict->GetString("processor", &solib)); |
| 315 // This will initially be called with the actual pipeline on creation. |
| 316 // Ignore and wait for the call to ResetPostProcessorsForTest. |
| 317 if (solib == kDelayModuleSolib) { |
| 318 const base::DictionaryValue* processor_config_dict; |
| 319 CHECK( |
| 320 description_dict->GetDictionary("config", &processor_config_dict)); |
| 321 int module_delay; |
| 322 CHECK(processor_config_dict->GetInteger("delay", &module_delay)); |
| 323 rendering_delay_ += module_delay; |
| 324 processor_config_dict->GetBoolean("ringing", &ringing_); |
| 325 } |
| 326 } |
| 327 ON_CALL(*this, ProcessFrames(_, _, _, _)) |
| 328 .WillByDefault( |
| 329 testing::Invoke(this, &MockPostProcessor::DoProcessFrames)); |
| 330 } |
| 331 ~MockPostProcessor() override { instances_.erase(name_); } |
| 332 MOCK_METHOD4(ProcessFrames, |
| 333 int(const std::vector<float*>& data, |
| 334 int num_frames, |
| 335 float current_volume, |
| 336 bool is_silence)); |
| 337 bool SetSampleRate(int sample_rate) override { return false; } |
| 338 bool IsRinging() override { return ringing_; } |
| 339 std::string name() const { return name_; } |
| 340 |
| 341 static std::unordered_map<std::string, MockPostProcessor*>* instances() { |
| 342 return &instances_; |
| 343 } |
| 344 |
| 345 private: |
| 346 int DoProcessFrames(const std::vector<float*>& data, |
| 347 int num_frames, |
| 348 float current_volume, |
| 349 bool is_sience) { |
| 350 return rendering_delay_; |
| 351 } |
| 352 |
| 353 static std::unordered_map<std::string, MockPostProcessor*> instances_; |
| 354 std::string name_; |
| 355 int rendering_delay_ = 0; |
| 356 bool ringing_ = false; |
| 357 |
| 358 DISALLOW_COPY_AND_ASSIGN(MockPostProcessor); |
| 359 }; |
| 360 |
| 361 std::unordered_map<std::string, MockPostProcessor*> |
| 362 MockPostProcessor::instances_; |
| 363 |
244 // Given |inputs|, returns mixed audio data according to the mixing method used | 364 // Given |inputs|, returns mixed audio data according to the mixing method used |
245 // by the mixer. | 365 // by the mixer. |
246 std::unique_ptr<::media::AudioBus> GetMixedAudioData( | 366 std::unique_ptr<::media::AudioBus> GetMixedAudioData( |
247 const std::vector<testing::StrictMock<MockInputQueue>*>& inputs) { | 367 const std::vector<testing::StrictMock<MockInputQueue>*>& inputs) { |
248 int read_size = std::numeric_limits<int>::max(); | 368 int read_size = std::numeric_limits<int>::max(); |
249 for (auto* input : inputs) { | 369 for (auto* input : inputs) { |
250 CHECK(input); | 370 CHECK(input); |
251 read_size = std::min(input->MaxReadSize(), read_size); | 371 read_size = std::min(input->MaxReadSize(), read_size); |
252 } | 372 } |
253 | 373 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 ASSERT_EQ(expected.channels(), actual.channels()); | 410 ASSERT_EQ(expected.channels(), actual.channels()); |
291 ASSERT_EQ(expected.frames(), actual.frames()); | 411 ASSERT_EQ(expected.frames(), actual.frames()); |
292 for (int c = 0; c < expected.channels(); ++c) { | 412 for (int c = 0; c < expected.channels(); ++c) { |
293 const float* expected_data = expected.channel(c); | 413 const float* expected_data = expected.channel(c); |
294 const float* actual_data = actual.channel(c); | 414 const float* actual_data = actual.channel(c); |
295 for (int f = 0; f < expected.frames(); ++f) | 415 for (int f = 0; f < expected.frames(); ++f) |
296 ASSERT_FLOAT_EQ(*expected_data++, *actual_data++) << c << " " << f; | 416 ASSERT_FLOAT_EQ(*expected_data++, *actual_data++) << c << " " << f; |
297 } | 417 } |
298 } | 418 } |
299 | 419 |
| 420 // Check that MediaPipelineBackendAlsa::RenderingDelay.delay_microseconds is |
| 421 // within kMaxDelayErrorUs of |delay| |
| 422 MATCHER_P2(MatchDelay, delay, id, "") { |
| 423 bool result = std::abs(arg.delay_microseconds - delay) < kMaxDelayErrorUs; |
| 424 if (!result) { |
| 425 LOG(ERROR) << "Expected delay_microseconds for " << id << " to be " << delay |
| 426 << " but got " << arg.delay_microseconds; |
| 427 } |
| 428 return result; |
| 429 } |
| 430 |
| 431 // Convert a number of frames at kTestSamplesPerSecond to microseconds |
| 432 int64_t FramesToDelayUs(int64_t frames) { |
| 433 return frames * base::Time::kMicrosecondsPerSecond / kTestSamplesPerSecond; |
| 434 } |
| 435 |
300 } // namespace | 436 } // namespace |
301 | 437 |
| 438 std::unique_ptr<PostProcessingPipeline> PostProcessingPipeline::Create( |
| 439 const std::string& name, |
| 440 const base::ListValue* filter_description_list, |
| 441 int channels) { |
| 442 return base::MakeUnique<testing::NiceMock<MockPostProcessor>>( |
| 443 name, filter_description_list, channels); |
| 444 } |
| 445 |
302 class StreamMixerAlsaTest : public testing::Test { | 446 class StreamMixerAlsaTest : public testing::Test { |
303 protected: | 447 protected: |
304 StreamMixerAlsaTest() | 448 StreamMixerAlsaTest() |
305 : message_loop_(new base::MessageLoop()), | 449 : message_loop_(new base::MessageLoop()), |
306 mock_alsa_(new testing::NiceMock<MockAlsaWrapper>()) { | 450 mock_alsa_(new testing::NiceMock<MockAlsaWrapper>()) { |
307 StreamMixerAlsa::MakeSingleThreadedForTest(); | 451 StreamMixerAlsa::MakeSingleThreadedForTest(); |
308 StreamMixerAlsa::Get()->DisablePostProcessingForTest(); | 452 char test_pipeline_json[sizeof(kTestPipelineJsonTemplate) * 2]; |
| 453 snprintf(test_pipeline_json, sizeof(test_pipeline_json), |
| 454 kTestPipelineJsonTemplate, kDelayModuleSolib, |
| 455 kDefaultProcessorDelay, kDelayModuleSolib, kTtsProcessorDelay, |
| 456 kDelayModuleSolib, kMixProcessorDelay, kDelayModuleSolib, |
| 457 kLinearizeProcessorDelay); |
| 458 StreamMixerAlsa::Get()->ResetPostProcessorsForTest(test_pipeline_json); |
| 459 CHECK_EQ(MockPostProcessor::instances()->size(), |
| 460 static_cast<size_t>(kNumPostProcessors)); |
309 StreamMixerAlsa::Get()->SetAlsaWrapperForTest(base::WrapUnique(mock_alsa_)); | 461 StreamMixerAlsa::Get()->SetAlsaWrapperForTest(base::WrapUnique(mock_alsa_)); |
310 } | 462 } |
311 | 463 |
312 ~StreamMixerAlsaTest() override { | 464 ~StreamMixerAlsaTest() override { |
313 StreamMixerAlsa::Get()->ClearInputsForTest(); | 465 StreamMixerAlsa::Get()->ClearInputsForTest(); |
314 StreamMixerAlsa::Get()->SetAlsaWrapperForTest(nullptr); | 466 StreamMixerAlsa::Get()->SetAlsaWrapperForTest(nullptr); |
315 } | 467 } |
316 | 468 |
317 MockAlsaWrapper* mock_alsa() { return mock_alsa_; } | 469 MockAlsaWrapper* mock_alsa() { return mock_alsa_; } |
318 | 470 |
(...skipping 477 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
796 EXPECT_CALL(*inputs[0], AfterWriteFrames(_)); | 948 EXPECT_CALL(*inputs[0], AfterWriteFrames(_)); |
797 EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0); | 949 EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0); |
798 EXPECT_CALL(*inputs[1], VolumeScaleAccumulate(_, _, _, _)).Times(0); | 950 EXPECT_CALL(*inputs[1], VolumeScaleAccumulate(_, _, _, _)).Times(0); |
799 EXPECT_CALL(*inputs[1], OnSkipped()); | 951 EXPECT_CALL(*inputs[1], OnSkipped()); |
800 EXPECT_CALL(*inputs[1], AfterWriteFrames(_)); | 952 EXPECT_CALL(*inputs[1], AfterWriteFrames(_)); |
801 | 953 |
802 EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1); | 954 EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1); |
803 mixer->WriteFramesForTest(); | 955 mixer->WriteFramesForTest(); |
804 } | 956 } |
805 | 957 |
| 958 #define EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(map, name, times, frames, \ |
| 959 silence) \ |
| 960 do { \ |
| 961 auto itr = map->find(name); \ |
| 962 CHECK(itr != map->end()) << "Could not find processor for " << name; \ |
| 963 EXPECT_CALL(*(itr->second), ProcessFrames(_, frames, _, silence)) \ |
| 964 .Times(times); \ |
| 965 } while (0); |
| 966 |
| 967 TEST_F(StreamMixerAlsaTest, PostProcessorDelayListedDeviceId) { |
| 968 int common_delay = kMixProcessorDelay + kLinearizeProcessorDelay; |
| 969 std::vector<testing::StrictMock<MockInputQueue>*> inputs; |
| 970 std::vector<int64_t> delays; |
| 971 inputs.push_back(new testing::StrictMock<MockInputQueue>( |
| 972 kTestSamplesPerSecond, "default")); |
| 973 delays.push_back(common_delay + kDefaultProcessorDelay); |
| 974 |
| 975 inputs.push_back(new testing::StrictMock<MockInputQueue>( |
| 976 kTestSamplesPerSecond, "communications")); |
| 977 delays.push_back(common_delay); |
| 978 |
| 979 inputs.push_back(new testing::StrictMock<MockInputQueue>( |
| 980 kTestSamplesPerSecond, "assistant-tts")); |
| 981 delays.push_back(common_delay + kTtsProcessorDelay); |
| 982 |
| 983 // Convert delay from frames to microseconds. |
| 984 std::transform(delays.begin(), delays.end(), delays.begin(), |
| 985 &FramesToDelayUs); |
| 986 |
| 987 const int kNumFrames = 10; |
| 988 for (auto* input : inputs) { |
| 989 input->SetMaxReadSize(kNumFrames); |
| 990 input->SetPaused(false); |
| 991 } |
| 992 |
| 993 StreamMixerAlsa* mixer = StreamMixerAlsa::Get(); |
| 994 for (size_t i = 0; i < inputs.size(); ++i) { |
| 995 EXPECT_CALL(*inputs[i], Initialize(_)).Times(1); |
| 996 mixer->AddInput(base::WrapUnique(inputs[i])); |
| 997 } |
| 998 |
| 999 mock_alsa()->set_avail(4086); |
| 1000 |
| 1001 auto* post_processors = MockPostProcessor::instances(); |
| 1002 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, |
| 1003 kNumFrames, false); |
| 1004 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, kNumFrames, |
| 1005 false); |
| 1006 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, |
| 1007 kNumFrames, false); |
| 1008 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "communications", 1, |
| 1009 kNumFrames, false); |
| 1010 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 1, |
| 1011 kNumFrames, false); |
| 1012 |
| 1013 // Poll the inputs for data. Each input will get a different |
| 1014 // rendering delay based on their device type. |
| 1015 for (size_t i = 0; i < inputs.size(); ++i) { |
| 1016 EXPECT_CALL(*inputs[i], GetResampledData(_, kNumFrames)); |
| 1017 EXPECT_CALL(*inputs[i], VolumeScaleAccumulate(_, _, kNumFrames, _)) |
| 1018 .Times(kNumChannels); |
| 1019 EXPECT_CALL(*inputs[i], AfterWriteFrames( |
| 1020 MatchDelay(delays[i], inputs[i]->device_id()))); |
| 1021 } |
| 1022 mixer->WriteFramesForTest(); |
| 1023 } |
| 1024 |
| 1025 TEST_F(StreamMixerAlsaTest, PostProcessorDelayUnlistedDevice) { |
| 1026 const std::string device_id = "not-a-device-id"; |
| 1027 testing::StrictMock<MockInputQueue>* input = |
| 1028 new testing::StrictMock<MockInputQueue>(kTestSamplesPerSecond, device_id); |
| 1029 |
| 1030 // Delay should be based on default processor |
| 1031 int64_t delay = FramesToDelayUs( |
| 1032 kDefaultProcessorDelay + kLinearizeProcessorDelay + kMixProcessorDelay); |
| 1033 const int kNumFrames = 10; |
| 1034 input->SetMaxReadSize(kNumFrames); |
| 1035 input->SetPaused(false); |
| 1036 |
| 1037 auto* post_processors = MockPostProcessor::instances(); |
| 1038 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, |
| 1039 kNumFrames, false); |
| 1040 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, kNumFrames, |
| 1041 false); |
| 1042 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, |
| 1043 kNumFrames, false); |
| 1044 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "communications", 0, |
| 1045 _, _); |
| 1046 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "assistant-tts", 0, |
| 1047 _, _); |
| 1048 |
| 1049 StreamMixerAlsa* mixer = StreamMixerAlsa::Get(); |
| 1050 EXPECT_CALL(*input, Initialize(_)); |
| 1051 mixer->AddInput(base::WrapUnique(input)); |
| 1052 |
| 1053 EXPECT_CALL(*input, GetResampledData(_, kNumFrames)); |
| 1054 EXPECT_CALL(*input, VolumeScaleAccumulate(_, _, kNumFrames, _)) |
| 1055 .Times(kNumChannels); |
| 1056 EXPECT_CALL(*input, AfterWriteFrames(MatchDelay(delay, device_id))); |
| 1057 mixer->WriteFramesForTest(); |
| 1058 } |
| 1059 |
| 1060 TEST_F(StreamMixerAlsaTest, PostProcessorRingingWithoutInputs) { |
| 1061 const char kTestPipelineJson[] = R"json( |
| 1062 { |
| 1063 "output_streams": [{ |
| 1064 "streams": [ "default" ], |
| 1065 "processors": [{ |
| 1066 "processor": "%s", |
| 1067 "config": { "delay": 0, "ringing": true} |
| 1068 }] |
| 1069 }, { |
| 1070 "streams": [ "foobar" ], |
| 1071 "processors": [{ |
| 1072 "processor": "%s", |
| 1073 "config": { "delay": 0, "ringing": true} |
| 1074 }] |
| 1075 }] |
| 1076 } |
| 1077 )json"; |
| 1078 |
| 1079 StreamMixerAlsa* mixer = StreamMixerAlsa::Get(); |
| 1080 char test_pipeline_json[sizeof(kTestPipelineJson) * 2]; |
| 1081 snprintf(test_pipeline_json, sizeof(test_pipeline_json), kTestPipelineJson, |
| 1082 kDelayModuleSolib, kDelayModuleSolib); |
| 1083 mixer->ResetPostProcessorsForTest(test_pipeline_json); |
| 1084 // "mix" + "linearize" should be automatic |
| 1085 CHECK_EQ(MockPostProcessor::instances()->size(), 4u); |
| 1086 |
| 1087 mock_alsa()->set_avail(4086); |
| 1088 |
| 1089 auto* post_processors = MockPostProcessor::instances(); |
| 1090 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "default", 1, |
| 1091 kPreventUnderrunChunkSize, true); |
| 1092 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "mix", 1, |
| 1093 kPreventUnderrunChunkSize, true); |
| 1094 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "linearize", 1, |
| 1095 kPreventUnderrunChunkSize, true); |
| 1096 EXPECT_POSTPROCESSOR_CALL_PROCESSFRAMES(post_processors, "foobar", 0, _, _); |
| 1097 |
| 1098 mixer->WriteFramesForTest(); |
| 1099 } |
| 1100 |
806 } // namespace media | 1101 } // namespace media |
807 } // namespace chromecast | 1102 } // namespace chromecast |
OLD | NEW |