Skip to content

Commit

Permalink
Update Quality Manager to add Rtp Padding (lynckia#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcague authored Mar 15, 2017
1 parent 412e198 commit e92e6f2
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 80 deletions.
3 changes: 1 addition & 2 deletions erizo/src/erizo/rtp/LayerBitrateCalculationHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace erizo {
class WebRtcConnection;

constexpr duration kLayerRateStatIntervalSize = std::chrono::milliseconds(100);
constexpr uint32_t kLayerRateStatIntervals = 10;
constexpr uint32_t kLayerRateStatIntervals = 30;

class LayerBitrateCalculationHandler: public OutboundHandler {
DECLARE_LOGGER();
Expand Down Expand Up @@ -41,4 +41,3 @@ class LayerBitrateCalculationHandler: public OutboundHandler {
} // namespace erizo

#endif // ERIZO_SRC_ERIZO_RTP_LAYERBITRATECALCULATIONHANDLER_H_

4 changes: 2 additions & 2 deletions erizo/src/erizo/rtp/LayerDetectorHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ void LayerDetectorHandler::parseLayerInfoFromVP8(std::shared_ptr<dataPacket> pac
packet->compatible_temporal_layers.push_back(1);
case 1:
packet->compatible_temporal_layers.push_back(2);
case 3:
packet->compatible_temporal_layers.push_back(3);
// case 3 and beyond are not handled because Chrome only
// supports 3 temporal scalability today (03/15/17)
break;
default:
packet->compatible_temporal_layers.push_back(0);
Expand Down
11 changes: 10 additions & 1 deletion erizo/src/erizo/rtp/QualityFilterHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ namespace erizo {

DEFINE_LOGGER(QualityFilterHandler, "rtp.QualityFilterHandler");

constexpr duration kSwitchTimeout = std::chrono::seconds(3);

QualityFilterHandler::QualityFilterHandler()
: connection_{nullptr}, enabled_{true}, initialized_{false},
receiving_multiple_ssrc_{false}, changing_spatial_layer_{false}, is_scalable_{false},
target_spatial_layer_{0},
future_spatial_layer_{-1}, target_temporal_layer_{0},
video_sink_ssrc_{0}, video_source_ssrc_{0}, last_ssrc_received_{0},
max_video_bw_{0}, last_timestamp_sent_{0}, timestamp_offset_{0} {}
max_video_bw_{0}, last_timestamp_sent_{0}, timestamp_offset_{0}, time_change_started_{clock::now()} {}

void QualityFilterHandler::enable() {
enabled_ = true;
Expand Down Expand Up @@ -53,6 +55,7 @@ void QualityFilterHandler::checkLayers() {
sendPLI();
future_spatial_layer_ = new_spatial_layer;
changing_spatial_layer_ = true;
time_change_started_ = clock::now();
}
int new_temporal_layer = quality_manager_->getTemporalLayer();
target_temporal_layer_ = new_temporal_layer;
Expand All @@ -76,11 +79,17 @@ void QualityFilterHandler::changeSpatialLayerOnKeyframeReceived(std::shared_ptr<
return;
}

time_point now = clock::now();

if (packet->belongsToSpatialLayer(future_spatial_layer_) &&
packet->belongsToTemporalLayer(target_temporal_layer_) &&
packet->is_keyframe) {
target_spatial_layer_ = future_spatial_layer_;
future_spatial_layer_ = -1;
} else if (now - time_change_started_ > kSwitchTimeout) {
sendPLI();
target_spatial_layer_ = future_spatial_layer_;
future_spatial_layer_ = -1;
}
}

Expand Down
2 changes: 2 additions & 0 deletions erizo/src/erizo/rtp/QualityFilterHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <map>

#include "./logger.h"
#include "lib/Clock.h"
#include "pipeline/Handler.h"
#include "rtp/SequenceNumberTranslator.h"
#include "rtp/QualityManager.h"
Expand Down Expand Up @@ -60,6 +61,7 @@ class QualityFilterHandler: public Handler, public std::enable_shared_from_this<
uint32_t max_video_bw_;
uint32_t last_timestamp_sent_;
uint32_t timestamp_offset_;
time_point time_change_started_;
};
} // namespace erizo

Expand Down
59 changes: 50 additions & 9 deletions erizo/src/erizo/rtp/QualityManager.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#include "rtp/QualityManager.h"
#include "WebRtcConnection.h"

namespace erizo {

DEFINE_LOGGER(QualityManager, "rtp.QualityManager");

constexpr duration QualityManager::kMinLayerSwitchInterval;
constexpr duration QualityManager::kActiveLayerInterval;
constexpr float QualityManager::kIncreaseLayerBitrateThreshold;

QualityManager::QualityManager(std::shared_ptr<Clock> the_clock)
: initialized_{false}, padding_enabled_{false}, forced_layers_{false},
spatial_layer_{0}, temporal_layer_{0},
: initialized_{false}, padding_enabled_{true}, forced_layers_{false},
spatial_layer_{0}, temporal_layer_{0}, max_active_spatial_layer_{0}, max_active_temporal_layer_{0},
current_estimated_bitrate_{0}, last_quality_check_{the_clock->now()},
clock_{the_clock} {}
last_activity_check_{the_clock->now()}, clock_{the_clock} {}


void QualityManager::notifyQualityUpdate() {
Expand All @@ -36,35 +38,41 @@ void QualityManager::notifyQualityUpdate() {
current_estimated_bitrate_ = stats_->getNode()["total"]["senderBitrateEstimation"].value();
uint64_t current_layer_instant_bitrate = getInstantLayerBitrate(spatial_layer_, temporal_layer_);
bool estimated_is_under_layer_bitrate = current_estimated_bitrate_ < current_layer_instant_bitrate;
bool layer_is_active = current_layer_instant_bitrate != 0;

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

bool layer_is_active = spatial_layer_ <= max_active_spatial_layer_;

if (!isInBaseLayer() && (
!layer_is_active
|| estimated_is_under_layer_bitrate)) {
ELOG_DEBUG("message: Forcing calculate new layer, "
"estimated_is_under_layer_bitrate: %d, layer_is_active: %d", estimated_is_under_layer_bitrate,
layer_is_active);
selectLayer();
return;
selectLayer(false);
} else if (now - last_quality_check_ > kMinLayerSwitchInterval) {
selectLayer();
selectLayer(true);
}
}

void QualityManager::selectLayer() {
void QualityManager::selectLayer(bool try_higher_layers) {
last_quality_check_ = clock_->now();
int aux_temporal_layer = 0;
int aux_spatial_layer = 0;
int next_temporal_layer = 0;
int next_spatial_layer = 0;
float bitrate_margin = try_higher_layers ? kIncreaseLayerBitrateThreshold : 0;
ELOG_DEBUG("Calculate best layer with %lu, current layer %d/%d",
current_estimated_bitrate_, spatial_layer_, temporal_layer_);
for (auto &spatial_layer_node : stats_->getNode()["qualityLayers"].getMap()) {
for (auto &temporal_layer_node : stats_->getNode()["qualityLayers"][spatial_layer_node.first.c_str()].getMap()) {
ELOG_DEBUG("Bitrate for layer %d/%d %lu",
aux_spatial_layer, aux_temporal_layer, temporal_layer_node.second->value());
if (temporal_layer_node.second->value() != 0 &&
temporal_layer_node.second->value() < current_estimated_bitrate_) {
(1. + bitrate_margin) * temporal_layer_node.second->value() < current_estimated_bitrate_) {
next_temporal_layer = aux_temporal_layer;
next_spatial_layer = aux_spatial_layer;
}
Expand All @@ -78,10 +86,39 @@ void QualityManager::selectLayer() {
spatial_layer_, temporal_layer_, next_spatial_layer, next_temporal_layer);
setTemporalLayer(next_temporal_layer);
setSpatialLayer(next_spatial_layer);

// TODO(javier): should we wait for the actual spatial switch?
// should we disable padding temporarily to avoid congestion (old padding + new bitrate)?
padding_enabled_ = !isInMaxLayer();
getContext()->getPipelineShared()->getService<WebRtcConnection>()->notifyUpdateToHandlers();
ELOG_DEBUG("message: Is padding enabled, padding_enabled_: %d", padding_enabled_);
}
}

void QualityManager::calculateMaxActiveLayer() {
int max_active_spatial_layer = 5;
int max_active_temporal_layer = 5;

for (; max_active_spatial_layer > 0; max_active_spatial_layer--) {
if (getInstantLayerBitrate(max_active_spatial_layer, 0) > 0) {
break;
}
}
for (; max_active_temporal_layer > 0; max_active_temporal_layer--) {
if (getInstantLayerBitrate(max_active_spatial_layer, max_active_temporal_layer) > 0) {
break;
}
}
max_active_spatial_layer_ = max_active_spatial_layer;
max_active_temporal_layer_ = max_active_temporal_layer;
}

uint64_t QualityManager::getInstantLayerBitrate(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->value(kActiveLayerInterval);
Expand All @@ -91,6 +128,10 @@ bool QualityManager::isInBaseLayer() {
return (spatial_layer_ == 0 && temporal_layer_ == 0);
}

bool QualityManager::isInMaxLayer() {
return (spatial_layer_ == max_active_spatial_layer_ && temporal_layer_ == max_active_temporal_layer_);
}

void QualityManager::forceLayers(int spatial_layer, int temporal_layer) {
forced_layers_ = true;
spatial_layer_ = spatial_layer;
Expand Down
20 changes: 13 additions & 7 deletions erizo/src/erizo/rtp/QualityManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class QualityManager: public Service, public std::enable_shared_from_this<Qualit
DECLARE_LOGGER();

public:
static constexpr duration kMinLayerSwitchInterval = std::chrono::seconds(2);
static constexpr duration kMinLayerSwitchInterval = std::chrono::seconds(10);
static constexpr duration kActiveLayerInterval = std::chrono::milliseconds(500);
static constexpr float kIncreaseLayerBitrateThreshold = 0.1;

public:
explicit QualityManager(std::shared_ptr<Clock> the_clock = std::make_shared<SteadyClock>());
Expand All @@ -29,23 +30,28 @@ class QualityManager: public Service, public std::enable_shared_from_this<Qualit

virtual bool isPaddingEnabled() const { return padding_enabled_; }

private:
void calculateMaxActiveLayer();
void selectLayer(bool try_higher_layers);
uint64_t getInstantLayerBitrate(int spatial_layer, int temporal_layer);
bool isInBaseLayer();
bool isInMaxLayer();


private:
bool initialized_;
bool padding_enabled_;
bool forced_layers_;
int spatial_layer_;
int temporal_layer_;
std::string spatial_layer_str_;
std::string temporal_layer_str_;
int max_active_spatial_layer_;
int max_active_temporal_layer_;
uint64_t current_estimated_bitrate_;

time_point last_quality_check_;
time_point last_activity_check_;
std::shared_ptr<Stats> stats_;
std::shared_ptr<Clock> clock_;

void selectLayer();
uint64_t getInstantLayerBitrate(int spatial_layer, int temporal_layer);
bool isInBaseLayer();
};
} // namespace erizo

Expand Down
31 changes: 6 additions & 25 deletions erizo/src/erizo/rtp/RtpPaddingGeneratorHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ namespace erizo {
DEFINE_LOGGER(RtpPaddingGeneratorHandler, "rtp.RtpPaddingGeneratorHandler");

constexpr duration kStatsPeriod = std::chrono::milliseconds(100);
constexpr duration kFastStartMaxDuration = std::chrono::seconds(30);
constexpr uint8_t kMaxPaddingSize = 255;
constexpr uint64_t kMinMarkerRate = 3;
constexpr uint8_t kMaxFractionLostAllowed = .05 * 255; // 5% of packet losts
constexpr uint64_t kInitialRembValue = 300000;
constexpr uint64_t kInitialBitrate = 300000;

RtpPaddingGeneratorHandler::RtpPaddingGeneratorHandler(std::shared_ptr<erizo::Clock> the_clock) :
clock_{the_clock}, connection_{nullptr}, max_video_bw_{0}, higher_sequence_number_{0},
Expand All @@ -26,7 +25,7 @@ RtpPaddingGeneratorHandler::RtpPaddingGeneratorHandler(std::shared_ptr<erizo::Cl
enabled_{false}, first_packet_received_{false},
marker_rate_{std::chrono::milliseconds(100), 20, 1., clock_},
padding_bitrate_{std::chrono::milliseconds(100), 10, 8., clock_},
rtp_header_length_{12}, remb_value_{kInitialRembValue}, fast_start_{true} {}
rtp_header_length_{12} {}


void RtpPaddingGeneratorHandler::enable() {
Expand Down Expand Up @@ -59,20 +58,6 @@ void RtpPaddingGeneratorHandler::notifyUpdate() {
}

void RtpPaddingGeneratorHandler::read(Context *ctx, std::shared_ptr<dataPacket> packet) {
RtpUtils::forEachRRBlock(packet, [this](RtcpHeader *chead) {
if (chead->packettype == RTCP_PS_Feedback_PT && chead->getBlockCount() == RTCP_AFB) {
char *uniqueId = reinterpret_cast<char*>(&chead->report.rembPacket.uniqueid);
if (!strncmp(uniqueId, "REMB", 4)) {
remb_value_ = chead->getREMBBitRate();
}
} else if (fast_start_ && chead->packettype == RTCP_Receiver_PT && chead->getSourceSSRC() == audio_source_ssrc_) {
if (chead->getFractionLost() > kMaxFractionLostAllowed) {
ELOG_DEBUG("Fast start disabled");
fast_start_ = false;
}
}
});

ctx->fireRead(packet);
}

Expand All @@ -85,10 +70,6 @@ void RtpPaddingGeneratorHandler::write(Context *ctx, std::shared_ptr<dataPacket>
started_at_ = clock_->now();
}
first_packet_received_ = true;
if (fast_start_ && clock_->now() - started_at_ > kFastStartMaxDuration) {
ELOG_DEBUG("Fast start disabled");
fast_start_ = false;
}
}

ctx->fireWrite(packet);
Expand Down Expand Up @@ -183,18 +164,18 @@ void RtpPaddingGeneratorHandler::recalculatePaddingRate() {
return;
}

uint64_t marker_rate = marker_rate_.value(std::chrono::seconds(2));
uint64_t marker_rate = marker_rate_.value(std::chrono::milliseconds(500));
marker_rate = std::max(marker_rate, kMinMarkerRate);
uint64_t bytes_per_marker = target_padding_bitrate / (marker_rate * 8);
number_of_full_padding_packets_ = bytes_per_marker / (kMaxPaddingSize + rtp_header_length_);
last_padding_packet_size_ = bytes_per_marker % (kMaxPaddingSize + rtp_header_length_) - rtp_header_length_;
}

uint64_t RtpPaddingGeneratorHandler::getTargetBitrate() {
uint64_t target_bitrate = remb_value_;
uint64_t target_bitrate = kInitialBitrate;

if (!fast_start_) {
target_bitrate = getStat("senderBitrateEstimation");
if (stats_->getNode()["total"].hasChild("senderBitrateEstimation")) {
target_bitrate = static_cast<CumulativeStat&>(stats_->getNode()["total"]["senderBitrateEstimation"]).value();
}

if (max_video_bw_ > 0) {
Expand Down
2 changes: 0 additions & 2 deletions erizo/src/erizo/rtp/RtpPaddingGeneratorHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ class RtpPaddingGeneratorHandler: public Handler {
MovingIntervalRateStat marker_rate_;
MovingIntervalRateStat padding_bitrate_;
uint32_t rtp_header_length_;
uint64_t remb_value_;
bool fast_start_;
};

} // namespace erizo
Expand Down
Loading

0 comments on commit e92e6f2

Please sign in to comment.