snooper_node_unittest.cc 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "services/audio/snooper_node.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/command_line.h"
  10. #include "base/files/file_path.h"
  11. #include "base/logging.h"
  12. #include "base/memory/scoped_refptr.h"
  13. #include "base/strings/string_piece.h"
  14. #include "base/test/test_mock_time_task_runner.h"
  15. #include "base/time/time.h"
  16. #include "media/base/audio_bus.h"
  17. #include "media/base/audio_parameters.h"
  18. #include "media/base/channel_layout.h"
  19. #include "services/audio/test/fake_consumer.h"
  20. #include "services/audio/test/fake_loopback_group_member.h"
  21. #include "testing/gtest/include/gtest/gtest.h"
  22. #include "third_party/abseil-cpp/absl/types/optional.h"
  23. namespace audio {
  24. namespace {
  25. // Used to test whether the output AudioBuses have had all their values set to
  26. // something finite.
  27. constexpr float kInvalidAudioSample = std::numeric_limits<float>::infinity();
  28. // The tones the source should generate into the left and right channels.
  29. constexpr double kLeftChannelFrequency = 500.0;
  30. constexpr double kRightChannelFrequency = 1200.0;
  31. constexpr double kSourceVolume = 0.5;
  32. // The duration of the audio that flows through the SnooperNode for each test.
  33. constexpr base::TimeDelta kTestDuration = base::Seconds(10);
  34. // The amount of time in the future where the inbound audio is being recorded.
  35. // This simulates an audio output stream that has rendered audio that is
  36. // scheduled to be played out in the near future.
  37. constexpr base::TimeDelta kInputAdvanceTime = base::Milliseconds(2);
  38. // Command-line switch to request dumping the recorded output to a WAV file for
  39. // analyzing the recorded output from one of the tests.
  40. constexpr base::StringPiece kDumpAsWavSwitch = "dump-as-wav";
  41. // Test parameters.
  42. struct InputAndOutputParams {
  43. media::AudioParameters input;
  44. media::AudioParameters output;
  45. };
  46. // Helper so that gtest can produce useful logging of the test parameters.
  47. std::ostream& operator<<(std::ostream& out,
  48. const InputAndOutputParams& test_params) {
  49. return out << "{input=" << test_params.input.AsHumanReadableString()
  50. << ", output=" << test_params.output.AsHumanReadableString()
  51. << "}";
  52. }
  53. class SnooperNodeTest : public testing::TestWithParam<InputAndOutputParams> {
  54. public:
  55. SnooperNodeTest() = default;
  56. ~SnooperNodeTest() override = default;
  57. const media::AudioParameters& input_params() const {
  58. return GetParam().input;
  59. }
  60. const media::AudioParameters& output_params() const {
  61. return GetParam().output;
  62. }
  63. base::TimeDelta output_delay() const { return output_delay_; }
  64. double max_relative_error() const { return max_relative_error_; }
  65. base::TestMockTimeTaskRunner* task_runner() const {
  66. return task_runner_.get();
  67. }
  68. FakeLoopbackGroupMember* group_member() { return &*group_member_; }
  69. SnooperNode* node() { return &*node_; }
  70. FakeConsumer* consumer() { return &*consumer_; }
  71. void SetUp() override {
  72. // Determine the amount of time in the past from which outbound audio should
  73. // be rendered. Use 20 ms as a reasonable baseline--the same as the initial
  74. // setting in audio::LoopbackStream--which will work for almost all normal
  75. // use cases.
  76. constexpr base::TimeDelta kBaselineOutputDelay = base::Milliseconds(20);
  77. output_delay_ = kBaselineOutputDelay;
  78. // Increase the output delay in special cases...
  79. if (input_params().sample_rate() < 32000) {
  80. // At the lower input sample rates (e.g., 8 kHz), prebufferring inside
  81. // SnooperNode's resampler becomes an issue: It uses a fixed size buffer
  82. // of 128 samples, which equates to a much longer duration of audio at the
  83. // lower sampling rates. With more buffering involved, the output delay
  84. // must be increased to avoid underruns.
  85. output_delay_ *= 2;
  86. } else if (input_params().GetBufferDuration() >
  87. output_params().GetBufferDuration()) {
  88. // For the HandlesBackwardsInput() test, the input goes backward by a full
  89. // buffer. If the duration of an input buffer is larger than an output
  90. // buffer, this could cause a brief moment of underrun (which is WAI!).
  91. // Rather than write lots of extra test code around such a specific
  92. // scenario, just fudge the delay up a little such that the underrun
  93. // cannot occur.
  94. output_delay_ += input_params().GetBufferDuration();
  95. }
  96. // Determine the maximum allowable error when measuring expected amplitudes.
  97. // This varies with the sampling rate because the loss of resolution at the
  98. // lower sampling rates can introduce error in the "amplitude-sensing
  99. // logic." Higher frequencies in the audio signal are especially vulnerable
  100. // to the error introduced by using low sampling rates.
  101. constexpr double kHighResAllowedError = 0.02; // 2% at 48 kHz
  102. constexpr double kHighResSampleRate = 48000;
  103. constexpr double kLowResAllowedError = 0.10; // 10% at 8 kHz
  104. constexpr double kLowResSampleRate = 8000;
  105. const int the_lower_sample_rate =
  106. std::min(input_params().sample_rate(), output_params().sample_rate());
  107. const double t = (the_lower_sample_rate - kHighResSampleRate) /
  108. (kLowResSampleRate - kHighResSampleRate);
  109. max_relative_error_ =
  110. kHighResAllowedError + t * (kLowResAllowedError - kHighResAllowedError);
  111. // Initialize a test clock and task runner. The starting TimeTicks value is
  112. // "huge" to ensure time calculations are being tested for overflow cases.
  113. task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
  114. base::Time(), base::TimeTicks() + base::Microseconds(INT64_C(1) << 62));
  115. }
  116. void TearDown() override {
  117. // If the "dump-as-wav" command-line switch is present, dump whatever has
  118. // been recorded in the consumer.
  119. const base::FilePath path =
  120. base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
  121. kDumpAsWavSwitch);
  122. if (!path.empty()) {
  123. if (consumer_) {
  124. consumer_->SaveToFile(path);
  125. } else {
  126. LOG(ERROR) << "No consumer: ignoring --" << kDumpAsWavSwitch;
  127. }
  128. }
  129. }
  130. // Selects which frequency to return for each channel based on the input and
  131. // output channel layout.
  132. enum WhichFlow : int8_t {
  133. FOR_INPUT,
  134. FOR_SWAPPED_INPUT,
  135. FOR_OUTPUT,
  136. FOR_SWAPPED_OUTPUT,
  137. };
  138. double GetLeftChannelFrequency(WhichFlow which) const {
  139. switch (which) {
  140. case FOR_INPUT:
  141. case FOR_OUTPUT:
  142. return kLeftChannelFrequency;
  143. case FOR_SWAPPED_INPUT:
  144. case FOR_SWAPPED_OUTPUT:
  145. return kRightChannelFrequency;
  146. }
  147. }
  148. double GetRightChannelFrequency(WhichFlow which) const {
  149. switch (which) {
  150. // If the test parameters call for stereo→mono channel down-mixing, use
  151. // the left channel frequency again for the right channel input. Down-
  152. // mixing is tested elsewhere.
  153. case FOR_INPUT:
  154. return (output_params().channels() == 1 ? kLeftChannelFrequency
  155. : kRightChannelFrequency);
  156. case FOR_SWAPPED_INPUT:
  157. return (output_params().channels() == 1 ? kRightChannelFrequency
  158. : kLeftChannelFrequency);
  159. // If the input was monaural, the output's right channel should contain
  160. // the input's "left" channel frequency.
  161. case FOR_OUTPUT:
  162. return (input_params().channels() == 1 ? kLeftChannelFrequency
  163. : kRightChannelFrequency);
  164. case FOR_SWAPPED_OUTPUT:
  165. return (input_params().channels() == 1 ? kRightChannelFrequency
  166. : kLeftChannelFrequency);
  167. }
  168. }
  169. void CreateNewPipeline() {
  170. group_member_.emplace(input_params());
  171. group_member_->SetChannelTone(0, GetLeftChannelFrequency(FOR_INPUT));
  172. if (input_params().channels() > 1) {
  173. group_member_->SetChannelTone(1, GetRightChannelFrequency(FOR_INPUT));
  174. }
  175. group_member_->SetVolume(kSourceVolume);
  176. node_.emplace(input_params(), output_params());
  177. group_member_->StartSnooping(node());
  178. consumer_.emplace(output_params().channels(),
  179. output_params().sample_rate());
  180. }
  181. // Have the SnooperNode render more output data and store it in the consumer
  182. // for later analysis.
  183. void RenderAndConsume(base::TimeTicks output_time) {
  184. // Assign invalid sample values to the AudioBus. Then, after the Render()
  185. // call, confirm that every sample was overwritten in the output AudioBus.
  186. const auto bus = media::AudioBus::Create(output_params());
  187. for (int ch = 0; ch < bus->channels(); ++ch) {
  188. std::fill_n(bus->channel(ch), bus->frames(), kInvalidAudioSample);
  189. }
  190. // If the SnooperNode provides a suggestion, check that |output_time| is
  191. // okay. Otherwise, Render() will be producing zero-fill gaps as the end of
  192. // |bus|. Don't do this check if there is already a test failure, and this
  193. // would just keep spamming the test output.
  194. if (!HasFailure()) {
  195. const absl::optional<base::TimeTicks> suggestion =
  196. node_->SuggestLatestRenderTime(bus->frames());
  197. if (suggestion) {
  198. EXPECT_LE(output_time, *suggestion)
  199. << "at frame=" << consumer_->GetRecordedFrameCount();
  200. }
  201. }
  202. node_->Render(output_time, bus.get());
  203. for (int ch = 0; ch < bus->channels(); ++ch) {
  204. EXPECT_FALSE(
  205. std::any_of(bus->channel(ch), bus->channel(ch) + bus->frames(),
  206. [](float x) { return x == kInvalidAudioSample; }))
  207. << " at output_time=" << output_time << ", ch=" << ch;
  208. }
  209. consumer_->Consume(*bus);
  210. }
  211. // Post delayed tasks to schedule normal, uninterrupted input with the default
  212. // kInputAdvanceTime delay.
  213. void ScheduleDefaultInputTasks(double skew = 1.0) {
  214. const base::TimeTicks start_time = task_runner_->NowTicks();
  215. const base::TimeTicks end_time = start_time + kTestDuration;
  216. const double time_step = skew / input_params().sample_rate();
  217. for (int position = 0;; position += input_params().frames_per_buffer()) {
  218. const base::TimeTicks task_time =
  219. start_time + base::Seconds(position * time_step);
  220. if (task_time >= end_time) {
  221. break;
  222. }
  223. const base::TimeTicks reference_time = task_time + kInputAdvanceTime;
  224. task_runner_->PostDelayedTask(
  225. FROM_HERE,
  226. base::BindOnce(&FakeLoopbackGroupMember::RenderMoreAudio,
  227. base::Unretained(group_member()), reference_time),
  228. task_time - start_time);
  229. }
  230. }
  231. // Post delayed tasks to schedule normal, uninterrupted output rendering to
  232. // occur at the default kCaptureDelay.
  233. void ScheduleDefaultRenderTasks(double skew = 1.0) {
  234. const base::TimeTicks start_time = task_runner_->NowTicks();
  235. const base::TimeTicks end_time = start_time + kTestDuration;
  236. const double time_step = skew / output_params().sample_rate();
  237. for (int position = 0;; position += output_params().frames_per_buffer()) {
  238. const base::TimeTicks task_time =
  239. start_time + base::Seconds(position * time_step);
  240. if (task_time >= end_time) {
  241. break;
  242. }
  243. const base::TimeTicks reference_time = task_time - output_delay();
  244. task_runner_->PostDelayedTask(
  245. FROM_HERE,
  246. base::BindOnce(&SnooperNodeTest::RenderAndConsume,
  247. base::Unretained(this), reference_time),
  248. task_time - start_time);
  249. }
  250. }
  251. void RunAllPendingTasks() { task_runner_->FastForwardUntilNoTasksRemain(); }
  252. private:
  253. scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
  254. // A suitable output delay to use for rendering audio from the pipeline. See
  255. // comments in SetUp() for further details.
  256. base::TimeDelta output_delay_;
  257. // The maximum allowable error relative to an expected amplitude.
  258. double max_relative_error_ = 0.0;
  259. // The pipeline from source to consumer.
  260. absl::optional<FakeLoopbackGroupMember> group_member_;
  261. absl::optional<SnooperNode> node_;
  262. absl::optional<FakeConsumer> consumer_;
  263. };
  264. // The skew test here is generating 10 seconds of audio per iteration, with
  265. // 5*5=25 iterations. That's 250 seconds of audio being generated to check for
  266. // skew-related issues. That's a lot of processing power needed! Thus, only
  267. // enable this test on optimized, non-debug builds, where it will run in a
  268. // reasonable amount of time. http://crbug.com/842428
  269. #ifdef NDEBUG
  270. #define MAYBE_ContinuousAudioFlowAdaptsToSkew ContinuousAudioFlowAdaptsToSkew
  271. #else
  272. #define MAYBE_ContinuousAudioFlowAdaptsToSkew \
  273. DISABLED_ContinuousAudioFlowAdaptsToSkew
  274. #endif
  275. // Tests that the internal time-stretching logic can handle various combinations
  276. // of input and output skews.
  277. TEST_P(SnooperNodeTest, MAYBE_ContinuousAudioFlowAdaptsToSkew) {
  278. // Note: A skew of 0.999 or 1.001 is very extreme. This is like saying the
  279. // clocks drift 1 ms for every second that goes by. If the implementation can
  280. // handle that, it's very likely to do a perfect job in-the-wild.
  281. for (double input_skew = 0.999; input_skew <= 1.001; input_skew += 0.0005) {
  282. for (double output_skew = 0.999; output_skew <= 1.001;
  283. output_skew += 0.0005) {
  284. SCOPED_TRACE(testing::Message() << "input_skew=" << input_skew
  285. << ", output_skew=" << output_skew);
  286. CreateNewPipeline();
  287. ScheduleDefaultInputTasks(input_skew);
  288. ScheduleDefaultRenderTasks(output_skew);
  289. RunAllPendingTasks();
  290. // All rendering for points-in-time before the audio from the source was
  291. // first recorded should be silence.
  292. const double expected_end_of_silence_position =
  293. ((input_skew * kInputAdvanceTime.InSecondsF()) +
  294. (output_skew * output_delay().InSecondsF())) *
  295. output_params().sample_rate();
  296. const double frames_in_one_millisecond =
  297. output_params().sample_rate() /
  298. double{base::Time::kMillisecondsPerSecond};
  299. EXPECT_NEAR(expected_end_of_silence_position,
  300. consumer()->FindEndOfSilence(0, 0),
  301. frames_in_one_millisecond);
  302. if (output_params().channels() > 1) {
  303. EXPECT_NEAR(expected_end_of_silence_position,
  304. consumer()->FindEndOfSilence(1, 0),
  305. frames_in_one_millisecond);
  306. }
  307. // Analyze the recording in several places for the expected tones.
  308. constexpr int kNumToneChecks = 16;
  309. for (int i = 1; i <= kNumToneChecks; ++i) {
  310. const int end_frame =
  311. consumer()->GetRecordedFrameCount() * i / kNumToneChecks;
  312. SCOPED_TRACE(testing::Message() << "end_frame=" << end_frame);
  313. EXPECT_NEAR(kSourceVolume,
  314. consumer()->ComputeAmplitudeAt(
  315. 0, GetLeftChannelFrequency(FOR_OUTPUT), end_frame),
  316. kSourceVolume * max_relative_error());
  317. if (output_params().channels() > 1) {
  318. EXPECT_NEAR(kSourceVolume,
  319. consumer()->ComputeAmplitudeAt(
  320. 1, GetRightChannelFrequency(FOR_OUTPUT), end_frame),
  321. kSourceVolume * max_relative_error());
  322. }
  323. }
  324. if (HasFailure()) {
  325. return;
  326. }
  327. }
  328. }
  329. }
  330. // Tests that gaps in the input are detected, are handled by introducing
  331. // zero-fill gaps in the output, and don't throw-off the timing/synchronization
  332. // between input and output.
  333. TEST_P(SnooperNodeTest, HandlesMissingInput) {
  334. CreateNewPipeline();
  335. // Schedule all input tasks, with drops to occur once per second for 1/4
  336. // second duration.
  337. const base::TimeTicks start_time = task_runner()->NowTicks();
  338. const base::TimeTicks end_time = start_time + kTestDuration;
  339. const double time_step = 1.0 / input_params().sample_rate();
  340. const int input_frames_in_one_second = input_params().sample_rate();
  341. // Drop duration: 1/4 second in terms of frames, aligned to frame buffer size.
  342. const int drop_duration =
  343. ((input_frames_in_one_second / 4) / input_params().frames_per_buffer()) *
  344. input_params().frames_per_buffer();
  345. int next_drop_position = input_frames_in_one_second;
  346. for (int position = 0;; position += input_params().frames_per_buffer()) {
  347. if (position >= next_drop_position) {
  348. position += drop_duration;
  349. next_drop_position += input_frames_in_one_second;
  350. }
  351. const base::TimeTicks task_time =
  352. start_time + base::Seconds(position * time_step);
  353. if (task_time >= end_time) {
  354. break;
  355. }
  356. const base::TimeTicks reference_time = task_time + kInputAdvanceTime;
  357. task_runner()->PostDelayedTask(
  358. FROM_HERE,
  359. base::BindOnce(&FakeLoopbackGroupMember::RenderMoreAudio,
  360. base::Unretained(group_member()), reference_time),
  361. task_time - start_time);
  362. }
  363. ScheduleDefaultRenderTasks();
  364. RunAllPendingTasks();
  365. // Check that there is silence in the drop positions, and that tones are
  366. // present around the silent sections. The ranges are adjusted to be 20 ms
  367. // away from the exact begin/end positions to account for a reasonable amount
  368. // of variance in due to the input buffer intervals.
  369. const int output_frames_in_one_second = output_params().sample_rate();
  370. const int output_frames_in_a_quarter_second = output_frames_in_one_second / 4;
  371. const int output_frames_in_20_milliseconds =
  372. output_frames_in_one_second * 20 / base::Time::kMillisecondsPerSecond;
  373. int output_silence_position =
  374. ((kInputAdvanceTime + output_delay()).InSecondsF() + 1.0) *
  375. output_params().sample_rate();
  376. for (int gap = 0; gap < 5; ++gap) {
  377. SCOPED_TRACE(testing::Message() << "gap=" << gap);
  378. // Just before the drop, there should be a tone.
  379. const int position_a_little_before_silence_begins =
  380. output_silence_position - output_frames_in_20_milliseconds;
  381. EXPECT_NEAR(
  382. kSourceVolume,
  383. consumer()->ComputeAmplitudeAt(0, GetLeftChannelFrequency(FOR_OUTPUT),
  384. position_a_little_before_silence_begins),
  385. kSourceVolume * max_relative_error());
  386. if (output_params().channels() > 1) {
  387. EXPECT_NEAR(kSourceVolume,
  388. consumer()->ComputeAmplitudeAt(
  389. 1, GetRightChannelFrequency(FOR_OUTPUT),
  390. position_a_little_before_silence_begins),
  391. kSourceVolume * max_relative_error());
  392. }
  393. // There should be silence during the drop.
  394. const int position_a_little_after_silence_begins =
  395. output_silence_position + output_frames_in_20_milliseconds;
  396. const int position_a_little_before_silence_ends =
  397. position_a_little_after_silence_begins +
  398. output_frames_in_a_quarter_second -
  399. 2 * output_frames_in_20_milliseconds;
  400. EXPECT_TRUE(
  401. consumer()->IsSilentInRange(0, position_a_little_after_silence_begins,
  402. position_a_little_before_silence_ends));
  403. // Finally, the tone should be back after the drop.
  404. const int position_a_little_after_silence_ends =
  405. position_a_little_before_silence_ends +
  406. 2 * output_frames_in_20_milliseconds;
  407. EXPECT_NEAR(
  408. kSourceVolume,
  409. consumer()->ComputeAmplitudeAt(0, GetLeftChannelFrequency(FOR_OUTPUT),
  410. position_a_little_after_silence_ends),
  411. kSourceVolume * max_relative_error());
  412. if (output_params().channels() > 1) {
  413. EXPECT_NEAR(kSourceVolume,
  414. consumer()->ComputeAmplitudeAt(
  415. 1, GetRightChannelFrequency(FOR_OUTPUT),
  416. position_a_little_after_silence_ends),
  417. kSourceVolume * max_relative_error());
  418. }
  419. output_silence_position += output_frames_in_one_second;
  420. }
  421. }
  422. // Tests that a backwards-jump in input reference timestamps doesn't attempt to
  423. // "re-write history" and otherwise maintains the timing/synchronization between
  424. // input and output. This is a regression test for http://crbug.com/934770.
  425. TEST_P(SnooperNodeTest, HandlesBackwardsInput) {
  426. CreateNewPipeline();
  427. // Schedule all input tasks. At the halfway point, simulate a device change
  428. // that shifts the timestamps backward by one buffer duration, and the
  429. // left/right sound tones are swapped.
  430. const base::TimeTicks start_time = task_runner()->NowTicks();
  431. const base::TimeTicks end_time = start_time + kTestDuration;
  432. const double time_step = 1.0 / input_params().sample_rate();
  433. const int change_position =
  434. input_params().sample_rate() * kTestDuration.InSeconds() / 2;
  435. int position_offset = 0;
  436. for (int position = 0;; position += input_params().frames_per_buffer()) {
  437. const base::TimeTicks task_time =
  438. start_time + base::Seconds(position * time_step);
  439. if (task_time >= end_time) {
  440. break;
  441. }
  442. if (position_offset == 0 && position >= change_position) {
  443. position_offset = -input_params().frames_per_buffer();
  444. task_runner()->PostDelayedTask(
  445. FROM_HERE,
  446. base::BindOnce(
  447. [](SnooperNodeTest* test) {
  448. test->group_member()->SetChannelTone(
  449. 0, test->GetLeftChannelFrequency(FOR_SWAPPED_INPUT));
  450. if (test->input_params().channels() > 1) {
  451. test->group_member()->SetChannelTone(
  452. 1, test->GetRightChannelFrequency(FOR_SWAPPED_INPUT));
  453. }
  454. },
  455. this),
  456. task_time - start_time);
  457. }
  458. const base::TimeTicks reference_time =
  459. start_time + kInputAdvanceTime +
  460. base::Seconds((position + position_offset) * time_step);
  461. task_runner()->PostDelayedTask(
  462. FROM_HERE,
  463. base::BindOnce(&FakeLoopbackGroupMember::RenderMoreAudio,
  464. base::Unretained(group_member()), reference_time),
  465. task_time - start_time);
  466. }
  467. ScheduleDefaultRenderTasks();
  468. RunAllPendingTasks();
  469. // In the consumer's recording, there should be audio having the default tones
  470. // before before the halfway point. After the halfway point, the tones should
  471. // be swapped (left vs right). Sample once every second, starting at a
  472. // ~half-second offset.
  473. const int output_position_halfway =
  474. (kInputAdvanceTime + output_delay() + (kTestDuration / 2)).InSecondsF() *
  475. output_params().sample_rate();
  476. const int output_frames_in_one_second = output_params().sample_rate();
  477. int output_position =
  478. ((kInputAdvanceTime + output_delay()).InSecondsF() + 0.5) *
  479. output_params().sample_rate();
  480. for (int output_end = consumer()->GetRecordedFrameCount();
  481. output_position < output_end;
  482. output_position += output_frames_in_one_second) {
  483. const int left_ch_freq = (output_position < output_position_halfway)
  484. ? GetLeftChannelFrequency(FOR_OUTPUT)
  485. : GetLeftChannelFrequency(FOR_SWAPPED_OUTPUT);
  486. EXPECT_NEAR(
  487. kSourceVolume,
  488. consumer()->ComputeAmplitudeAt(0, left_ch_freq, output_position),
  489. kSourceVolume * max_relative_error());
  490. if (output_params().channels() > 1) {
  491. const int right_ch_freq =
  492. (output_position < output_position_halfway)
  493. ? GetRightChannelFrequency(FOR_OUTPUT)
  494. : GetRightChannelFrequency(FOR_SWAPPED_OUTPUT);
  495. EXPECT_NEAR(
  496. kSourceVolume,
  497. consumer()->ComputeAmplitudeAt(1, right_ch_freq, output_position),
  498. kSourceVolume * max_relative_error());
  499. }
  500. }
  501. }
  502. // Tests that reasonable render times are suggested as audio is feeding into, or
  503. // not feeding into, the SnooperNode.
  504. TEST_P(SnooperNodeTest, SuggestsRenderTimes) {
  505. constexpr base::TimeDelta kTwentyMilliseconds = base::Milliseconds(20);
  506. CreateNewPipeline();
  507. // Before any audio has flowed into the SnooperNode, there should be nothing
  508. // to base a suggestion on.
  509. EXPECT_FALSE(
  510. node()->SuggestLatestRenderTime(output_params().frames_per_buffer()));
  511. // Feed-in the first buffer and expect a render time suggestion that is
  512. // greater than 150% the output buffer's duration amount of time in the
  513. // past. (The extra 50% is a safety margin; see internal code comments for
  514. // further details.) The suggestion should also not be too far in the past.
  515. const base::TimeTicks first_input_time = task_runner()->NowTicks();
  516. group_member()->RenderMoreAudio(first_input_time);
  517. const absl::optional<base::TimeTicks> first_suggestion =
  518. node()->SuggestLatestRenderTime(output_params().frames_per_buffer());
  519. ASSERT_TRUE(first_suggestion);
  520. base::TimeTicks time_at_end_of_input =
  521. first_input_time + input_params().GetBufferDuration();
  522. const base::TimeDelta required_duration_buffered =
  523. output_params().GetBufferDuration() * 3 / 2;
  524. EXPECT_GT(time_at_end_of_input - required_duration_buffered,
  525. *first_suggestion);
  526. EXPECT_LT(
  527. time_at_end_of_input - required_duration_buffered - kTwentyMilliseconds,
  528. *first_suggestion);
  529. // If another suggestion is solicited before more input was provided,
  530. // SnooperNode shouldn't give one.
  531. for (int i = 0; i < 3; ++i) {
  532. EXPECT_FALSE(
  533. node()->SuggestLatestRenderTime(output_params().frames_per_buffer()));
  534. }
  535. // When feeding-in successive buffers, a new suggestion can be given after
  536. // each, reflecting the timing of the additional audio that has been buffered.
  537. for (int i = 1; i <= 3; ++i) {
  538. const base::TimeTicks next_input_time =
  539. first_input_time +
  540. base::Seconds(i * input_params().frames_per_buffer() /
  541. static_cast<double>(input_params().sample_rate()));
  542. group_member()->RenderMoreAudio(next_input_time);
  543. const absl::optional<base::TimeTicks> next_suggestion =
  544. node()->SuggestLatestRenderTime(output_params().frames_per_buffer());
  545. ASSERT_TRUE(next_suggestion);
  546. time_at_end_of_input = next_input_time + input_params().GetBufferDuration();
  547. EXPECT_GT(time_at_end_of_input - required_duration_buffered,
  548. *next_suggestion);
  549. EXPECT_LT(
  550. time_at_end_of_input - required_duration_buffered - kTwentyMilliseconds,
  551. *next_suggestion);
  552. }
  553. }
  554. namespace {
  555. // Used in the HandlesSeekedRenderTimes test below. Returns one of 10 possible
  556. // tone frequencies to use at the specified time |offset| in the audio.
  557. double MapTimeOffsetToATone(base::TimeDelta offset) {
  558. constexpr double kMinFrequency = 200;
  559. constexpr double kMaxFrequency = 2000;
  560. constexpr int kNumToneSteps = 10;
  561. const int64_t step_number = offset.IntDiv(kTestDuration / kNumToneSteps);
  562. const double t = static_cast<double>(step_number) / kNumToneSteps;
  563. return kMinFrequency + t * (kMaxFrequency - kMinFrequency);
  564. }
  565. } // namespace
  566. // Tests that the SnooperNode can be asked to seek (forward or backward) its
  567. // Render() positions, as the needs of the system demand.
  568. TEST_P(SnooperNodeTest, HandlesSeekedRenderTimes) {
  569. constexpr base::TimeDelta kQuarterSecond = base::Milliseconds(250);
  570. CreateNewPipeline();
  571. // Schedule input tasks where the audio tones are changed once per second, to
  572. // allow for identifying the timing of the audio in the consumer's recording
  573. // later on.
  574. const base::TimeTicks start_time = task_runner()->NowTicks();
  575. const base::TimeTicks end_time = start_time + kTestDuration;
  576. double time_step = 1.0 / input_params().sample_rate();
  577. for (int position = 0;; position += input_params().frames_per_buffer()) {
  578. const base::TimeTicks task_time =
  579. start_time + base::Seconds(position * time_step);
  580. if (task_time >= end_time) {
  581. break;
  582. }
  583. const base::TimeTicks reference_time = task_time + kInputAdvanceTime;
  584. task_runner()->PostDelayedTask(
  585. FROM_HERE,
  586. base::BindOnce(
  587. [](FakeLoopbackGroupMember* group_member,
  588. base::TimeTicks start_time, base::TimeTicks reference_time) {
  589. group_member->SetChannelTone(
  590. FakeLoopbackGroupMember::kSetAllChannels,
  591. MapTimeOffsetToATone(reference_time - start_time));
  592. group_member->RenderMoreAudio(reference_time);
  593. },
  594. group_member(), start_time, reference_time),
  595. task_time - start_time);
  596. }
  597. // Schedule normal render tasks for the first third of the test, then skip
  598. // back a quarter-second and run for another third of the test, then skip
  599. // forward a quarter-second and run to the end.
  600. time_step = 1.0 / output_params().sample_rate();
  601. for (int position = 0;; position += output_params().frames_per_buffer()) {
  602. const base::TimeTicks task_time =
  603. start_time + base::Seconds(position * time_step);
  604. if (task_time >= end_time) {
  605. break;
  606. }
  607. base::TimeDelta time_offset = task_time - start_time;
  608. if (time_offset < (kTestDuration / 3) ||
  609. time_offset >= (kTestDuration * 2 / 3)) {
  610. time_offset = base::TimeDelta();
  611. } else {
  612. time_offset = -kQuarterSecond;
  613. }
  614. const base::TimeTicks reference_time =
  615. task_time + time_offset - output_delay();
  616. task_runner()->PostDelayedTask(
  617. FROM_HERE,
  618. base::BindOnce(&SnooperNodeTest::RenderAndConsume,
  619. base::Unretained(this), reference_time),
  620. task_time - start_time);
  621. }
  622. RunAllPendingTasks();
  623. // Examine the consumer's recorded audio for the expected audio signal: For
  624. // the first third of the test, the consumer should hear the first few tones.
  625. // Then, at the point where rendering seeked backward, there will be a
  626. // zero-fill gap for a quarter second, followed by the tone changes being
  627. // "late" by a quarter second. Finally, at the point where rendering seeked
  628. // forward, the tone changes will be shifted back again.
  629. const base::TimeDelta lead_in = kInputAdvanceTime + output_delay();
  630. for (base::TimeDelta recording_time = lead_in + kQuarterSecond;
  631. recording_time < kTestDuration; recording_time += kQuarterSecond) {
  632. const base::TimeDelta render_time = recording_time - kInputAdvanceTime;
  633. base::TimeDelta input_time =
  634. recording_time - lead_in - input_params().GetBufferDuration();
  635. // The recording is shifted forward during the middle-third of the test.
  636. if (render_time >= (kTestDuration / 3) &&
  637. render_time < (kTestDuration * 2 / 3)) {
  638. input_time -= kQuarterSecond;
  639. }
  640. const double expected_freq = MapTimeOffsetToATone(input_time);
  641. SCOPED_TRACE(testing::Message() << "recording_time=" << recording_time
  642. << ", expected_freq=" << expected_freq);
  643. const int position =
  644. output_params().sample_rate() * recording_time.InSecondsF() -
  645. output_params().frames_per_buffer();
  646. if (render_time >= (kTestDuration / 3) &&
  647. render_time < (kTestDuration / 3 + kQuarterSecond)) {
  648. // Special case: Expect the zero-fill gap immediately after the first
  649. // discontinuity.
  650. for (int ch = 0; ch < output_params().channels(); ++ch) {
  651. EXPECT_TRUE(consumer()->IsSilentInRange(
  652. ch, position - output_params().frames_per_buffer(), position));
  653. }
  654. } else {
  655. for (int ch = 0; ch < output_params().channels(); ++ch) {
  656. EXPECT_NEAR(kSourceVolume,
  657. consumer()->ComputeAmplitudeAt(ch, expected_freq, position),
  658. kSourceVolume * max_relative_error());
  659. }
  660. }
  661. }
  662. }
  663. InputAndOutputParams MakeParams(media::ChannelLayout input_channel_layout,
  664. int input_sample_rate,
  665. int input_frames_per_buffer,
  666. media::ChannelLayout output_channel_layout,
  667. int output_sample_rate,
  668. int output_frames_per_buffer) {
  669. return InputAndOutputParams{
  670. media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
  671. input_channel_layout, input_sample_rate,
  672. input_frames_per_buffer),
  673. media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
  674. output_channel_layout, output_sample_rate,
  675. output_frames_per_buffer)};
  676. }
  677. INSTANTIATE_TEST_SUITE_P(
  678. All,
  679. SnooperNodeTest,
  680. testing::Values(MakeParams(media::CHANNEL_LAYOUT_STEREO,
  681. 48000,
  682. 480,
  683. media::CHANNEL_LAYOUT_STEREO,
  684. 48000,
  685. 480),
  686. MakeParams(media::CHANNEL_LAYOUT_STEREO,
  687. 48000,
  688. 64,
  689. media::CHANNEL_LAYOUT_STEREO,
  690. 48000,
  691. 480),
  692. MakeParams(media::CHANNEL_LAYOUT_STEREO,
  693. 44100,
  694. 64,
  695. media::CHANNEL_LAYOUT_STEREO,
  696. 48000,
  697. 480),
  698. MakeParams(media::CHANNEL_LAYOUT_STEREO,
  699. 48000,
  700. 512,
  701. media::CHANNEL_LAYOUT_STEREO,
  702. 44100,
  703. 441),
  704. MakeParams(media::CHANNEL_LAYOUT_MONO,
  705. 8000,
  706. 64,
  707. media::CHANNEL_LAYOUT_STEREO,
  708. 48000,
  709. 480),
  710. MakeParams(media::CHANNEL_LAYOUT_STEREO,
  711. 48000,
  712. 480,
  713. media::CHANNEL_LAYOUT_MONO,
  714. 8000,
  715. 80)));
  716. } // namespace
  717. } // namespace audio