Skip to content

Commit

Permalink
Implement new padding manager calculation algorithm (#1810)
Browse files Browse the repository at this point in the history
  • Loading branch information
lodoyun authored May 10, 2022
1 parent cb8fad8 commit 5623df2
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 99 deletions.
2 changes: 2 additions & 0 deletions erizo/src/erizo/WebRtcConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ void WebRtcConnection::initializeStats() {
log_stats_->getNode().insertStat("bwe", CumulativeStat{0});
log_stats_->getNode().insertStat("bwDistributionAlgorithm", StringStat{""});
log_stats_->getNode().insertStat("bwPriorityStrategy", StringStat{""});
log_stats_->getNode().insertStat("paddingMode", CumulativeStat{0});

std::weak_ptr<WebRtcConnection> weak_this = shared_from_this();
worker_->scheduleEvery([weak_this] () {
Expand Down Expand Up @@ -178,6 +179,7 @@ void WebRtcConnection::printStats() {
log_stats_->getNode().insertStat("connectionQualityLevel",
CumulativeStat(getConnectionQualityLevel()));
transferMediaStats("bwe", "total", "senderBitrateEstimation");
transferMediaStats("paddingMode", "total", "paddingMode");

ELOG_INFOT(ConnectionStatsLogger, "%s", log_stats_->getStats());
}
Expand Down
2 changes: 1 addition & 1 deletion erizo/src/erizo/rtp/BandwidthEstimationHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ void BandwidthEstimationHandler::read(Context *ctx, std::shared_ptr<DataPacket>
RtpHeader *head = reinterpret_cast<RtpHeader*> (packet->data);
int64_t arrival_time_ms = packet->received_time_ms;
arrival_time_ms = clock_->TimeInMilliseconds() - (ClockUtils::timePointToMs(clock::now()) - arrival_time_ms);
size_t payload_size = packet->length - head->getHeaderLength();
size_t payload_size = packet->length;
pickEstimatorFromHeader();
rbe_->IncomingPacket(arrival_time_ms, payload_size, header_);
} else {
Expand Down
175 changes: 126 additions & 49 deletions erizo/src/erizo/rtp/RtpPaddingManagerHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@ namespace erizo {
DEFINE_LOGGER(RtpPaddingManagerHandler, "rtp.RtpPaddingManagerHandler");

static constexpr duration kStatsPeriod = std::chrono::milliseconds(100);
constexpr duration RtpPaddingManagerHandler::kMinDurationToSendPaddingAfterBweDecrease;
constexpr duration RtpPaddingManagerHandler::kMaxDurationInRecoveryFromBwe;
static constexpr double kBitrateComparisonMargin = 1.1;
static constexpr uint64_t kInitialBitrate = 300000;
static constexpr uint64_t kUnnasignedBitrateMargin = 50000;
static constexpr uint64_t kUnnasignedBitrateMargin = 200000;

RtpPaddingManagerHandler::RtpPaddingManagerHandler(std::shared_ptr<erizo::Clock> the_clock) :
initialized_{false},
clock_{the_clock},
last_rate_calculation_time_{clock_->now()},
last_mode_change_{clock_->now()},
connection_{nullptr},
last_estimated_bandwidth_{0} {
last_estimated_bandwidth_{0},
can_recover_{true},
current_mode_{PaddingManagerMode::START} {
}

void RtpPaddingManagerHandler::enable() {
Expand Down Expand Up @@ -53,6 +54,8 @@ void RtpPaddingManagerHandler::notifyUpdate() {
MovingIntervalRateStat{std::chrono::milliseconds(100), 30, 8., clock_});
stats_->getNode()["total"].insertStat("videoBitrate",
MovingIntervalRateStat{std::chrono::milliseconds(100), 30, 8., clock_});
stats_->getNode()["total"].insertStat("paddingMode",
CumulativeStat{current_mode_});
}

if (!connection_) {
Expand Down Expand Up @@ -83,6 +86,11 @@ void RtpPaddingManagerHandler::write(Context *ctx, std::shared_ptr<DataPacket> p
ctx->fireWrite(packet);
}

PaddingManagerMode RtpPaddingManagerHandler::getCurrentPaddingMode() {
ELOG_DEBUG("Getting current padding mode - %u", current_mode_);
return current_mode_;
}

void RtpPaddingManagerHandler::recalculatePaddingRate() {
if (!isTimeToCalculateBitrate()) {
return;
Expand All @@ -107,62 +115,130 @@ void RtpPaddingManagerHandler::recalculatePaddingRate() {
}

int64_t target_padding_bitrate = std::max(target_bitrate - media_bitrate, int64_t(0));
int64_t available_bw = std::max(estimated_bandwidth - media_bitrate, int64_t(0));

target_padding_bitrate = std::min(target_padding_bitrate, available_bw);

bool estimated_is_high_enough = estimated_bandwidth > (target_bitrate * kBitrateComparisonMargin);
bool has_unnasigned_bitrate = false;
bool has_connection_target_bitrate = connection_->getConnectionTargetBw() > 0;
if (stats_->getNode()["total"].hasChild("unnasignedBitrate")) {
has_unnasigned_bitrate =
stats_->getNode()["total"]["unnasignedBitrate"].value() > kUnnasignedBitrateMargin &&
!has_connection_target_bitrate;
bool remb_sharp_drop = estimated_bandwidth < last_estimated_bandwidth_*kBweSharpDropThreshold;
if (current_mode_ == PaddingManagerMode::RECOVER) { // if in recover mode any drop will stop it
remb_sharp_drop = estimated_bandwidth < last_estimated_bandwidth_;
}
if (estimated_is_high_enough || has_unnasigned_bitrate) {
target_padding_bitrate = 0;
int64_t available_bitrate = std::max(estimated_bandwidth - media_bitrate, int64_t(0));

ELOG_DEBUG("Is sharp drop? last_estimated*k %f, new_estimated %u", last_estimated_bandwidth_*kBweSharpDropThreshold,
estimated_bandwidth);
// Maybe Trigger Hold Mode
if (remb_sharp_drop) {
estimated_before_drop_ = last_estimated_bandwidth_;
ELOG_DEBUG("%s Detected sharp BWE drop, estimated_before_drop_: %lu, estimated_bandwidth: %u",
connection_->toLog(), estimated_before_drop_, estimated_bandwidth);
if (current_mode_ == PaddingManagerMode::RECOVER) {
ELOG_DEBUG("%s, Sharp drop in recover mode", connection_->toLog());
can_recover_ = false;
}
forceModeSwitch(PaddingManagerMode::HOLD);
}

bool remb_is_decreasing = estimated_bandwidth < last_estimated_bandwidth_;
last_estimated_bandwidth_ = estimated_bandwidth;
double step = 1.0;
duration time_since_bwe_decreased_ = clock_->now() - last_time_bwe_decreased_;
if (remb_is_decreasing) {
ELOG_DEBUG("%s Remb is decreasing", connection_->toLog());
target_padding_bitrate = 0;
last_time_bwe_decreased_ = clock_->now();
} else if (time_since_bwe_decreased_ < kMinDurationToSendPaddingAfterBweDecrease) {
ELOG_DEBUG("%s Backoff up period time since %d, min %d",
connection_->toLog(),
std::chrono::duration_cast<std::chrono::milliseconds>(time_since_bwe_decreased_).count(),
std::chrono::duration_cast<std::chrono::milliseconds>(kMinDurationToSendPaddingAfterBweDecrease).count());
target_padding_bitrate = 0;
} else if (time_since_bwe_decreased_ >= kMinDurationToSendPaddingAfterBweDecrease) {
step = static_cast<double>(time_since_bwe_decreased_.count()) / kMaxDurationInRecoveryFromBwe.count();
ELOG_DEBUG("%s Ramping up period time since %d, min %d, max %d, calculated step %f",
connection_->toLog(),
std::chrono::duration_cast<std::chrono::milliseconds>(time_since_bwe_decreased_).count(),
std::chrono::duration_cast<std::chrono::milliseconds>(kMinDurationToSendPaddingAfterBweDecrease).count(),
std::chrono::duration_cast<std::chrono::milliseconds>(kMaxDurationInRecoveryFromBwe).count(),
step);
step = std::min(step, 1.0);
target_padding_bitrate = target_padding_bitrate * step;

// Check if it's time to change mode
maybeTriggerTimedModeChanges();

switch (current_mode_) {
case PaddingManagerMode::START:
{
available_bitrate = std::max(estimated_bandwidth*kStartModeFactor - media_bitrate, static_cast<double>(0));
target_padding_bitrate = std::min(target_padding_bitrate, available_bitrate); // never send more than max
break;
}
case PaddingManagerMode::STABLE:
{
can_recover_ = true;
target_padding_bitrate = std::min(target_padding_bitrate,
static_cast<int64_t>(available_bitrate*kStableModeAvailableFactor));
bool has_unnasigned_bitrate = false;
bool has_connection_target_bitrate = connection_->getConnectionTargetBw() > 0;
bool estimated_is_high_enough = estimated_bandwidth > (target_bitrate * kBitrateComparisonMargin);
if (stats_->getNode()["total"].hasChild("unnasignedBitrate")) {
has_unnasigned_bitrate =
stats_->getNode()["total"]["unnasignedBitrate"].value() > kUnnasignedBitrateMargin &&
!has_connection_target_bitrate;
}
if (estimated_is_high_enough || has_unnasigned_bitrate) {
target_padding_bitrate = 0;
}
break;
}
case PaddingManagerMode::HOLD:
{
target_padding_bitrate = 0;
break;
}
case PaddingManagerMode::RECOVER:
{
available_bitrate = std::max(estimated_before_drop_*kRecoverBweFactor - media_bitrate, static_cast<double>(0));
target_padding_bitrate = std::min(available_bitrate, target_bitrate);
break;
}
}

ELOG_DEBUG("%s Calculated: target %d, bwe %d, last_bwe %d, media %d, target %d, bwe enough %d"
" step %f, remb_is_decreasing %d",
connection_->toLog(),
ELOG_DEBUG("Padding stats: target_bitrate %lu, target_padding_bitrate %lu, current_mode_ %u "
"estimated_bitrate %lu, media_bitrate: %lu, available_bw: %lu",
target_bitrate,
target_padding_bitrate,
current_mode_,
estimated_bandwidth,
last_estimated_bandwidth_,
media_bitrate,
target_bitrate,
estimated_is_high_enough,
step,
remb_is_decreasing);
available_bitrate,
current_mode_);

distributeTotalTargetPaddingBitrate(target_padding_bitrate);
}

void RtpPaddingManagerHandler::forceModeSwitch(PaddingManagerMode new_mode) {
ELOG_DEBUG("%s switching mode, current_mode_ %u, new_mode %u, ms since last change %lu",
connection_->toLog(),
current_mode_,
new_mode,
std::chrono::duration_cast<std::chrono::milliseconds>(clock_->now() - last_mode_change_));
current_mode_ = new_mode;
last_mode_change_ = clock_->now();
stats_->getNode()["total"].insertStat("paddingMode",
CumulativeStat{current_mode_});
}

void RtpPaddingManagerHandler::maybeTriggerTimedModeChanges() {
duration time_in_current_mode = clock_->now() - last_mode_change_;
switch (current_mode_) {
case PaddingManagerMode::START:
{
if (time_in_current_mode > kMaxDurationInStartMode) {
ELOG_DEBUG("%s Start mode is over, switching to stable", connection_->toLog());
forceModeSwitch(PaddingManagerMode::STABLE);
}
break;
}
case PaddingManagerMode::STABLE:
break;
case PaddingManagerMode::HOLD:
{
if (time_in_current_mode > kMaxDurationInHoldMode) {
if (can_recover_) {
ELOG_DEBUG("%s Hold mode is over, switching to recover", connection_->toLog());
forceModeSwitch(PaddingManagerMode::RECOVER);
} else {
ELOG_DEBUG("%s Hold mode is over, switching to stable", connection_->toLog());
forceModeSwitch(PaddingManagerMode::STABLE);
}
}
break;
}
case PaddingManagerMode::RECOVER:
{
if (time_in_current_mode > kMaxDurationInRecoverMode) {
ELOG_DEBUG("%s Recover is successful, switching to stable", connection_->toLog());
forceModeSwitch(PaddingManagerMode::STABLE);
}
break;
}
}
}

void RtpPaddingManagerHandler::distributeTotalTargetPaddingBitrate(int64_t bitrate) {
size_t num_streams = 0;
connection_->forEachMediaStream([&num_streams]
Expand All @@ -182,6 +258,7 @@ void RtpPaddingManagerHandler::distributeTotalTargetPaddingBitrate(int64_t bitra
if (!media_stream->canSendPadding()) {
return;
}
ELOG_DEBUG("Setting Target %u", bitrate_per_stream);
media_stream->setTargetPaddingBitrate(bitrate_per_stream);
});
}
Expand Down
24 changes: 22 additions & 2 deletions erizo/src/erizo/rtp/RtpPaddingManagerHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,25 @@ namespace erizo {

class WebRtcConnection;

enum PaddingManagerMode {
START = 0,
STABLE = 1,
HOLD = 2,
RECOVER = 3
};


class RtpPaddingManagerHandler: public Handler, public std::enable_shared_from_this<RtpPaddingManagerHandler> {
DECLARE_LOGGER();

public:
static constexpr duration kMinDurationToSendPaddingAfterBweDecrease = std::chrono::seconds(5);
static constexpr duration kMaxDurationInRecoveryFromBwe = std::chrono::seconds(30);
static constexpr duration kMaxDurationInStartMode = std::chrono::seconds(15);
static constexpr duration kMaxDurationInRecoverMode = std::chrono::seconds(10);
static constexpr duration kMaxDurationInHoldMode = std::chrono::seconds(5);
static constexpr double kBweSharpDropThreshold = 0.66;
static constexpr double kStartModeFactor = 3;
static constexpr double kRecoverBweFactor = 0.85;
static constexpr double kStableModeAvailableFactor = 1.1;
explicit RtpPaddingManagerHandler(std::shared_ptr<erizo::Clock> the_clock = std::make_shared<erizo::SteadyClock>());

void enable() override;
Expand All @@ -33,21 +46,28 @@ class RtpPaddingManagerHandler: public Handler, public std::enable_shared_from_t
void read(Context *ctx, std::shared_ptr<DataPacket> packet) override;
void write(Context *ctx, std::shared_ptr<DataPacket> packet) override;
void notifyUpdate() override;
PaddingManagerMode getCurrentPaddingMode();

private:
bool isTimeToCalculateBitrate();
void recalculatePaddingRate();
void distributeTotalTargetPaddingBitrate(int64_t bitrate);
int64_t getTotalTargetBitrate();
void maybeTriggerTimedModeChanges();
void forceModeSwitch(PaddingManagerMode new_mode);

private:
bool initialized_;
std::shared_ptr<erizo::Clock> clock_;
time_point last_rate_calculation_time_;
time_point last_time_bwe_decreased_;
time_point last_mode_change_;
uint64_t estimated_before_drop_;
WebRtcConnection* connection_;
std::shared_ptr<Stats> stats_;
int64_t last_estimated_bandwidth_;
bool can_recover_;
PaddingManagerMode current_mode_;
};

} // namespace erizo
Expand Down
5 changes: 5 additions & 0 deletions erizo/src/erizo/rtp/SenderBandwidthEstimantionHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ namespace erizo {

DEFINE_LOGGER(SenderBandwidthEstimationHandler, "rtp.SenderBandwidthEstimationHandler");

const uint16_t SenderBandwidthEstimationHandler::kMaxSrListSize;
const uint32_t SenderBandwidthEstimationHandler::kStartSendBitrate;
const uint32_t SenderBandwidthEstimationHandler::kMinSendBitrate;
const uint32_t SenderBandwidthEstimationHandler::kMinSendBitrateLimit;
const uint32_t SenderBandwidthEstimationHandler::kMaxSendBitrate;
constexpr duration SenderBandwidthEstimationHandler::kMinUpdateEstimateInterval;

SenderBandwidthEstimationHandler::SenderBandwidthEstimationHandler(std::shared_ptr<Clock> the_clock) :
Expand Down
Loading

0 comments on commit 5623df2

Please sign in to comment.