Exclude short segments from peak bandwidth computation
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 contiguous set of segments whose total duration is between 0.5 and 1.5 times the target duration. Fixes #498. Change-Id: I1f28972b9cc5977735e47906bdcd88ba3942db5a
This commit is contained in:
parent
061785285e
commit
74df8d30cc
|
@ -210,9 +210,7 @@ std::unique_ptr<KeySource> CreateDecryptionKeySource(
|
|||
return decryption_key_source;
|
||||
}
|
||||
|
||||
MpdOptions GetMpdOptions(bool on_demand_profile,
|
||||
const MpdParams& mpd_params,
|
||||
double target_segment_duration) {
|
||||
MpdOptions GetMpdOptions(bool on_demand_profile, const MpdParams& mpd_params) {
|
||||
MpdOptions mpd_options;
|
||||
mpd_options.dash_profile =
|
||||
on_demand_profile ? DashProfile::kOnDemand : DashProfile::kLive;
|
||||
|
@ -221,7 +219,6 @@ MpdOptions GetMpdOptions(bool on_demand_profile,
|
|||
? MpdType::kStatic
|
||||
: MpdType::kDynamic;
|
||||
mpd_options.mpd_params = mpd_params;
|
||||
mpd_options.target_segment_duration = target_segment_duration;
|
||||
return mpd_options;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,9 +45,7 @@ std::unique_ptr<KeySource> CreateDecryptionKeySource(
|
|||
const DecryptionParams& decryption_params);
|
||||
|
||||
/// @return MpdOptions from provided inputs.
|
||||
MpdOptions GetMpdOptions(bool on_demand_profile,
|
||||
const MpdParams& mpd_params,
|
||||
double target_segment_duration);
|
||||
MpdOptions GetMpdOptions(bool on_demand_profile, const MpdParams& mpd_params);
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
|
|
@ -330,7 +330,8 @@ MediaPlaylist::MediaPlaylist(const HlsParams& hls_params,
|
|||
: hls_params_(hls_params),
|
||||
file_name_(file_name),
|
||||
name_(name),
|
||||
group_id_(group_id) {}
|
||||
group_id_(group_id),
|
||||
bandwidth_estimator_(hls_params_.target_segment_duration) {}
|
||||
|
||||
MediaPlaylist::~MediaPlaylist() {}
|
||||
|
||||
|
|
|
@ -46,6 +46,15 @@ struct HlsParams {
|
|||
/// in 'EXT-X-MEDIA' tag. This allows the player to choose the correct default
|
||||
/// language for the content.
|
||||
std::string default_language;
|
||||
/// This is the target segment duration requested by the user. The actual
|
||||
/// segment duration may be different to the target segment duration.
|
||||
/// This parameter is included here to for bandwidth estimator to exclude the
|
||||
/// segments with duration less than half of the target duration from
|
||||
/// bandwidth estimation. See
|
||||
/// https://github.com/google/shaka-packager/issues/498 for details. It will
|
||||
/// be populated from segment duration specified in ChunkingParams if not
|
||||
/// specified.
|
||||
double target_segment_duration = 0;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
|
||||
namespace shaka {
|
||||
|
||||
BandwidthEstimator::BandwidthEstimator() = default;
|
||||
BandwidthEstimator::BandwidthEstimator(double target_segment_duration)
|
||||
: target_segment_duration_(target_segment_duration) {}
|
||||
|
||||
BandwidthEstimator::~BandwidthEstimator() = default;
|
||||
|
||||
void BandwidthEstimator::AddBlock(uint64_t size_in_bytes, double duration) {
|
||||
|
@ -30,6 +32,30 @@ void BandwidthEstimator::AddBlock(uint64_t size_in_bytes, double duration) {
|
|||
total_duration_ += duration;
|
||||
|
||||
const uint64_t bitrate = static_cast<uint64_t>(ceil(size_in_bits / duration));
|
||||
|
||||
if (duration < 0.5 * target_segment_duration_) {
|
||||
// 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.
|
||||
//
|
||||
// TODO(kqyang): Review if we can just stick to the user provided segment
|
||||
// duration as our 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.
|
||||
// See https://github.com/google/shaka-packager/issues/498 for details.
|
||||
VLOG(1) << "Exclude short segment (duration " << duration
|
||||
<< ", target_duration " << target_segment_duration_
|
||||
<< ") in peak bandwidth computation.";
|
||||
return;
|
||||
}
|
||||
max_bitrate_ = std::max(bitrate, max_bitrate_);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace shaka {
|
|||
|
||||
class BandwidthEstimator {
|
||||
public:
|
||||
BandwidthEstimator();
|
||||
explicit BandwidthEstimator(double target_segment_duration);
|
||||
~BandwidthEstimator();
|
||||
|
||||
/// @param size is the size of the block in bytes. Should be positive.
|
||||
|
@ -35,6 +35,7 @@ class BandwidthEstimator {
|
|||
BandwidthEstimator(const BandwidthEstimator&) = delete;
|
||||
BandwidthEstimator& operator=(const BandwidthEstimator&) = delete;
|
||||
|
||||
const double target_segment_duration_ = 0;
|
||||
uint64_t total_size_in_bits_ = 0;
|
||||
double total_duration_ = 0;
|
||||
uint64_t max_bitrate_ = 0;
|
||||
|
|
|
@ -15,9 +15,9 @@ const uint64_t kBitsInByte = 8;
|
|||
} // namespace
|
||||
|
||||
TEST(BandwidthEstimatorTest, AllBlocks) {
|
||||
BandwidthEstimator be;
|
||||
const uint64_t kNumBlocksToAdd = 100;
|
||||
const double kDuration = 1.0;
|
||||
BandwidthEstimator be(kDuration);
|
||||
const uint64_t kNumBlocksToAdd = 100;
|
||||
uint64_t total_bytes = 0;
|
||||
for (uint64_t i = 1; i <= kNumBlocksToAdd; ++i) {
|
||||
be.AddBlock(i, kDuration);
|
||||
|
@ -31,4 +31,19 @@ TEST(BandwidthEstimatorTest, AllBlocks) {
|
|||
EXPECT_EQ(kMax, be.Max());
|
||||
}
|
||||
|
||||
TEST(BandwidthEstimatorTest, ExcludeShortSegments) {
|
||||
const double kDuration = 1.0;
|
||||
BandwidthEstimator be(kDuration);
|
||||
|
||||
// Add 4 blocks with duration 0.1, 0.8, 1.8 and 0.2 respectively. The first
|
||||
// and the last blocks are excluded as they are too short.
|
||||
be.AddBlock(1, 0.1 * kDuration);
|
||||
be.AddBlock(1, 0.8 * kDuration);
|
||||
be.AddBlock(1, 1.8 * kDuration);
|
||||
be.AddBlock(1, 0.2 * kDuration);
|
||||
|
||||
const uint64_t kExpectedMax = 1 / 0.8 * kBitsInByte;
|
||||
EXPECT_EQ(kExpectedMax, be.Max());
|
||||
}
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -26,11 +26,6 @@ struct MpdOptions {
|
|||
DashProfile dash_profile = DashProfile::kOnDemand;
|
||||
MpdType mpd_type = MpdType::kStatic;
|
||||
MpdParams mpd_params;
|
||||
/// This is the target segment duration requested by the user. The actual
|
||||
/// segment duration may be different to the target segment duration.
|
||||
/// This parameter is included here to calculate the approximate
|
||||
/// SegmentTimeline if it is enabled.
|
||||
double target_segment_duration = 0;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -115,6 +115,7 @@ Representation::Representation(
|
|||
std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
|
||||
: media_info_(media_info),
|
||||
id_(id),
|
||||
bandwidth_estimator_(mpd_options.mpd_params.target_segment_duration),
|
||||
mpd_options_(mpd_options),
|
||||
state_change_listener_(std::move(state_change_listener)),
|
||||
allow_approximate_segment_timeline_(
|
||||
|
@ -426,7 +427,8 @@ int64_t Representation::AdjustDuration(int64_t duration) const {
|
|||
if (!allow_approximate_segment_timeline_)
|
||||
return duration;
|
||||
const int64_t scaled_target_duration =
|
||||
mpd_options_.target_segment_duration * media_info_.reference_time_scale();
|
||||
mpd_options_.mpd_params.target_segment_duration *
|
||||
media_info_.reference_time_scale();
|
||||
return ApproximiatelyEqual(scaled_target_duration, duration)
|
||||
? scaled_target_duration
|
||||
: duration;
|
||||
|
|
|
@ -416,6 +416,8 @@ const char kSElementTemplateWithoutR[] =
|
|||
const int kDefaultStartNumber = 1;
|
||||
const uint32_t kDefaultTimeScale = 1000u;
|
||||
const int64_t kScaledTargetSegmentDuration = 10;
|
||||
const double kTargetSegmentDurationInSeconds =
|
||||
static_cast<double>(kScaledTargetSegmentDuration) / kDefaultTimeScale;
|
||||
const uint32_t kSampleDuration = 2;
|
||||
|
||||
std::string GetDefaultMediaInfo() {
|
||||
|
@ -441,6 +443,9 @@ std::string GetDefaultMediaInfo() {
|
|||
|
||||
class SegmentTemplateTest : public RepresentationTest {
|
||||
public:
|
||||
SegmentTemplateTest()
|
||||
: bandwidth_estimator_(kTargetSegmentDurationInSeconds) {}
|
||||
|
||||
void SetUp() override {
|
||||
mpd_options_.mpd_type = MpdType::kDynamic;
|
||||
representation_ =
|
||||
|
@ -715,8 +720,8 @@ class SegmentTimelineTestBase : public SegmentTemplateTest {
|
|||
const std::string& number_template_media_info =
|
||||
base::StringPrintf(kMediaInfo, kDefaultTimeScale);
|
||||
mpd_options_.mpd_type = MpdType::kDynamic;
|
||||
mpd_options_.target_segment_duration =
|
||||
static_cast<double>(kScaledTargetSegmentDuration) / kDefaultTimeScale;
|
||||
mpd_options_.mpd_params.target_segment_duration =
|
||||
kTargetSegmentDurationInSeconds;
|
||||
representation_ =
|
||||
CreateRepresentation(ConvertToMediaInfo(number_template_media_info),
|
||||
kAnyRepresentationId, NoListener());
|
||||
|
|
|
@ -69,6 +69,14 @@ struct MpdParams {
|
|||
/// Ignored if $Time$ is used in segment template, since $Time$ requires
|
||||
/// accurate Segment Timeline.
|
||||
bool allow_approximate_segment_timeline = false;
|
||||
/// This is the target segment duration requested by the user. The actual
|
||||
/// segment duration may be different to the target segment duration.
|
||||
/// This parameter is included here to calculate the approximate
|
||||
/// SegmentTimeline if it is enabled. It is also used by the bandwidth
|
||||
/// estimator to exclude the segments with duration less than half of the
|
||||
/// target duration from bandwidth estimation. It will be populated from
|
||||
/// segment duration specified in ChunkingParams if not specified.
|
||||
double target_segment_duration = 0;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -867,18 +867,28 @@ Status Packager::Initialize(
|
|||
return Status(error::INVALID_ARGUMENT, "Failed to create key source.");
|
||||
}
|
||||
|
||||
// Store callback params to make it available during packaging.
|
||||
internal->buffer_callback_params = packaging_params.buffer_callback_params;
|
||||
|
||||
// Update MPD output and HLS output if callback param is specified.
|
||||
// Update MPD output and HLS output if needed.
|
||||
MpdParams mpd_params = packaging_params.mpd_params;
|
||||
HlsParams hls_params = packaging_params.hls_params;
|
||||
|
||||
// |target_segment_duration| is needed for bandwidth estimation and also for
|
||||
// DASH approximate segment timeline.
|
||||
const double target_segment_duration =
|
||||
packaging_params.chunking_params.segment_duration_in_seconds;
|
||||
if (mpd_params.target_segment_duration != 0)
|
||||
mpd_params.target_segment_duration = target_segment_duration;
|
||||
if (hls_params.target_segment_duration != 0)
|
||||
hls_params.target_segment_duration = target_segment_duration;
|
||||
|
||||
// Store callback params to make it available during packaging.
|
||||
internal->buffer_callback_params = packaging_params.buffer_callback_params;
|
||||
if (internal->buffer_callback_params.write_func) {
|
||||
mpd_params.mpd_output = File::MakeCallbackFileName(
|
||||
internal->buffer_callback_params, mpd_params.mpd_output);
|
||||
hls_params.master_playlist_output = File::MakeCallbackFileName(
|
||||
internal->buffer_callback_params, hls_params.master_playlist_output);
|
||||
}
|
||||
|
||||
// Both DASH and HLS require language to follow RFC5646
|
||||
// (https://tools.ietf.org/html/rfc5646), which requires the language to be
|
||||
// in the shortest form.
|
||||
|
@ -890,10 +900,8 @@ Status Packager::Initialize(
|
|||
if (!mpd_params.mpd_output.empty()) {
|
||||
const bool on_demand_dash_profile =
|
||||
stream_descriptors.begin()->segment_template.empty();
|
||||
const double target_segment_duration =
|
||||
packaging_params.chunking_params.segment_duration_in_seconds;
|
||||
const MpdOptions mpd_options = media::GetMpdOptions(
|
||||
on_demand_dash_profile, mpd_params, target_segment_duration);
|
||||
const MpdOptions mpd_options =
|
||||
media::GetMpdOptions(on_demand_dash_profile, mpd_params);
|
||||
internal->mpd_notifier.reset(new SimpleMpdNotifier(mpd_options));
|
||||
if (!internal->mpd_notifier->Init()) {
|
||||
LOG(ERROR) << "MpdNotifier failed to initialize.";
|
||||
|
|
Loading…
Reference in New Issue