diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd
index 9849d82ef7..cf91185d7a 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd
@@ -7,7 +7,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
@@ -22,7 +22,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd
index 41e66c930f..76eddfd8d1 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd
@@ -3,7 +3,7 @@
-
+
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
@@ -18,7 +18,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd
index e9af6691d3..9d67db21b3 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd
@@ -5,7 +5,7 @@
-
+
@@ -18,7 +18,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd
index 59ef0f3a3d..cb99e314b4 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd
@@ -5,7 +5,7 @@
-
+
@@ -18,7 +18,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd
index 1244a592b8..682fa433ac 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd
@@ -3,7 +3,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd
index 5a0e4cdfca..99bca76bdb 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd
@@ -3,7 +3,7 @@
-
+
@@ -14,7 +14,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd
index 72f56ed30c..15c92e2adf 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd
@@ -3,7 +3,7 @@
-
+
@@ -13,7 +13,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd
index a4c435e0b0..7b5e2ead31 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd
@@ -3,7 +3,7 @@
-
+
@@ -14,7 +14,7 @@
-
+
diff --git a/packager/mpd/base/bandwidth_estimator.cc b/packager/mpd/base/bandwidth_estimator.cc
index 1f8d6fd37d..38cf42edb0 100644
--- a/packager/mpd/base/bandwidth_estimator.cc
+++ b/packager/mpd/base/bandwidth_estimator.cc
@@ -7,57 +7,68 @@
#include "packager/mpd/base/bandwidth_estimator.h"
#include
-#include
#include "packager/base/logging.h"
-const int BandwidthEstimator::kUseAllBlocks = 0;
+namespace shaka {
-BandwidthEstimator::BandwidthEstimator(int num_blocks)
- : num_blocks_for_estimation_(num_blocks),
- harmonic_mean_denominator_(0.0),
- num_blocks_added_(0) {}
+BandwidthEstimator::BandwidthEstimator(size_t num_blocks)
+ : sliding_queue_(num_blocks) {}
BandwidthEstimator::~BandwidthEstimator() {}
void BandwidthEstimator::AddBlock(uint64_t size, double duration) {
DCHECK_GT(duration, 0.0);
DCHECK_GT(size, 0u);
- if (num_blocks_for_estimation_ < 0 &&
- static_cast(history_.size()) >= -1 * num_blocks_for_estimation_) {
- // Short circuiting the case where |num_blocks_for_estimation_| number of
- // blocks have been added already.
- return;
- }
-
const int kBitsInByte = 8;
const double bits_per_second_reciprocal = duration / (kBitsInByte * size);
- harmonic_mean_denominator_ += bits_per_second_reciprocal;
- if (num_blocks_for_estimation_ == kUseAllBlocks) {
- DCHECK_EQ(history_.size(), 0u);
- ++num_blocks_added_;
- return;
- }
-
- history_.push_back(bits_per_second_reciprocal);
- if (num_blocks_for_estimation_ > 0 &&
- static_cast(history_.size()) > num_blocks_for_estimation_) {
- harmonic_mean_denominator_ -= history_.front();
- history_.pop_front();
- }
-
- DCHECK_NE(num_blocks_for_estimation_, kUseAllBlocks);
- DCHECK_LE(static_cast(history_.size()), abs(num_blocks_for_estimation_));
- DCHECK_EQ(num_blocks_added_, 0u);
- return;
+ sliding_queue_.Add(bits_per_second_reciprocal);
}
uint64_t BandwidthEstimator::Estimate() const {
- if (harmonic_mean_denominator_ == 0.0)
- return 0;
-
- const uint64_t num_blocks = num_blocks_for_estimation_ == kUseAllBlocks
- ? num_blocks_added_
- : history_.size();
- return static_cast(ceil(num_blocks / harmonic_mean_denominator_));
+ return sliding_queue_.size() == 0
+ ? 0
+ : static_cast(
+ ceil(sliding_queue_.size() / sliding_queue_.sum()));
}
+
+uint64_t BandwidthEstimator::Max() const {
+ // The first element has minimum "bits per second reciprocal", thus the
+ // reverse is maximum "bits per second".
+ return sliding_queue_.size() == 0
+ ? 0
+ : static_cast(ceil(1 / sliding_queue_.min()));
+}
+
+BandwidthEstimator::SlidingQueue::SlidingQueue(size_t window_size)
+ : window_size_(window_size) {}
+
+void BandwidthEstimator::SlidingQueue::Add(double value) {
+ // Remove elements if needed to form a monotonic non-decreasing sequence.
+ while (!min_.empty() && min_.back() > value)
+ min_.pop_back();
+ min_.push_back(value);
+
+ if (window_size_ == kUseAllBlocks) {
+ size_++;
+ sum_ += value;
+ min_.resize(1); // Keep only the minimum one.
+ return;
+ }
+
+ window_.push_back(value);
+ sum_ += value;
+
+ if (window_.size() <= window_size_) {
+ size_++;
+ return;
+ }
+
+ if (min_.front() == window_.front())
+ min_.pop_front();
+
+ sum_ -= window_.front();
+ window_.pop_front();
+}
+
+} // namespace shaka
diff --git a/packager/mpd/base/bandwidth_estimator.h b/packager/mpd/base/bandwidth_estimator.h
index c82b59f47d..e7f59e2c55 100644
--- a/packager/mpd/base/bandwidth_estimator.h
+++ b/packager/mpd/base/bandwidth_estimator.h
@@ -10,34 +10,76 @@
#include
#include
-#include
+#include
+
+namespace shaka {
class BandwidthEstimator {
public:
- /// @param num_blocks is the number of latest blocks to use. Negative values
- /// use first N blocks. 0 uses all.
- explicit BandwidthEstimator(int num_blocks);
+ /// @param num_blocks is the number of latest blocks to use. 0 uses all.
+ static constexpr size_t kUseAllBlocks = 0;
+ explicit BandwidthEstimator(size_t num_blocks);
~BandwidthEstimator();
- // @param size is the size of the block in bytes. Should be positive.
- // @param duration is the length in seconds. Should be positive.
+ /// @param size is the size of the block in bytes. Should be positive.
+ /// @param duration is the length in seconds. Should be positive.
void AddBlock(uint64_t size, double duration);
- // @return The estimate bandwidth, in bits per second, from the harmonic mean
- // of the number of blocks specified in the constructor. The value is
- // rounded up to the nearest integer.
+ /// @return The estimate bandwidth, in bits per second, from the harmonic mean
+ /// of the number of blocks specified in the constructor. The value is
+ /// rounded up to the nearest integer.
uint64_t Estimate() const;
- static const int kUseAllBlocks;
+ /// @return The max bandwidth, in bits per second, of the number of blocks
+ /// specified in the constructor. The value is rounded up to the
+ /// nearest integer.
+ uint64_t Max() const;
private:
- const int num_blocks_for_estimation_;
- double harmonic_mean_denominator_;
+ BandwidthEstimator(const BandwidthEstimator&) = delete;
+ BandwidthEstimator& operator=(const BandwidthEstimator&) = delete;
- // This is not used when num_blocks_for_estimation_ != 0. Therefore it should
- // always be 0 if num_blocks_for_estimation_ != 0.
- size_t num_blocks_added_;
- std::list history_;
+ // A sliding queue that provide convenient functions to get the minimum value
+ // and the sum when window slides.
+ class SlidingQueue {
+ public:
+ // |window_size| defines the size of the sliding window. 0 uses all.
+ explicit SlidingQueue(size_t window_size);
+
+ // Add a new value. Old values may be moved out.
+ void Add(double value);
+
+ // Return the sum of the values in the sliding window.
+ double sum() const { return sum_; }
+ // Return the number of values in the sliding window.
+ double size() const { return size_; }
+ // Return the minimum value of the values in the sliding window.
+ double min() const { return min_.front(); }
+
+ private:
+ SlidingQueue(const SlidingQueue&) = delete;
+ SlidingQueue& operator=(const SlidingQueue&) = delete;
+
+ const size_t window_size_;
+ size_t size_ = 0;
+ double sum_ = 0;
+ // Keeps track of the values in the sliding window. Not needed if
+ // |window_size| is kUseAllBlocks.
+ std::deque window_;
+ // Keeps track of a monotonic non-decreasing sequence of values, i.e.
+ // local minimum values in the sliding window. The front() is always the
+ // global minimum, i.e. the minimum value in the sliding window.
+ // This is achieved through:
+ // (1) New value is added to the back with the original values before it
+ // that are larger removed as they are no longer useful.
+ // (2) When a value is removed from |window_|, if it is the minimum value,
+ // it is also removed from |min_|; if it is not, it means the value is not
+ // present in |min_|.
+ std::deque min_;
+ };
+ SlidingQueue sliding_queue_;
};
+} // namespace shaka
+
#endif // MPD_BASE_BANDWIDTH_ESTIMATOR_H_
diff --git a/packager/mpd/base/bandwidth_estimator_unittest.cc b/packager/mpd/base/bandwidth_estimator_unittest.cc
index 947756c21c..ad24cc85e2 100644
--- a/packager/mpd/base/bandwidth_estimator_unittest.cc
+++ b/packager/mpd/base/bandwidth_estimator_unittest.cc
@@ -14,10 +14,15 @@
namespace shaka {
namespace {
-const int kNumBlocksForEstimate = 5;
-const int kFirstOneBlockForEstimate = -1;
+const size_t kNumBlocksForEstimate = 5;
const uint64_t kBitsInByte = 8;
const int kEstimateRoundError = 1;
+
+struct Bandwidth {
+ uint64_t average;
+ uint64_t max;
+};
+
} // namespace
// Make sure that averaging of 5 blocks works, and also when there aren't all 5
@@ -25,23 +30,20 @@ const int kEstimateRoundError = 1;
TEST(BandwidthEstimatorTest, FiveBlocksFiveBlocksAdded) {
BandwidthEstimator be(kNumBlocksForEstimate);
const double kDuration = 1.0;
- const uint64_t kExpectedResults[] = {
+ const Bandwidth kExpectedResults[] = {
// Harmonic mean of [1 * 8], [1 * 8, 2 * 8], ...
// 8 is the number of bits in a byte and 1, 2, ... is from the loop
// counter below.
// Note that these are rounded up.
- 8,
- 11,
- 14,
- 16,
- 18
+ {8, 8}, {11, 2 * 8}, {14, 3 * 8}, {16, 4 * 8}, {18, 5 * 8},
};
static_assert(kNumBlocksForEstimate == arraysize(kExpectedResults),
"incorrect_number_of_expectations");
for (uint64_t i = 1; i <= arraysize(kExpectedResults); ++i) {
be.AddBlock(i, kDuration);
- EXPECT_EQ(kExpectedResults[i - 1], be.Estimate());
+ EXPECT_EQ(kExpectedResults[i - 1].average, be.Estimate());
+ EXPECT_EQ(kExpectedResults[i - 1].max, be.Max());
}
}
@@ -65,9 +67,10 @@ TEST(BandwidthEstimatorTest, FiveBlocksNormal) {
}
EXPECT_NEAR(kExptectedEstimate, be.Estimate(), kEstimateRoundError);
+ // All blocks are of the same bitrate, so Max is the same as average.
+ EXPECT_NEAR(kExptectedEstimate, be.Max(), kEstimateRoundError);
}
-// Average all the blocks!
TEST(BandwidthEstimatorTest, AllBlocks) {
BandwidthEstimator be(BandwidthEstimator::kUseAllBlocks);
const uint64_t kNumBlocksToAdd = 100;
@@ -78,19 +81,39 @@ TEST(BandwidthEstimatorTest, AllBlocks) {
// The harmonic mean of 8, 16, ... , 800; rounded up.
const uint64_t kExptectedEstimate = 155;
EXPECT_EQ(kExptectedEstimate, be.Estimate());
+ const uint64_t kMax = 100 * 8;
+ EXPECT_EQ(kMax, be.Max());
}
-// Use only the first one.
-TEST(BandwidthEstimatorTest, FirstOneBlock) {
- BandwidthEstimator be(kFirstOneBlockForEstimate);
- const double kDuration = 11.0;
- const uint64_t kExptectedEstimate = 123456;
- be.AddBlock(kExptectedEstimate * kDuration / kBitsInByte, kDuration);
+TEST(BandwidthEstimatorTest, MaxWithSlidingWindow) {
+ BandwidthEstimator be(kNumBlocksForEstimate);
+ const double kDuration = 1.0 * kBitsInByte;
- // Anything. Should be ignored.
- for (int i = 0; i < 1000; ++i)
- be.AddBlock(100000, 10);
- EXPECT_EQ(kExptectedEstimate, be.Estimate());
+ // clang-format off
+ const uint64_t kSizes[] = {
+ // Sequence 1: Monotonic decreasing.
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
+ // Sequence 2: Monotonic increasing.
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ // Sequence 3: Random sequence.
+ 10, 1, 9, 6, 9, 5, 4, 9, 7, 8,
+ };
+ const uint64_t kExpectedMaxes[] = {
+ // Sequence 1.
+ 10, 10, 10, 10, 10, 9, 8, 7, 6, 5,
+ // Sequence 2.
+ 4, 3, 3, 4, 5, 6, 7, 8, 9, 10,
+ // Sequence 3.
+ 10, 10, 10, 10, 10, 9, 9, 9, 9, 9,
+ };
+ // clang-format on
+
+ static_assert(arraysize(kSizes) == arraysize(kExpectedMaxes),
+ "incorrect_number_of_expectations");
+ for (size_t i = 0; i < arraysize(kSizes); ++i) {
+ be.AddBlock(kSizes[i], kDuration);
+ EXPECT_EQ(kExpectedMaxes[i], be.Max());
+ }
}
} // namespace shaka
diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc
index 49a63a7592..fbfc95c43a 100644
--- a/packager/mpd/base/representation.cc
+++ b/packager/mpd/base/representation.cc
@@ -240,7 +240,7 @@ xml::scoped_xml_ptr Representation::GetXml() {
const uint64_t bandwidth = media_info_.has_bandwidth()
? media_info_.bandwidth()
- : bandwidth_estimator_.Estimate();
+ : bandwidth_estimator_.Max();
DCHECK(!(HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)));
diff --git a/packager/mpd/base/representation_unittest.cc b/packager/mpd/base/representation_unittest.cc
index 8210e39095..a318de76ae 100644
--- a/packager/mpd/base/representation_unittest.cc
+++ b/packager/mpd/base/representation_unittest.cc
@@ -461,7 +461,7 @@ class SegmentTemplateTest : public RepresentationTest {
"
\n"
" \n"
"\n";
- return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Estimate(),
+ return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(),
expected_s_elements_.c_str());
}
@@ -721,7 +721,7 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
" \n"
"\n";
- return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Estimate(),
+ return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(),
expected_start_number,
expected_s_element.c_str());
}