2022-08-26 15:44:59 +00:00
|
|
|
// Copyright 2014 Google LLC. All rights reserved.
|
2014-05-22 02:16:17 +00:00
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
2023-10-10 23:51:11 +00:00
|
|
|
#include <packager/mpd/base/bandwidth_estimator.h>
|
2014-05-22 02:16:17 +00:00
|
|
|
|
2018-06-22 23:19:30 +00:00
|
|
|
#include <algorithm>
|
2014-05-22 02:16:17 +00:00
|
|
|
#include <cmath>
|
2019-07-11 21:32:10 +00:00
|
|
|
#include <numeric>
|
2014-05-22 02:16:17 +00:00
|
|
|
|
2023-10-13 19:42:47 +00:00
|
|
|
#include <absl/log/check.h>
|
|
|
|
#include <absl/log/log.h>
|
|
|
|
|
2023-10-14 16:36:01 +00:00
|
|
|
#include <packager/macros/logging.h>
|
2014-05-22 02:16:17 +00:00
|
|
|
|
2018-04-13 18:48:11 +00:00
|
|
|
namespace shaka {
|
2014-05-22 02:16:17 +00:00
|
|
|
|
2019-07-11 21:32:10 +00:00
|
|
|
BandwidthEstimator::BandwidthEstimator() = default;
|
2018-11-14 20:47:48 +00:00
|
|
|
|
2018-06-22 23:19:30 +00:00
|
|
|
BandwidthEstimator::~BandwidthEstimator() = default;
|
2014-05-22 02:16:17 +00:00
|
|
|
|
2018-06-22 23:19:30 +00:00
|
|
|
void BandwidthEstimator::AddBlock(uint64_t size_in_bytes, double duration) {
|
|
|
|
if (size_in_bytes == 0 || duration == 0) {
|
|
|
|
LOG(WARNING) << "Ignore block with size=" << size_in_bytes
|
2018-05-31 01:28:16 +00:00
|
|
|
<< ", duration=" << duration;
|
|
|
|
return;
|
|
|
|
}
|
2018-06-22 23:19:30 +00:00
|
|
|
|
2014-05-22 02:16:17 +00:00
|
|
|
const int kBitsInByte = 8;
|
2018-06-22 23:19:30 +00:00
|
|
|
const uint64_t size_in_bits = size_in_bytes * kBitsInByte;
|
|
|
|
total_size_in_bits_ += size_in_bits;
|
|
|
|
total_duration_ += duration;
|
|
|
|
|
2019-07-11 21:32:10 +00:00
|
|
|
const size_t kTargetDurationThreshold = 10;
|
|
|
|
if (initial_blocks_.size() < kTargetDurationThreshold) {
|
|
|
|
initial_blocks_.push_back({size_in_bits, duration});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target_block_duration_ == 0) {
|
|
|
|
// Use the average duration as the target block duration. It will be used
|
|
|
|
// to filter small blocks from bandwidth calculation.
|
|
|
|
target_block_duration_ = GetAverageBlockDuration();
|
|
|
|
for (const Block& block : initial_blocks_) {
|
|
|
|
max_bitrate_ =
|
|
|
|
std::max(max_bitrate_, GetBitrate(block, target_block_duration_));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
max_bitrate_ = std::max(max_bitrate_, GetBitrate({size_in_bits, duration},
|
|
|
|
target_block_duration_));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t BandwidthEstimator::Estimate() const {
|
|
|
|
if (total_duration_ == 0)
|
|
|
|
return 0;
|
|
|
|
return static_cast<uint64_t>(ceil(total_size_in_bits_ / total_duration_));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t BandwidthEstimator::Max() const {
|
|
|
|
if (max_bitrate_ != 0)
|
|
|
|
return max_bitrate_;
|
|
|
|
|
|
|
|
// We don't have the |target_block_duration_| yet. Calculate a target
|
|
|
|
// duration from the current available blocks.
|
|
|
|
DCHECK(target_block_duration_ == 0);
|
|
|
|
const double target_block_duration = GetAverageBlockDuration();
|
2018-11-14 20:47:48 +00:00
|
|
|
|
2019-07-11 21:32:10 +00:00
|
|
|
// Calculate maximum bitrate with the target duration calculated above.
|
|
|
|
uint64_t max_bitrate = 0;
|
|
|
|
for (const Block& block : initial_blocks_) {
|
|
|
|
max_bitrate =
|
|
|
|
std::max(max_bitrate, GetBitrate(block, target_block_duration));
|
|
|
|
}
|
|
|
|
return max_bitrate;
|
|
|
|
}
|
|
|
|
|
|
|
|
double BandwidthEstimator::GetAverageBlockDuration() const {
|
|
|
|
if (initial_blocks_.empty())
|
|
|
|
return 0.0;
|
|
|
|
const double sum =
|
|
|
|
std::accumulate(initial_blocks_.begin(), initial_blocks_.end(), 0.0,
|
|
|
|
[](double duration, const Block& block) {
|
|
|
|
return duration + block.duration;
|
|
|
|
});
|
|
|
|
return sum / initial_blocks_.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t BandwidthEstimator::GetBitrate(const Block& block,
|
|
|
|
double target_block_duration) const {
|
|
|
|
if (block.duration < 0.5 * target_block_duration) {
|
2018-11-14 20:47:48 +00:00
|
|
|
// https://tools.ietf.org/html/rfc8216#section-4.1
|
|
|
|
// The peak segment bit rate of a Media Playlist is the largest bit rate of
|
|
|
|
// any continuous set of segments whose total duration is between 0.5
|
|
|
|
// and 1.5 times the target duration.
|
|
|
|
// Only the short segments are excluded here as our media playlist generator
|
|
|
|
// sets the target duration in the playlist to the largest segment duration.
|
|
|
|
// So although the segment duration could be 1.5 times the user provided
|
|
|
|
// segment duration, it will never be larger than the actual target
|
|
|
|
// duration.
|
|
|
|
//
|
|
|
|
// We also apply the same exclusion to the bandwidth computation for DASH as
|
|
|
|
// the bitrate for the short segment is not a good signal for peak
|
|
|
|
// bandwidth.
|
2022-03-07 19:56:34 +00:00
|
|
|
// See https://github.com/shaka-project/shaka-packager/issues/498 for
|
|
|
|
// details.
|
2019-07-11 21:32:10 +00:00
|
|
|
VLOG(1) << "Exclude short segment (duration " << block.duration
|
|
|
|
<< ", target_duration " << target_block_duration
|
2018-11-14 20:47:48 +00:00
|
|
|
<< ") in peak bandwidth computation.";
|
2023-08-05 03:45:21 +00:00
|
|
|
return 0;
|
2018-11-14 20:47:48 +00:00
|
|
|
}
|
2019-07-11 21:32:10 +00:00
|
|
|
return static_cast<uint64_t>(ceil(block.size_in_bits / block.duration));
|
2014-05-22 02:16:17 +00:00
|
|
|
}
|
2018-04-13 18:48:11 +00:00
|
|
|
|
|
|
|
} // namespace shaka
|