Add MovingMedianFilter to rtc_base/numerics
This class will be used for filtering remote clock offset in rtp streams.
It is a separate wrapper around PercentileFilter because it will be used
in that form in several places.
Bug: webrtc:8468
Change-Id: If1f6c38ac1ffa02232c1aed5512b92878b1c346a
Reviewed-on: https://webrtc-review.googlesource.com/17841
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20531}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 775fd14..400efb6 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -402,6 +402,7 @@
sources = [
"numerics/exp_filter.cc",
"numerics/exp_filter.h",
+ "numerics/moving_median_filter.h",
"numerics/percentile_filter.h",
"numerics/sequence_number_util.h",
]
@@ -997,6 +998,7 @@
}
sources = [
"numerics/exp_filter_unittest.cc",
+ "numerics/moving_median_filter_unittest.cc",
"numerics/percentile_filter_unittest.cc",
"numerics/sequence_number_util_unittest.cc",
]
diff --git a/rtc_base/numerics/moving_median_filter.h b/rtc_base/numerics/moving_median_filter.h
new file mode 100644
index 0000000..4b42d4a
--- /dev/null
+++ b/rtc_base/numerics/moving_median_filter.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_NUMERICS_MOVING_MEDIAN_FILTER_H_
+#define RTC_BASE_NUMERICS_MOVING_MEDIAN_FILTER_H_
+
+#include <list>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/constructormagic.h"
+#include "rtc_base/numerics/percentile_filter.h"
+
+namespace webrtc {
+
+// Class to efficiently get moving median filter from a stream of samples.
+template <typename T>
+class MovingMedianFilter {
+ public:
+ // Construct filter. |window_size| is how many latest samples are stored and
+ // used to take median. |window_size| must be positive.
+ explicit MovingMedianFilter(size_t window_size);
+
+ // Insert a new sample.
+ void Insert(const T& value);
+
+ // Get median over the latest window.
+ T GetFilteredValue() const;
+
+ private:
+ PercentileFilter<T> percentile_filter_;
+ std::list<T> samples_;
+ size_t samples_stored_;
+ const size_t window_size_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(MovingMedianFilter);
+};
+
+template <typename T>
+MovingMedianFilter<T>::MovingMedianFilter(size_t window_size)
+ : percentile_filter_(0.5f), samples_stored_(0), window_size_(window_size) {
+ RTC_CHECK_GT(window_size, 0);
+}
+
+template <typename T>
+void MovingMedianFilter<T>::Insert(const T& value) {
+ percentile_filter_.Insert(value);
+ samples_.emplace_back(value);
+ ++samples_stored_;
+ if (samples_stored_ > window_size_) {
+ percentile_filter_.Erase(samples_.front());
+ samples_.pop_front();
+ --samples_stored_;
+ }
+}
+
+template <typename T>
+T MovingMedianFilter<T>::GetFilteredValue() const {
+ return percentile_filter_.GetPercentileValue();
+}
+
+} // namespace webrtc
+#endif // RTC_BASE_NUMERICS_MOVING_MEDIAN_FILTER_H_
diff --git a/rtc_base/numerics/moving_median_filter_unittest.cc b/rtc_base/numerics/moving_median_filter_unittest.cc
new file mode 100644
index 0000000..5a6eb3d
--- /dev/null
+++ b/rtc_base/numerics/moving_median_filter_unittest.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/numerics/moving_median_filter.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(MovingMedianFilterTest, ProcessesNoSamples) {
+ MovingMedianFilter<int> filter(2);
+ EXPECT_EQ(0, filter.GetFilteredValue());
+}
+
+TEST(MovingMedianFilterTest, ReturnsMovingMedianWindow5) {
+ MovingMedianFilter<int> filter(5);
+ const int64_t kSamples[5] = {1, 5, 2, 3, 4};
+ const int64_t kExpectedFilteredValues[5] = {1, 1, 2, 2, 3};
+ for (int i = 0; i < 5; ++i) {
+ filter.Insert(kSamples[i]);
+ EXPECT_EQ(kExpectedFilteredValues[i], filter.GetFilteredValue());
+ }
+}
+
+TEST(MovingMedianFilterTest, ReturnsMovingMedianWindow3) {
+ MovingMedianFilter<int> filter(3);
+ const int64_t kSamples[5] = {1, 5, 2, 3, 4};
+ const int64_t kExpectedFilteredValues[5] = {1, 1, 2, 3, 3};
+ for (int i = 0; i < 5; ++i) {
+ filter.Insert(kSamples[i]);
+ EXPECT_EQ(kExpectedFilteredValues[i], filter.GetFilteredValue());
+ }
+}
+
+TEST(MovingMedianFilterTest, ReturnsMovingMedianWindow1) {
+ MovingMedianFilter<int> filter(1);
+ const int64_t kSamples[5] = {1, 5, 2, 3, 4};
+ const int64_t kExpectedFilteredValues[5] = {1, 5, 2, 3, 4};
+ for (int i = 0; i < 5; ++i) {
+ filter.Insert(kSamples[i]);
+ EXPECT_EQ(kExpectedFilteredValues[i], filter.GetFilteredValue());
+ }
+}
+
+} // namespace webrtc