OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 |
| 5 #include <limits> |
| 6 #include <vector> |
| 7 |
| 8 #include "base/bind.h" |
| 9 #include "base/bind_helpers.h" |
| 10 #include "base/macros.h" |
| 11 #include "media/base/audio_bus.h" |
| 12 #include "media/base/audio_push_fifo.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 |
| 15 namespace media { |
| 16 |
| 17 namespace { |
| 18 |
| 19 class AudioPushFifoTest : public testing::TestWithParam<int> { |
| 20 public: |
| 21 AudioPushFifoTest() {} |
| 22 ~AudioPushFifoTest() override {} |
| 23 |
| 24 int output_chunk_size() const { return GetParam(); } |
| 25 |
| 26 void SetUp() final { |
| 27 fifo_.reset(new AudioPushFifo(base::Bind( |
| 28 &AudioPushFifoTest::ReceiveAndCheckNextChunk, base::Unretained(this)))); |
| 29 fifo_->Reset(output_chunk_size()); |
| 30 ASSERT_EQ(output_chunk_size(), fifo_->output_frames()); |
| 31 } |
| 32 |
| 33 protected: |
| 34 struct OutputChunkResult { |
| 35 int num_frames; |
| 36 float first_sample_value; |
| 37 float last_sample_value; |
| 38 int frame_offset; |
| 39 }; |
| 40 |
| 41 // Returns the number of output chunks that should have been emitted given the |
| 42 // number of input frames pushed so far. |
| 43 size_t GetExpectedOutputChunks(int frames_pushed) const { |
| 44 return static_cast<size_t>(frames_pushed / output_chunk_size()); |
| 45 } |
| 46 |
| 47 // Returns the number of Push() calls to make in order to get at least 3 |
| 48 // output chunks. |
| 49 int GetNumPushTestIterations(int input_chunk_size) const { |
| 50 return 3 * std::max(1, output_chunk_size() / input_chunk_size); |
| 51 } |
| 52 |
| 53 // Repeatedly pushes constant-sized batches of input samples and checks that |
| 54 // the input data is re-chunked correctly. |
| 55 void RunSimpleRechunkTest(int input_chunk_size) { |
| 56 const int num_iterations = GetNumPushTestIterations(input_chunk_size); |
| 57 |
| 58 int sample_value = 0; |
| 59 const scoped_ptr<AudioBus> audio_bus = |
| 60 AudioBus::Create(1, input_chunk_size); |
| 61 |
| 62 for (int i = 0; i < num_iterations; ++i) { |
| 63 EXPECT_EQ(GetExpectedOutputChunks(i * input_chunk_size), results_.size()); |
| 64 |
| 65 // Fill audio data with predictable values. |
| 66 for (int j = 0; j < audio_bus->frames(); ++j) |
| 67 audio_bus->channel(0)[j] = static_cast<float>(sample_value++); |
| 68 |
| 69 fifo_->Push(*audio_bus); |
| 70 // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or |
| 71 // more times. |
| 72 } |
| 73 EXPECT_EQ(GetExpectedOutputChunks(num_iterations * input_chunk_size), |
| 74 results_.size()); |
| 75 |
| 76 // Confirm first and last sample values that have been output are the |
| 77 // expected ones. |
| 78 ASSERT_FALSE(results_.empty()); |
| 79 EXPECT_EQ(0.0f, results_.front().first_sample_value); |
| 80 const float last_value_in_last_chunk = static_cast<float>( |
| 81 GetExpectedOutputChunks(num_iterations * input_chunk_size) * |
| 82 output_chunk_size() - |
| 83 1); |
| 84 EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value); |
| 85 |
| 86 // Confirm the expected frame offsets for the first output chunk (or two). |
| 87 if (input_chunk_size < output_chunk_size()) { |
| 88 const int num_queued_before_first_output = |
| 89 ((output_chunk_size() - 1) / input_chunk_size) * input_chunk_size; |
| 90 EXPECT_EQ(-num_queued_before_first_output, results_.front().frame_offset); |
| 91 } else if (input_chunk_size >= output_chunk_size()) { |
| 92 EXPECT_EQ(0, results_[0].frame_offset); |
| 93 if (input_chunk_size >= 2 * output_chunk_size()) { |
| 94 EXPECT_EQ(output_chunk_size(), results_[1].frame_offset); |
| 95 } else { |
| 96 const int num_remaining_after_first_output = |
| 97 input_chunk_size - output_chunk_size(); |
| 98 EXPECT_EQ(-num_remaining_after_first_output, results_[1].frame_offset); |
| 99 } |
| 100 } |
| 101 } |
| 102 |
| 103 // Returns a "random" integer in the range [begin,end). |
| 104 int GetRandomInRange(int begin, int end) { |
| 105 const int len = end - begin; |
| 106 const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin)); |
| 107 return begin + rand_offset; |
| 108 } |
| 109 |
| 110 scoped_ptr<AudioPushFifo> fifo_; |
| 111 std::vector<OutputChunkResult> results_; |
| 112 |
| 113 private: |
| 114 // Called by |fifo_| to deliver another chunk of audio. Sanity checks |
| 115 // the sample values are as expected, and without any dropped/duplicated, and |
| 116 // adds a result to |results_|. |
| 117 void ReceiveAndCheckNextChunk(const AudioBus& audio_bus, int frame_offset) { |
| 118 OutputChunkResult result; |
| 119 result.num_frames = audio_bus.frames(); |
| 120 result.first_sample_value = audio_bus.channel(0)[0]; |
| 121 result.last_sample_value = audio_bus.channel(0)[audio_bus.frames() - 1]; |
| 122 result.frame_offset = frame_offset; |
| 123 |
| 124 // Check that each sample value is the previous sample value plus one. |
| 125 for (int i = 1; i < audio_bus.frames(); ++i) { |
| 126 ASSERT_EQ(result.first_sample_value + i, audio_bus.channel(0)[i]) |
| 127 << "Sample at offset " << i << " is incorrect."; |
| 128 } |
| 129 |
| 130 results_.push_back(result); |
| 131 } |
| 132 |
| 133 // Note: Not using base::RandInt() because it is horribly slow on debug |
| 134 // builds. The following is a very simple, deterministic LCG: |
| 135 int NextRandomInt() { |
| 136 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31); |
| 137 return static_cast<int>(rand_seed_); |
| 138 } |
| 139 |
| 140 uint32_t rand_seed_ = 0x7e110; |
| 141 |
| 142 DISALLOW_COPY_AND_ASSIGN(AudioPushFifoTest); |
| 143 }; |
| 144 |
| 145 // Tests an atypical edge case: Push()ing one frame at a time. |
| 146 TEST_P(AudioPushFifoTest, PushOneFrameAtATime) { |
| 147 RunSimpleRechunkTest(1); |
| 148 } |
| 149 |
| 150 // Tests that re-chunking the audio from common platform input chunk sizes |
| 151 // works. |
| 152 TEST_P(AudioPushFifoTest, Push128FramesAtATime) { |
| 153 RunSimpleRechunkTest(128); |
| 154 } |
| 155 TEST_P(AudioPushFifoTest, Push512FramesAtATime) { |
| 156 RunSimpleRechunkTest(512); |
| 157 } |
| 158 |
| 159 // Tests that re-chunking the audio from common "10 ms" input chunk sizes |
| 160 // works (44100 Hz * 10 ms = 441, and 48000 Hz * 10 ms = 480). |
| 161 TEST_P(AudioPushFifoTest, Push441FramesAtATime) { |
| 162 RunSimpleRechunkTest(441); |
| 163 } |
| 164 TEST_P(AudioPushFifoTest, Push480FramesAtATime) { |
| 165 RunSimpleRechunkTest(480); |
| 166 } |
| 167 |
| 168 // Tests that re-chunking when input audio is provided in varying chunk sizes |
| 169 // works. |
| 170 TEST_P(AudioPushFifoTest, PushArbitraryNumbersOfFramesAtATime) { |
| 171 // The loop below will run until both: 1) kMinNumIterations loops have |
| 172 // occurred; and 2) there are at least 3 entries in |results_|. |
| 173 const int kMinNumIterations = 30; |
| 174 |
| 175 int sample_value = 0; |
| 176 int frames_pushed_so_far = 0; |
| 177 for (int i = 0; i < kMinNumIterations || results_.size() < 3; ++i) { |
| 178 EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size()); |
| 179 |
| 180 // Create an AudioBus of a random length, populated with sample values. |
| 181 const int input_chunk_size = GetRandomInRange(1, 1920); |
| 182 const scoped_ptr<AudioBus> audio_bus = |
| 183 AudioBus::Create(1, input_chunk_size); |
| 184 for (int j = 0; j < audio_bus->frames(); ++j) |
| 185 audio_bus->channel(0)[j] = static_cast<float>(sample_value++); |
| 186 |
| 187 fifo_->Push(*audio_bus); |
| 188 // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or |
| 189 // more times. |
| 190 |
| 191 frames_pushed_so_far += input_chunk_size; |
| 192 } |
| 193 EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size()); |
| 194 |
| 195 ASSERT_FALSE(results_.empty()); |
| 196 EXPECT_EQ(0.0f, results_.front().first_sample_value); |
| 197 const float last_value_in_last_chunk = static_cast<float>( |
| 198 GetExpectedOutputChunks(frames_pushed_so_far) * output_chunk_size() - 1); |
| 199 EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value); |
| 200 } |
| 201 |
| 202 INSTANTIATE_TEST_CASE_P(, |
| 203 AudioPushFifoTest, |
| 204 ::testing::Values( |
| 205 // 1 ms output chunks at common sample rates. |
| 206 16, // 16000 Hz |
| 207 22, // 22050 Hz |
| 208 44, // 44100 Hz |
| 209 48, // 48000 Hz |
| 210 |
| 211 // 10 ms output chunks at common sample rates. |
| 212 160, // 16000 Hz |
| 213 220, // 22050 Hz |
| 214 441, // 44100 Hz |
| 215 480, // 48000 Hz |
| 216 |
| 217 // 60 ms output chunks at common sample rates. |
| 218 960, // 16000 Hz |
| 219 1323, // 22050 Hz |
| 220 2646, // 44100 Hz |
| 221 2880 // 48000 Hz |
| 222 )); |
| 223 |
| 224 } // namespace |
| 225 |
| 226 } // namespace media |
OLD | NEW |