Use max bitrate in Representation@bandwidth instead of average bitrate
According to DASH spec (23009-1:2014): Consider a hypothetical constant bitrate channel of bandwidth with the value of this attribute in bits per second (bps). Then, if the Representation is continuously delivered at this bitrate, starting at any SAP that is indicated either by @startwithsap or by any Segment Index box, a client can be assured of having enough data for continuous playout providing playout begins after @minbuffertime * @bandwidth bits have been received (i.e. at time @minbuffertime after the first bit is received). For dependent Representations this value specifies the bandwidth according to the above definition for the aggregation of this Representation and all complementary Representations. This suggests that max bitrate should be used instead of average bitrate. Also cleaned up BandwidthEstimator code. Fixes #376. Change-Id: Ibf5896394c5c6bb820849771a2129c59202d2273
This commit is contained in:
parent
64c2ad7d6e
commit
43be3c8e8b
|
@ -3,7 +3,7 @@
|
|||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="dynamic" publishTime="some_time" availabilityStartTime="some_time" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
|
||||
<Period id="0" start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="854753" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="978382" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||
|
@ -18,7 +18,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="124634" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="134015" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<Representation id="0" bandwidth="854753" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="978382" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="32032"/>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<Representation id="1" bandwidth="124634" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="134015" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"/>
|
||||
<Representation id="0" bandwidth="855149" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="978958" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="32032"/>
|
||||
|
@ -18,7 +18,7 @@
|
|||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"/>
|
||||
<Representation id="1" bandwidth="125122" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="134592" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="dynamic" publishTime="some_time" availabilityStartTime="some_time" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
|
||||
<Period id="0" start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="855607" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="979373" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"/>
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
|
@ -16,7 +16,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="125598" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="135009" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"/>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"/>
|
||||
<Representation id="0" bandwidth="855607" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="979373" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="32032"/>
|
||||
|
@ -18,7 +18,7 @@
|
|||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"/>
|
||||
<Representation id="1" bandwidth="125598" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="135009" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="dynamic" publishTime="some_time" availabilityStartTime="some_time" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
|
||||
<Period id="0" start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="852255" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="974122" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="32032"/>
|
||||
|
@ -14,7 +14,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="122308" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="131009" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.802799940109253S">
|
||||
<Period id="0" duration="PT2.0687333333333333S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="844102" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="974122" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="32032"/>
|
||||
|
@ -13,7 +13,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="143117" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="175884" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.802799940109253S">
|
||||
<Period id="0">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="852255" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<Representation id="0" bandwidth="974122" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="32032"/>
|
||||
|
@ -14,7 +14,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="122308" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="131009" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -7,57 +7,68 @@
|
|||
#include "packager/mpd/base/bandwidth_estimator.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#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<int>(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<int>(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<int>(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<uint64_t>(ceil(num_blocks / harmonic_mean_denominator_));
|
||||
return sliding_queue_.size() == 0
|
||||
? 0
|
||||
: static_cast<uint64_t>(
|
||||
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<uint64_t>(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
|
||||
|
|
|
@ -10,34 +10,76 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <list>
|
||||
#include <deque>
|
||||
|
||||
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<double> 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<double> 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<double> min_;
|
||||
};
|
||||
SlidingQueue sliding_queue_;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
||||
#endif // MPD_BASE_BANDWIDTH_ESTIMATOR_H_
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -242,7 +242,7 @@ xml::scoped_xml_ptr<xmlNode> 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_)));
|
||||
|
||||
|
|
|
@ -470,7 +470,7 @@ class SegmentTemplateTest : public RepresentationTest {
|
|||
" </SegmentTimeline>\n"
|
||||
" </SegmentTemplate>\n"
|
||||
"</Representation>\n";
|
||||
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Estimate(),
|
||||
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(),
|
||||
expected_s_elements_.c_str());
|
||||
}
|
||||
|
||||
|
@ -730,7 +730,7 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
|||
" </SegmentTemplate>\n"
|
||||
"</Representation>\n";
|
||||
|
||||
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Estimate(),
|
||||
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(),
|
||||
expected_start_number,
|
||||
expected_s_element.c_str());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue