| Index: content/renderer/media/repetition_detector_unittest.cc
|
| diff --git a/content/renderer/media/repetition_detector_unittest.cc b/content/renderer/media/repetition_detector_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0a0cd26625af8f1f694fbc1f025eb1d264e38285
|
| --- /dev/null
|
| +++ b/content/renderer/media/repetition_detector_unittest.cc
|
| @@ -0,0 +1,382 @@
|
| +// Copyright 2015 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 <map>
|
| +
|
| +#include "base/macros.h" // arraysize
|
| +#include "base/rand_util.h" // RandDouble
|
| +#include "content/renderer/media/repetition_detector.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace content {
|
| +
|
| +class RepetitionDetectorForTest : public RepetitionDetector {
|
| + public:
|
| + int GetCount(int id) {
|
| + auto it = counters_.find(id);
|
| + if (it == counters_.end()) {
|
| + return 0;
|
| + }
|
| + return counters_[id];
|
| + }
|
| +
|
| + void ResetCounters() {
|
| + for (auto& item : counters_) {
|
| + item.second = 0;
|
| + }
|
| + }
|
| +
|
| + void ResetRepetitionPattern(const RepetitionDetector::Pattern* patterns,
|
| + size_t num_patterns) {
|
| + states_.clear();
|
| + RegisterRepetitionPatterns(patterns, num_patterns);
|
| + }
|
| +
|
| + void set_max_frames(size_t max_frames) { max_frames_ = max_frames; }
|
| +
|
| + private:
|
| + void ReportRepetition(int id) override {
|
| + auto it = counters_.find(id);
|
| + if (it == counters_.end()) {
|
| + counters_[id] = 0;
|
| + }
|
| + counters_[id]++;
|
| + }
|
| +
|
| + std::map<int, size_t> counters_;
|
| +};
|
| +
|
| +class RepetitionDetectorTest : public ::testing::Test {
|
| + protected:
|
| + struct ExpectedCount {
|
| + int id_;
|
| + int count_;
|
| + };
|
| +
|
| + // Verify if the counts on the repetition patterns match expectation after
|
| + // injecting a signal. No reset on the counters
|
| + void Verify(const ExpectedCount* expected_counts, size_t num_patterns,
|
| + const float* tester, size_t num_frames,
|
| + int sample_rate_hz, size_t channels = 1) {
|
| + detector_.Detect(tester, num_frames, channels, sample_rate_hz);
|
| + int id;
|
| + for (size_t idx = 0; idx < num_patterns; idx++) {
|
| + id = expected_counts[idx].id_;
|
| + EXPECT_EQ(expected_counts[idx].count_, detector_.GetCount(id)) <<
|
| + "Repetition #" << id << " counted wrong.";
|
| + }
|
| + }
|
| +
|
| + void VerifyStereo(const ExpectedCount* expected_counts, size_t num_patterns,
|
| + const float* tester, size_t num_frames,
|
| + int sample_rate_hz) {
|
| + const size_t kNumChannels = 2;
|
| +
|
| + // Get memory to store interleaved stereo.
|
| + scoped_ptr<float[]> tester_stereo(
|
| + new float[num_frames * kNumChannels]);
|
| +
|
| + for (size_t idx = 0; idx < num_frames; ++idx, ++tester) {
|
| + for (size_t channel = 0; channel < kNumChannels; ++channel) {
|
| + tester_stereo[idx * kNumChannels + channel] = *tester;
|
| + }
|
| + }
|
| +
|
| + Verify(expected_counts, num_patterns, tester_stereo.get(),
|
| + num_frames, sample_rate_hz, kNumChannels);
|
| + }
|
| +
|
| + void ResetRepetitionPattern(const RepetitionDetector::Pattern* patterns,
|
| + size_t num_patterns) {
|
| + detector_.ResetRepetitionPattern(patterns, num_patterns);
|
| + }
|
| +
|
| + void SetMaxFrames(size_t max_frames) {
|
| + detector_.set_max_frames(max_frames);
|
| + }
|
| +
|
| + void ResetCounters() {
|
| + detector_.ResetCounters();
|
| + }
|
| +
|
| + private:
|
| + RepetitionDetectorForTest detector_;
|
| +};
|
| +
|
| +TEST_F(RepetitionDetectorTest, Basic) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3}
|
| + };
|
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3};
|
| + const ExpectedCount kExpectedCounts_1[] = {
|
| + {0, 1}
|
| + };
|
| + const ExpectedCount kExpectedCounts_2[] = {
|
| + {0, 1}
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + ResetCounters();
|
| +
|
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, StereoOutOfSync) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3}
|
| + };
|
| + const float kTestSignal[] = {
|
| + 1, 1,
|
| + 2, 2,
|
| + 3, 3,
|
| + 1, 1,
|
| + 2, 2,
|
| + 3, 1};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + {0, 0}
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal) / 2, kSampleRateHz, 2);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, IncompletePattern) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3},
|
| + };
|
| + const float kTestSignal[] = {1, 2, 1, 2, 3, 1, 2, 3};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + {0, 1},
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, PatternLongerThanFrame) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 6, 6},
|
| + };
|
| + const float kTestSignal_1[] = {1, 2, 3, 4, 5};
|
| + const float kTestSignal_2[] = {6, 1, 2, 3, 4, 5, 6};
|
| + const ExpectedCount kExpectedCounts_1[] = {
|
| + {0, 0},
|
| + };
|
| + const ExpectedCount kExpectedCounts_2[] = {
|
| + {0, 1},
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1,
|
| + arraysize(kTestSignal_1), kSampleRateHz);
|
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2,
|
| + arraysize(kTestSignal_2), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1,
|
| + arraysize(kTestSignal_1), kSampleRateHz);
|
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2,
|
| + arraysize(kTestSignal_2), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, TwoPatterns) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3},
|
| + {1, 4, 4},
|
| + };
|
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + // 1,2,3 belongs to both patterns.
|
| + {0, 1},
|
| + {1, 1}
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, MaxFramesShorterThanInput) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3},
|
| + {1, 4, 4},
|
| + };
|
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + // 1,2,3 belongs to both patterns.
|
| + {0, 1},
|
| + {1, 1}
|
| + };
|
| +
|
| + // length of kTestSignal is 11 but I set maximum frames to be 2. The detection
|
| + // should still work.
|
| + SetMaxFrames(2);
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, NestedPatterns) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3},
|
| + {1, 6, 6}, // When a triplet repeated 3 times, this is triggered.
|
| + };
|
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3};
|
| + const ExpectedCount kExpectedCounts_1[] = {
|
| + {0, 1},
|
| + {1, 0}
|
| + };
|
| + const ExpectedCount kExpectedCounts_2[] = {
|
| + {0, 1},
|
| + {1, 1}
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, NotFullLengthPattern) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 4, 3},
|
| + };
|
| + const float kTestSignal[] = {1, 2, 3, -1, 1, 2, 3, -2};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + {0, 1},
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal,
|
| + arraysize(kTestSignal), kSampleRateHz);
|
| +}
|
| +
|
| +TEST_F(RepetitionDetectorTest, ZerosCountOrNot) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::Pattern kRepetitionPatterns[] = {
|
| + // id, look_back_ms, min_length_ms
|
| + {0, 3, 3},
|
| + };
|
| + const float kTestSignal_1[] = {0, 0, 0, 0, 0, 0};
|
| + const float kTestSignal_2[] = {0, 1, 2, 0, 1, 2};
|
| + const ExpectedCount kExpectedCounts_1[] = {
|
| + // Full zeros won't count.
|
| + {0, 0},
|
| + };
|
| + const ExpectedCount kExpectedCounts_2[] = {
|
| + // Partial zero will count.
|
| + {0, 1},
|
| + };
|
| +
|
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns));
|
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1,
|
| + arraysize(kTestSignal_1), kSampleRateHz);
|
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2,
|
| + arraysize(kTestSignal_2), kSampleRateHz);
|
| + ResetCounters();
|
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1,
|
| + arraysize(kTestSignal_1), kSampleRateHz);
|
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2,
|
| + arraysize(kTestSignal_2), kSampleRateHz);
|
| +}
|
| +
|
| +// Previous tests use short signal to test the detection algorithm, this one
|
| +// tests the built-in pattern in RepetitionDetector.
|
| +TEST_F(RepetitionDetectorTest, BuiltInPattern) {
|
| + const int kSampleRateHz = 44100;
|
| + // Let the signal be "*(4ms)-A(13ms)-*(100ms)-A", where * denotes random
|
| + // samples.
|
| + const size_t kPreSamples = kSampleRateHz * 4 / 1000;
|
| + const size_t kRepSamples = kSampleRateHz * 13 / 1000;
|
| + const size_t kSkipSamples = kSampleRateHz * 100 / 1000;
|
| + const size_t kSamples = kPreSamples + kRepSamples * 2 + kSkipSamples;
|
| +
|
| + float test_signal[kSamples];
|
| + size_t idx = 0;
|
| + for (; idx < kPreSamples + kRepSamples + kSkipSamples; ++idx) {
|
| + test_signal[idx] = static_cast<float>(base::RandDouble());
|
| + }
|
| + for (; idx < kSamples; ++idx) {
|
| + test_signal[idx] = test_signal[idx - kSkipSamples];
|
| + }
|
| +
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + // Partial zero will count.
|
| + {1, 0}, // 10 ms look back
|
| + {2, 0}, // 20 ms look back
|
| + {3, 0}, // 30 ms look back
|
| + {4, 0}, // 40 ms look back
|
| + {5, 0}, // 50 ms look back
|
| + {6, 0}, // 60 ms look back
|
| + {7, 0}, // 70 ms look back
|
| + {8, 0}, // 80 ms look back
|
| + {9, 0}, // 90 ms look back
|
| + {10, 1}, // 100 ms look back
|
| + {20, 0} // 200 ms look back
|
| + };
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), test_signal, kSamples,
|
| + kSampleRateHz);
|
| +}
|
| +
|
| +} // namespace content
|
|
|