Skip to content

Commit

Permalink
Adjust bandwidth estimation stats for stream priority distributor (#1712
Browse files Browse the repository at this point in the history
)
  • Loading branch information
lodoyun committed May 19, 2021
1 parent 075c942 commit 71fb501
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 57 deletions.
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

0 comments on commit 71fb501

Please sign in to comment.