Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Unified Diff: media/base/audio_push_fifo_unittest.cc

Issue 1714593003: Introduce media::AudioPushFifo and a couple of use cases (and clean-ups). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix unittest compile breakage caused by recent method rename. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/base/audio_push_fifo.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/base/audio_push_fifo_unittest.cc
diff --git a/media/base/audio_push_fifo_unittest.cc b/media/base/audio_push_fifo_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8d815fd613fa2888264c63c90911aaca782780ea
--- /dev/null
+++ b/media/base/audio_push_fifo_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright 2016 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 <limits>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_push_fifo.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+class AudioPushFifoTest : public testing::TestWithParam<int> {
+ public:
+ AudioPushFifoTest() {}
+ ~AudioPushFifoTest() override {}
+
+ int output_chunk_size() const { return GetParam(); }
+
+ void SetUp() final {
+ fifo_.reset(new AudioPushFifo(base::Bind(
+ &AudioPushFifoTest::ReceiveAndCheckNextChunk, base::Unretained(this))));
+ fifo_->Reset(output_chunk_size());
+ ASSERT_EQ(output_chunk_size(), fifo_->frames_per_buffer());
+ }
+
+ protected:
+ struct OutputChunkResult {
+ int num_frames;
+ float first_sample_value;
+ float last_sample_value;
+ int frame_delay;
+ };
+
+ // Returns the number of output chunks that should have been emitted given the
+ // number of input frames pushed so far.
+ size_t GetExpectedOutputChunks(int frames_pushed) const {
+ return static_cast<size_t>(frames_pushed / output_chunk_size());
+ }
+
+ // Returns the number of Push() calls to make in order to get at least 3
+ // output chunks.
+ int GetNumPushTestIterations(int input_chunk_size) const {
+ return 3 * std::max(1, output_chunk_size() / input_chunk_size);
+ }
+
+ // Repeatedly pushes constant-sized batches of input samples and checks that
+ // the input data is re-chunked correctly.
+ void RunSimpleRechunkTest(int input_chunk_size) {
+ const int num_iterations = GetNumPushTestIterations(input_chunk_size);
+
+ int sample_value = 0;
+ const scoped_ptr<AudioBus> audio_bus =
+ AudioBus::Create(1, input_chunk_size);
+
+ for (int i = 0; i < num_iterations; ++i) {
+ EXPECT_EQ(GetExpectedOutputChunks(i * input_chunk_size), results_.size());
+
+ // Fill audio data with predictable values.
+ for (int j = 0; j < audio_bus->frames(); ++j)
+ audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
+
+ fifo_->Push(*audio_bus);
+ // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or
+ // more times.
+ }
+ EXPECT_EQ(GetExpectedOutputChunks(num_iterations * input_chunk_size),
+ results_.size());
+
+ // Confirm first and last sample values that have been output are the
+ // expected ones.
+ ASSERT_FALSE(results_.empty());
+ EXPECT_EQ(0.0f, results_.front().first_sample_value);
+ const float last_value_in_last_chunk = static_cast<float>(
+ GetExpectedOutputChunks(num_iterations * input_chunk_size) *
+ output_chunk_size() -
+ 1);
+ EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
+
+ // Confirm the expected frame delays for the first output chunk (or two).
+ if (input_chunk_size < output_chunk_size()) {
+ const int num_queued_before_first_output =
+ ((output_chunk_size() - 1) / input_chunk_size) * input_chunk_size;
+ EXPECT_EQ(-num_queued_before_first_output, results_.front().frame_delay);
+ } else if (input_chunk_size >= output_chunk_size()) {
+ EXPECT_EQ(0, results_[0].frame_delay);
+ if (input_chunk_size >= 2 * output_chunk_size()) {
+ EXPECT_EQ(output_chunk_size(), results_[1].frame_delay);
+ } else {
+ const int num_remaining_after_first_output =
+ input_chunk_size - output_chunk_size();
+ EXPECT_EQ(-num_remaining_after_first_output, results_[1].frame_delay);
+ }
+ }
+
+ const size_t num_results_before_flush = results_.size();
+ fifo_->Flush();
+ const size_t num_results_after_flush = results_.size();
+ if (num_results_after_flush > num_results_before_flush) {
+ EXPECT_NE(0, results_.back().frame_delay);
+ EXPECT_LT(-output_chunk_size(), results_.back().frame_delay);
+ }
+ }
+
+ // Returns a "random" integer in the range [begin,end).
+ int GetRandomInRange(int begin, int end) {
+ const int len = end - begin;
+ const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
+ return begin + rand_offset;
+ }
+
+ scoped_ptr<AudioPushFifo> fifo_;
+ std::vector<OutputChunkResult> results_;
+
+ private:
+ // Called by |fifo_| to deliver another chunk of audio. Sanity checks
+ // the sample values are as expected, and without any dropped/duplicated, and
+ // adds a result to |results_|.
+ void ReceiveAndCheckNextChunk(const AudioBus& audio_bus, int frame_delay) {
+ OutputChunkResult result;
+ result.num_frames = audio_bus.frames();
+ result.first_sample_value = audio_bus.channel(0)[0];
+ result.last_sample_value = audio_bus.channel(0)[audio_bus.frames() - 1];
+ result.frame_delay = frame_delay;
+
+ // Check that each sample value is the previous sample value plus one.
+ for (int i = 1; i < audio_bus.frames(); ++i) {
+ const float expected_value = result.first_sample_value + i;
+ const float actual_value = audio_bus.channel(0)[i];
+ if (actual_value != expected_value) {
+ if (actual_value == 0.0f) {
+ // This chunk is probably being emitted by a Flush(). If that's true
+ // then the frame_delay will be negative and the rest of the
+ // |audio_bus| should be all zeroes.
+ ASSERT_GT(0, frame_delay);
+ for (int j = i + 1; j < audio_bus.frames(); ++j)
+ ASSERT_EQ(0.0f, audio_bus.channel(0)[j]);
+ break;
+ } else {
+ ASSERT_EQ(expected_value, actual_value) << "Sample at offset " << i
+ << " is incorrect.";
+ }
+ }
+ }
+
+ results_.push_back(result);
+ }
+
+ // Note: Not using base::RandInt() because it is horribly slow on debug
+ // builds. The following is a very simple, deterministic LCG:
+ int NextRandomInt() {
+ rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
+ return static_cast<int>(rand_seed_);
+ }
+
+ uint32_t rand_seed_ = 0x7e110;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioPushFifoTest);
+};
+
+// Tests an atypical edge case: Push()ing one frame at a time.
+TEST_P(AudioPushFifoTest, PushOneFrameAtATime) {
+ RunSimpleRechunkTest(1);
+}
+
+// Tests that re-chunking the audio from common platform input chunk sizes
+// works.
+TEST_P(AudioPushFifoTest, Push128FramesAtATime) {
+ RunSimpleRechunkTest(128);
+}
+TEST_P(AudioPushFifoTest, Push512FramesAtATime) {
+ RunSimpleRechunkTest(512);
+}
+
+// Tests that re-chunking the audio from common "10 ms" input chunk sizes
+// works (44100 Hz * 10 ms = 441, and 48000 Hz * 10 ms = 480).
+TEST_P(AudioPushFifoTest, Push441FramesAtATime) {
+ RunSimpleRechunkTest(441);
+}
+TEST_P(AudioPushFifoTest, Push480FramesAtATime) {
+ RunSimpleRechunkTest(480);
+}
+
+// Tests that re-chunking when input audio is provided in varying chunk sizes
+// works.
+TEST_P(AudioPushFifoTest, PushArbitraryNumbersOfFramesAtATime) {
+ // The loop below will run until both: 1) kMinNumIterations loops have
+ // occurred; and 2) there are at least 3 entries in |results_|.
+ const int kMinNumIterations = 30;
+
+ int sample_value = 0;
+ int frames_pushed_so_far = 0;
+ for (int i = 0; i < kMinNumIterations || results_.size() < 3; ++i) {
+ EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
+
+ // Create an AudioBus of a random length, populated with sample values.
+ const int input_chunk_size = GetRandomInRange(1, 1920);
+ const scoped_ptr<AudioBus> audio_bus =
+ AudioBus::Create(1, input_chunk_size);
+ for (int j = 0; j < audio_bus->frames(); ++j)
+ audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
+
+ fifo_->Push(*audio_bus);
+ // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or
+ // more times.
+
+ frames_pushed_so_far += input_chunk_size;
+ }
+ EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
+
+ ASSERT_FALSE(results_.empty());
+ EXPECT_EQ(0.0f, results_.front().first_sample_value);
+ const float last_value_in_last_chunk = static_cast<float>(
+ GetExpectedOutputChunks(frames_pushed_so_far) * output_chunk_size() - 1);
+ EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
+
+ const size_t num_results_before_flush = results_.size();
+ fifo_->Flush();
+ const size_t num_results_after_flush = results_.size();
+ if (num_results_after_flush > num_results_before_flush) {
+ EXPECT_NE(0, results_.back().frame_delay);
+ EXPECT_LT(-output_chunk_size(), results_.back().frame_delay);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ AudioPushFifoTest,
+ ::testing::Values(
+ // 1 ms output chunks at common sample rates.
+ 16, // 16000 Hz
+ 22, // 22050 Hz
+ 44, // 44100 Hz
+ 48, // 48000 Hz
+
+ // 10 ms output chunks at common sample rates.
+ 160, // 16000 Hz
+ 220, // 22050 Hz
+ 441, // 44100 Hz
+ 480, // 48000 Hz
+
+ // 60 ms output chunks at common sample rates.
+ 960, // 16000 Hz
+ 1323, // 22050 Hz
+ 2646, // 44100 Hz
+ 2880 // 48000 Hz
+ ));
+
+} // namespace
+
+} // namespace media
« no previous file with comments | « media/base/audio_push_fifo.cc ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698