Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust bandwidth estimation stats for stream priority distributor #1712

Merged
merged 3 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion erizo/src/erizo/bandwidth/BwDistributionConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class StreamPriorityStrategy {
explicit StreamPriorityStrategy(const std::string& strategy_id = "none");
std::vector<StreamPriorityStep> strategy;
uint16_t step_index;
std::string strategy_id;
void addStep(StreamPriorityStep step);
void initWithVector(std::vector<StreamPriorityStep> strat_vector);
int getHighestLayerForPriority(std::string priority);
Expand Down
2 changes: 2 additions & 0 deletions erizo/src/erizo/bandwidth/StreamPriorityBWDistributor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "StreamPriorityBWDistributor.h"
#include "MediaStream.h"
#include "rtp/QualityManager.h"
#include "Transport.h"
#include "rtp/RtpUtils.h"

Expand Down Expand Up @@ -86,6 +87,7 @@ void StreamPriorityBWDistributor::distribute(uint32_t remb, uint32_t ssrc,
needed_bitrate_for_stream =
bitrate_for_higher_temporal_in_spatial == 0 ? max_bitrate_that_meets_constraints :
std::min(bitrate_for_higher_temporal_in_spatial, max_bitrate_that_meets_constraints);
needed_bitrate_for_stream = needed_bitrate_for_stream * (1 + QualityManager::kIncreaseLayerBitrateThreshold);
}
uint64_t bitrate = std::min(needed_bitrate_for_stream, remaining_avg_bitrate);
uint64_t remb = std::min(static_cast<uint64_t>(stream_info.stream->getMaxVideoBW()), bitrate);
Expand Down
100 changes: 62 additions & 38 deletions erizo/src/erizo/rtp/QualityManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,16 @@ void QualityManager::notifyQualityUpdate() {
uint64_t current_layer_instant_bitrate = getInstantLayerBitrate(spatial_layer_, temporal_layer_);
bool estimated_is_under_layer_bitrate = current_estimated_bitrate_ < current_layer_instant_bitrate;

if (now - last_activity_check_ > kActiveLayerInterval) {
calculateMaxActiveLayer();
last_activity_check_ = now;
}
maybeUpdateAvailableLayersAndBitrates();

bool layer_is_active = spatial_layer_ <= max_active_spatial_layer_;

if (!layer_is_active || (estimated_is_under_layer_bitrate && !freeze_fallback_active_)) {
ELOG_DEBUG("message: Forcing calculate new layer, "
"estimated_is_under_layer_bitrate: %d, layer_is_active: %d, freeze_fallback_active_: %d",
estimated_is_under_layer_bitrate, layer_is_active, freeze_fallback_active_);
"estimated_is_under_layer_bitrate: %d, layer_is_active: %d, freeze_fallback_active_: %d,"
"estimated %lu, layer_bitrate %lu",
estimated_is_under_layer_bitrate, layer_is_active, freeze_fallback_active_,
current_estimated_bitrate_, current_layer_instant_bitrate);
selectLayer(false);
} else if (now - last_quality_check_ > kMinLayerSwitchInterval) {
selectLayer(true);
Expand Down Expand Up @@ -121,39 +120,16 @@ bool QualityManager::doesLayerMeetConstraints(int spatial_layer, int temporal_la
return meets_resolution && meets_frame_rate;
}

void QualityManager::calculateMaxBitrateThatMeetsConstraints() {
int max_available_spatial_layer_that_meets_constraints = 0;
int max_available_temporal_layer_that_meets_constraints = 0;

int max_spatial_layer_with_resolution_info = 0;
if (video_frame_width_list_.size() > 0 && video_frame_height_list_.size() > 0) {
max_spatial_layer_with_resolution_info = std::min(static_cast<int>(video_frame_width_list_.size()) - 1,
static_cast<int>(video_frame_height_list_.size()) - 1);
}
int max_temporal_layer_with_frame_rate_info = std::max(static_cast<int>(video_frame_rate_list_.size()) - 1, 0);

int max_spatial_layer_available = std::min(max_spatial_layer_with_resolution_info, max_active_spatial_layer_);
int max_temporal_layer_available = std::min(max_temporal_layer_with_frame_rate_info, max_active_temporal_layer_);

// reset layers
for (int spatial_layer = 5; spatial_layer >=0; spatial_layer--) {
for (int temporal_layer = 5; temporal_layer >=0; temporal_layer--) {
stream_->setBitrateForLayer(spatial_layer, temporal_layer, 0);
}
}

for (int spatial_layer = 0; spatial_layer <= max_spatial_layer_available; spatial_layer++) {
for (int temporal_layer = 0; temporal_layer <= max_temporal_layer_available; temporal_layer++) {
stream_->setBitrateForLayer(spatial_layer, temporal_layer, getInstantLayerBitrate(spatial_layer, temporal_layer));
if (doesLayerMeetConstraints(spatial_layer, temporal_layer)) {
max_available_spatial_layer_that_meets_constraints = spatial_layer;
max_available_temporal_layer_that_meets_constraints = temporal_layer;
}
}
void QualityManager::maybeUpdateAvailableLayersAndBitrates() {
time_point now = clock_->now();
if (now - last_activity_check_ > kActiveLayerInterval) {
last_activity_check_ = now;
} else {
return;
}

stream_->setBitrateFromMaxQualityLayer(getInstantLayerBitrate(max_available_spatial_layer_that_meets_constraints,
max_available_temporal_layer_that_meets_constraints));
calculateMaxActiveLayer();
storeLayersAndBitratesInMediaStream();
}

void QualityManager::selectLayer(bool try_higher_layers) {
Expand All @@ -162,7 +138,7 @@ void QualityManager::selectLayer(bool try_higher_layers) {
}
stream_->setSimulcast(true);
last_quality_check_ = clock_->now();
calculateMaxBitrateThatMeetsConstraints();
maybeUpdateAvailableLayersAndBitrates();

int min_requested_spatial_layer =
enable_slideshow_below_spatial_layer_ ? std::max(slideshow_below_spatial_layer_, 0) : 0;
Expand Down Expand Up @@ -244,6 +220,43 @@ void QualityManager::selectLayer(bool try_higher_layers) {
CumulativeStat{layer_capped_by_constraints});
}

void QualityManager::storeLayersAndBitratesInMediaStream() {
int max_available_spatial_layer_that_meets_constraints = 0;
int max_available_temporal_layer_that_meets_constraints = 0;

int max_spatial_layer_with_resolution_info = 0;
if (video_frame_width_list_.size() > 0 && video_frame_height_list_.size() > 0) {
max_spatial_layer_with_resolution_info = std::min(static_cast<int>(video_frame_width_list_.size()) - 1,
static_cast<int>(video_frame_height_list_.size()) - 1);
}
int max_temporal_layer_with_frame_rate_info = std::max(static_cast<int>(video_frame_rate_list_.size()) - 1, 0);

int max_spatial_layer_available = std::min(max_spatial_layer_with_resolution_info, max_active_spatial_layer_);
int max_temporal_layer_available = std::min(max_temporal_layer_with_frame_rate_info, max_active_temporal_layer_);

// reset layers
for (int spatial_layer = 5; spatial_layer >=0; spatial_layer--) {
for (int temporal_layer = 5; temporal_layer >=0; temporal_layer--) {
stream_->setBitrateForLayer(spatial_layer, temporal_layer, 0);
}
}

for (int spatial_layer = 0; spatial_layer <= max_spatial_layer_available; spatial_layer++) {
for (int temporal_layer = 0; temporal_layer <= max_temporal_layer_available; temporal_layer++) {
stream_->setBitrateForLayer(spatial_layer, temporal_layer,
getMaxLayerBitrateInInterval(spatial_layer, temporal_layer));
if (doesLayerMeetConstraints(spatial_layer, temporal_layer)) {
max_available_spatial_layer_that_meets_constraints = spatial_layer;
max_available_temporal_layer_that_meets_constraints = temporal_layer;
}
}
}

stream_->setBitrateFromMaxQualityLayer(
getMaxLayerBitrateInInterval(max_available_spatial_layer_that_meets_constraints,
max_available_temporal_layer_that_meets_constraints));
}

void QualityManager::calculateMaxActiveLayer() {
int max_active_spatial_layer = 5;
int max_active_temporal_layer = 5;
Expand Down Expand Up @@ -279,6 +292,17 @@ uint64_t QualityManager::getInstantLayerBitrate(int spatial_layer, int temporal_
return layer_stat->value(kActiveLayerInterval);
}

uint64_t QualityManager::getMaxLayerBitrateInInterval(int spatial_layer, int temporal_layer) {
if (!stats_->getNode()["qualityLayers"].hasChild(spatial_layer) ||
!stats_->getNode()["qualityLayers"][spatial_layer].hasChild(temporal_layer)) {
return 0;
}

MovingIntervalRateStat* layer_stat =
reinterpret_cast<MovingIntervalRateStat*>(&stats_->getNode()["qualityLayers"][spatial_layer][temporal_layer]);
return layer_stat->maxValueForIntervalSize(std::chrono::milliseconds(1000));
}

bool QualityManager::isInBaseLayer() {
return (spatial_layer_ == 0 && temporal_layer_ == 0);
}
Expand Down
4 changes: 3 additions & 1 deletion erizo/src/erizo/rtp/QualityManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ class QualityManager: public Service, public std::enable_shared_from_this<Qualit

private:
void calculateMaxActiveLayer();
void calculateMaxBitrateThatMeetsConstraints();
void maybeUpdateAvailableLayersAndBitrates();
void storeLayersAndBitratesInMediaStream();
void selectLayer(bool try_higher_layers);
uint64_t getInstantLayerBitrate(int spatial_layer, int temporal_layer);
uint64_t getMaxLayerBitrateInInterval(int spatial_layer, int temporal_layer);
bool isInBaseLayer();
bool isInMaxLayer();
bool doesLayerMeetConstraints(int spatial_layer, int temporal_layer);
Expand Down
26 changes: 24 additions & 2 deletions erizo/src/erizo/stats/StatNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,14 @@ std::string MovingIntervalRateStat::toString() {
return std::to_string(value());
}

uint64_t MovingIntervalRateStat::calculateRateForInterval(uint64_t interval_to_calculate_ms) {
uint64_t MovingIntervalRateStat::calculateRateForInterval(uint64_t interval_to_calculate_ms,
uint64_t start_interval_offset_ms) {
if (!initialized_) {
return 0;
}

uint64_t now_ms = ClockUtils::timePointToMs(clock_->now());
uint64_t start_of_requested_interval = now_ms - interval_to_calculate_ms;
uint64_t start_of_requested_interval = now_ms - interval_to_calculate_ms - start_interval_offset_ms;
uint64_t interval_start_time = std::max(start_of_requested_interval, current_window_start_ms_);
uint32_t intervals_to_pass = (interval_start_time - current_window_start_ms_) / interval_size_ms_;
// We check if it's within the data we have
Expand Down Expand Up @@ -219,6 +220,27 @@ uint64_t MovingIntervalRateStat::calculateRateForInterval(uint64_t interval_to_c
return (rate * 1000 * scale_);
}

uint64_t MovingIntervalRateStat::maxValueForIntervalSize(duration requested_interval_size) {
if (!initialized_) {
return 0;
}

uint64_t requested_interval_size_ms = ClockUtils::durationToMs(requested_interval_size);

uint64_t window_size_ms = interval_size_ms_ * intervals_in_window_;
int64_t interval_offset_ms =
std::max(static_cast<int64_t>(window_size_ms - requested_interval_size_ms), static_cast<int64_t>(0));
uint64_t max_rate = 0;

do {
uint64_t rate = calculateRateForInterval(requested_interval_size_ms, interval_offset_ms);
interval_offset_ms -= requested_interval_size_ms;
max_rate = rate > max_rate? rate: max_rate;
} while (interval_offset_ms >= 0);

return max_rate;
}

uint32_t MovingIntervalRateStat::getIntervalForTimeMs(uint64_t time_ms) {
return ((time_ms - calculation_start_ms_)/interval_size_ms_) % intervals_in_window_;
}
Expand Down
3 changes: 2 additions & 1 deletion erizo/src/erizo/stats/StatNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,14 @@ class MovingIntervalRateStat : public StatNode {

uint64_t value() override;
uint64_t value(duration stat_interval);
uint64_t maxValueForIntervalSize(duration requested_interval_size);

std::string toString() override;


private:
void add(uint64_t value);
uint64_t calculateRateForInterval(uint64_t interval_to_calculate_ms);
uint64_t calculateRateForInterval(uint64_t interval_to_calculate_ms, uint64_t start_interval_offset_ms = 0);
uint32_t getIntervalForTimeMs(uint64_t time_ms);
uint32_t getNextInterval(uint32_t interval);
void updateWindowTimes();
Expand Down
Loading