Skip to content

Commit

Permalink
quality manager logic (#797)
Browse files Browse the repository at this point in the history
  • Loading branch information
lodoyun authored Mar 10, 2017
1 parent dca0135 commit e8ceb00
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 13 deletions.
5 changes: 2 additions & 3 deletions erizo/src/erizo/WebRtcConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ WebRTCEvent WebRtcConnection::getCurrentState() {
void WebRtcConnection::getJSONStats(std::function<void(std::string)> callback) {
asyncTask([callback] (std::shared_ptr<WebRtcConnection> connection) {
std::string requested_stats = connection->stats_->getStats();
ELOG_DEBUG("%s message: Stats, stats: %s", connection->toLog(), requested_stats.c_str());
// ELOG_DEBUG("%s message: Stats, stats: %s", connection->toLog(), requested_stats.c_str());
callback(requested_stats);
});
}
Expand Down Expand Up @@ -822,8 +822,7 @@ void WebRtcConnection::sendPacket(std::shared_ptr<dataPacket> p) {

void WebRtcConnection::setQualityLayer(int spatial_layer, int temporal_layer) {
asyncTask([spatial_layer, temporal_layer] (std::shared_ptr<WebRtcConnection> connection) {
connection->quality_manager_->setSpatialLayer(spatial_layer);
connection->quality_manager_->setTemporalLayer(temporal_layer);
connection->quality_manager_->forceLayers(spatial_layer, temporal_layer);
});
}

Expand Down
6 changes: 6 additions & 0 deletions erizo/src/erizo/rtp/LayerBitrateCalculationHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ void LayerBitrateCalculationHandler::write(Context *ctx, std::shared_ptr<dataPac
ctx->fireWrite(packet);
return;
}

std::for_each(packet->compatible_spatial_layers.begin(),
packet->compatible_spatial_layers.end(), [this, packet](int &layer_num){
std::string spatial_layer_name = std::to_string(layer_num);
Expand All @@ -40,6 +41,7 @@ void LayerBitrateCalculationHandler::write(Context *ctx, std::shared_ptr<dataPac
}
});
});
quality_manager_->notifyQualityUpdate();
ctx->fireWrite(packet);
}

Expand All @@ -57,6 +59,10 @@ void LayerBitrateCalculationHandler::notifyUpdate() {
if (!stats_) {
return;
}
quality_manager_ = pipeline->getService<QualityManager>();
if (!quality_manager_) {
return;
}
initialized_ = true;
}
} // namespace erizo
4 changes: 3 additions & 1 deletion erizo/src/erizo/rtp/LayerBitrateCalculationHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "./logger.h"
#include "pipeline/Handler.h"
#include "./Stats.h"
#include "rtp/QualityManager.h"

namespace erizo {

Expand All @@ -31,10 +32,11 @@ class LayerBitrateCalculationHandler: public OutboundHandler {
void notifyUpdate() override;

private:
const std::string kQualityLayersStatsKey = "qualityLayers";
bool enabled_;
bool initialized_;
std::shared_ptr<Stats> stats_;
const std::string kQualityLayersStatsKey = "qualityLayers";
std::shared_ptr<QualityManager> quality_manager_;
};
} // namespace erizo

Expand Down
105 changes: 103 additions & 2 deletions erizo/src/erizo/rtp/QualityManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,108 @@ namespace erizo {

DEFINE_LOGGER(QualityManager, "rtp.QualityManager");

QualityManager::QualityManager()
: spatial_layer_{0}, temporal_layer_{0}, padding_enabled_{false} {}
constexpr duration QualityManager::kMinLayerSwitchInterval;
constexpr duration QualityManager::kActiveLayerInterval;

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


void QualityManager::notifyQualityUpdate() {
if (!initialized_) {
auto pipeline = getContext()->getPipelineShared();
if (!pipeline) {
return;
}
stats_ = pipeline->getService<Stats>();
if (!stats_) {
return;
}
if (!stats_->getNode()["total"].hasChild("senderBitrateEstimation")) {
return;
}
initialized_ = true;
}
if (forced_layers_) {
return;
}
time_point now = clock_->now();
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 (!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;
} else if (now - last_quality_check_ > kMinLayerSwitchInterval) {
selectLayer();
}
}

void QualityManager::selectLayer() {
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;
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_) {
next_temporal_layer = aux_temporal_layer;
next_spatial_layer = aux_spatial_layer;
}
aux_temporal_layer++;
}
aux_temporal_layer = 0;
aux_spatial_layer++;
}
if (next_temporal_layer != temporal_layer_ || next_spatial_layer != spatial_layer_) {
ELOG_DEBUG("message: Layer Switch, current_layer: %d/%d, new_layer: %d/%d",
spatial_layer_, temporal_layer_, next_spatial_layer, next_temporal_layer);
setTemporalLayer(next_temporal_layer);
setSpatialLayer(next_spatial_layer);
}
}

uint64_t QualityManager::getInstantLayerBitrate(int spatial_layer, int temporal_layer) {
MovingIntervalRateStat* layer_stat =
reinterpret_cast<MovingIntervalRateStat*>(&stats_->getNode()["qualityLayers"][spatial_layer][temporal_layer]);
return layer_stat->value(kActiveLayerInterval);
}

bool QualityManager::isInBaseLayer() {
return (spatial_layer_ == 0 && temporal_layer_ == 0);
}

void QualityManager::forceLayers(int spatial_layer, int temporal_layer) {
forced_layers_ = true;
spatial_layer_ = spatial_layer;
temporal_layer_ = temporal_layer;
}

void QualityManager::setSpatialLayer(int spatial_layer) {
if (!forced_layers_) {
spatial_layer_ = spatial_layer;
}
}
void QualityManager::setTemporalLayer(int temporal_layer) {
if (!forced_layers_) {
temporal_layer_ = temporal_layer;
}
}

} // namespace erizo
34 changes: 28 additions & 6 deletions erizo/src/erizo/rtp/QualityManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#define ERIZO_SRC_ERIZO_RTP_QUALITYMANAGER_H_

#include "./logger.h"
#include "Stats.h"
#include "lib/Clock.h"
#include "pipeline/Service.h"

namespace erizo {
Expand All @@ -10,20 +12,40 @@ class QualityManager: public Service, public std::enable_shared_from_this<Qualit
DECLARE_LOGGER();

public:
QualityManager();
static constexpr duration kMinLayerSwitchInterval = std::chrono::seconds(2);
static constexpr duration kActiveLayerInterval = std::chrono::milliseconds(500);

virtual int getSpatialLayer() const { return spatial_layer_; }
virtual int getTemporalLayer() const { return temporal_layer_; }
public:
explicit QualityManager(std::shared_ptr<Clock> the_clock = std::make_shared<SteadyClock>());

virtual int getSpatialLayer() const { return spatial_layer_; }
virtual int getTemporalLayer() const { return temporal_layer_; }
void setSpatialLayer(int spatial_layer);
void setTemporalLayer(int temporal_layer);

void setSpatialLayer(int spatial_layer) { spatial_layer_ = spatial_layer; }
void setTemporalLayer(int temporal_layer) { temporal_layer_ = temporal_layer; }
void forceLayers(int spatial_layer, int temporal_layer);

void notifyQualityUpdate();

virtual bool isPaddingEnabled() const { return padding_enabled_; }

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

time_point last_quality_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
2 changes: 2 additions & 0 deletions erizo/src/erizo/stats/StatNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class StatNode {

virtual uint64_t value() { return 0; }

virtual const std::map<std::string, std::shared_ptr<StatNode>>& getMap() {return node_map_;}

virtual std::string toString();

private:
Expand Down
1 change: 1 addition & 0 deletions erizo/src/test/log4cxx.properties
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ log4j.logger.media.codecs.AudioDecoder=ERROR
log4j.logger.media.mixers.VideoMixer=ERROR
log4j.logger.media.mixers.VideoUtils=ERROR

log4j.logger.rtp.QualityManager=ERROR
log4j.logger.rtp.RtcpAggregator=ERROR
log4j.logger.rtp.RtcpForwarder=ERROR
log4j.logger.rtp.RtpExtensionProcessor=ERROR
Expand Down
Loading

0 comments on commit e8ceb00

Please sign in to comment.