Skip to content

Commit

Permalink
Add a padding generator handler (lynckia#794)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcague authored Mar 9, 2017
1 parent f535ac9 commit e1fa4e7
Show file tree
Hide file tree
Showing 21 changed files with 550 additions and 26 deletions.
2 changes: 2 additions & 0 deletions erizo/src/erizo/WebRtcConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "rtp/QualityFilterHandler.h"
#include "rtp/QualityManager.h"
#include "rtp/PliPacerHandler.h"
#include "rtp/RtpPaddingGeneratorHandler.h"

namespace erizo {
DEFINE_LOGGER(WebRtcConnection, "WebRtcConnection");
Expand Down Expand Up @@ -259,6 +260,7 @@ void WebRtcConnection::initializePipeline() {
pipeline_->addFront(QualityFilterHandler());
pipeline_->addFront(RtpAudioMuteHandler());
pipeline_->addFront(RtpSlideShowHandler());
pipeline_->addFront(RtpPaddingGeneratorHandler());
pipeline_->addFront(PliPacerHandler());
pipeline_->addFront(BandwidthEstimationHandler());
pipeline_->addFront(RtcpFeedbackGenerationHandler());
Expand Down
2 changes: 1 addition & 1 deletion erizo/src/erizo/rtp/QualityManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace erizo {
DEFINE_LOGGER(QualityManager, "rtp.QualityManager");

QualityManager::QualityManager()
: spatial_layer_{0}, temporal_layer_{0} {}
: spatial_layer_{0}, temporal_layer_{0}, padding_enabled_{false} {}

} // namespace erizo
7 changes: 5 additions & 2 deletions erizo/src/erizo/rtp/QualityManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ class QualityManager: public Service, public std::enable_shared_from_this<Qualit
public:
QualityManager();

int getSpatialLayer() const { return spatial_layer_; }
int getTemporalLayer() const { return temporal_layer_; }
virtual int getSpatialLayer() const { return spatial_layer_; }
virtual int getTemporalLayer() const { return temporal_layer_; }

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

virtual bool isPaddingEnabled() const { return padding_enabled_; }

private:
int spatial_layer_;
int temporal_layer_;
bool padding_enabled_;
};
} // namespace erizo

Expand Down
6 changes: 3 additions & 3 deletions erizo/src/erizo/rtp/RtcpAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ namespace erizo {

DEFINE_LOGGER(RtcpAggregator, "rtp.RtcpAggregator");

RtcpAggregator::RtcpAggregator(MediaSink* msink, MediaSource* msource, uint32_t maxVideoBw)
: RtcpProcessor(msink, msource, maxVideoBw), defaultVideoBw_(maxVideoBw / 2) {
RtcpAggregator::RtcpAggregator(MediaSink* msink, MediaSource* msource, uint32_t max_video_bw)
: RtcpProcessor(msink, msource, max_video_bw), defaultVideoBw_(max_video_bw / 2) {
ELOG_DEBUG("Starting RtcpAggregator");
}

Expand All @@ -43,7 +43,7 @@ void RtcpAggregator::addSourceSsrc(uint32_t ssrc) {
}

void RtcpAggregator::setPublisherBW(uint32_t bandwidth) {
defaultVideoBw_ = (bandwidth*1.2) > maxVideoBw_? maxVideoBw_:(bandwidth*1.2);
defaultVideoBw_ = (bandwidth*1.2) > max_video_bw_? max_video_bw_:(bandwidth*1.2);
}

void RtcpAggregator::analyzeSr(RtcpHeader* chead) {
Expand Down
2 changes: 1 addition & 1 deletion erizo/src/erizo/rtp/RtcpAggregator.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class RtcpAggregator: public RtcpProcessor{
DECLARE_LOGGER();

public:
RtcpAggregator(MediaSink* msink, MediaSource* msource, uint32_t maxVideoBw = 300000);
RtcpAggregator(MediaSink* msink, MediaSource* msource, uint32_t max_video_bw = 300000);
virtual ~RtcpAggregator() {}
void addSourceSsrc(uint32_t ssrc);
void setPublisherBW(uint32_t bandwidth);
Expand Down
10 changes: 5 additions & 5 deletions erizo/src/erizo/rtp/RtcpForwarder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ using std::memcpy;
namespace erizo {
DEFINE_LOGGER(RtcpForwarder, "rtp.RtcpForwarder");

RtcpForwarder::RtcpForwarder(MediaSink* msink, MediaSource* msource, uint32_t maxVideoBw)
: RtcpProcessor(msink, msource, maxVideoBw) {
RtcpForwarder::RtcpForwarder(MediaSink* msink, MediaSource* msource, uint32_t max_video_bw)
: RtcpProcessor(msink, msource, max_video_bw) {
ELOG_DEBUG("Starting RtcpForwarder");
}

Expand Down Expand Up @@ -132,11 +132,11 @@ int RtcpForwarder::analyzeFeedback(char *buf, int len) {
if (!strncmp(uniqueId, "REMB", 4)) {
uint64_t bitrate = chead->getBrMantis() << chead->getBrExp();
uint64_t cappedBitrate = 0;
cappedBitrate = bitrate < maxVideoBw_? bitrate: maxVideoBw_;
if (bitrate < maxVideoBw_) {
cappedBitrate = bitrate < max_video_bw_? bitrate: max_video_bw_;
if (bitrate < max_video_bw_) {
cappedBitrate = bitrate;
} else {
cappedBitrate = maxVideoBw_;
cappedBitrate = max_video_bw_;
}
ELOG_DEBUG("Received REMB %llu, partnum %u, cappedBitrate %llu",
bitrate, currentBlock, cappedBitrate);
Expand Down
2 changes: 1 addition & 1 deletion erizo/src/erizo/rtp/RtcpForwarder.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class RtcpForwarder: public RtcpProcessor{
DECLARE_LOGGER();

public:
RtcpForwarder(MediaSink* msink, MediaSource* msource, uint32_t maxVideoBw = 300000);
RtcpForwarder(MediaSink* msink, MediaSource* msource, uint32_t max_video_bw = 300000);
virtual ~RtcpForwarder() {}
void addSourceSsrc(uint32_t ssrc);
void setPublisherBW(uint32_t bandwidth);
Expand Down
10 changes: 5 additions & 5 deletions erizo/src/erizo/rtp/RtcpProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,22 @@ class RtcpData {

class RtcpProcessor : public Service {
public:
RtcpProcessor(MediaSink* msink, MediaSource* msource, uint32_t maxVideoBw = 300000):
rtcpSink_(msink), rtcpSource_(msource) {}
RtcpProcessor(MediaSink* msink, MediaSource* msource, uint32_t max_video_bw = 300000):
rtcpSink_(msink), rtcpSource_(msource), max_video_bw_{max_video_bw} {}
virtual ~RtcpProcessor() {}
virtual void addSourceSsrc(uint32_t ssrc) = 0;
virtual void setPublisherBW(uint32_t bandwidth) = 0;
virtual void analyzeSr(RtcpHeader* chead) = 0;
virtual int analyzeFeedback(char* buf, int len) = 0;
virtual void checkRtcpFb() = 0;

virtual void setMaxVideoBW(uint32_t bandwidth) { maxVideoBw_ = bandwidth; }
virtual uint32_t getMaxVideoBW() { return maxVideoBw_; }
virtual void setMaxVideoBW(uint32_t bandwidth) { max_video_bw_ = bandwidth; }
virtual uint32_t getMaxVideoBW() { return max_video_bw_; }

protected:
MediaSink* rtcpSink_; // The sink to send RRs
MediaSource* rtcpSource_; // The source of SRs
uint32_t maxVideoBw_;
uint32_t max_video_bw_;
};

} // namespace erizo
Expand Down
4 changes: 4 additions & 0 deletions erizo/src/erizo/rtp/RtpHeaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ class RtpHeader {
return padding;
}

inline void setPadding(uint8_t has_padding) {
padding = has_padding;
}

inline uint8_t getVersion() const {
return version;
}
Expand Down
217 changes: 217 additions & 0 deletions erizo/src/erizo/rtp/RtpPaddingGeneratorHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#include "rtp/RtpPaddingGeneratorHandler.h"

#include <algorithm>
#include <string>

#include "./MediaDefinitions.h"
#include "./WebRtcConnection.h"
#include "./RtpUtils.h"

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;

RtpPaddingGeneratorHandler::RtpPaddingGeneratorHandler(std::shared_ptr<erizo::Clock> the_clock) :
clock_{the_clock}, connection_{nullptr}, max_video_bw_{0}, higher_sequence_number_{0},
video_sink_ssrc_{0}, audio_source_ssrc_{0},
number_of_full_padding_packets_{0}, last_padding_packet_size_{0},
last_rate_calculation_time_{clock_->now()}, started_at_{clock_->now()},
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} {}


void RtpPaddingGeneratorHandler::enable() {
}

void RtpPaddingGeneratorHandler::disable() {
}

void RtpPaddingGeneratorHandler::notifyUpdate() {
auto pipeline = getContext()->getPipelineShared();
if (pipeline && !connection_) {
connection_ = pipeline->getService<WebRtcConnection>().get();
video_sink_ssrc_ = connection_->getVideoSinkSSRC();
audio_source_ssrc_ = connection_->getAudioSinkSSRC();
stats_ = pipeline->getService<Stats>();
}

auto quality_manager = pipeline->getService<QualityManager>();

if (quality_manager->isPaddingEnabled() && !enabled_) {
enablePadding();
} else if (!quality_manager->isPaddingEnabled() && enabled_) {
disablePadding();
}

auto processor = pipeline->getService<RtcpProcessor>();
if (processor) {
max_video_bw_ = processor->getMaxVideoBW();
}
}

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);
}

void RtpPaddingGeneratorHandler::write(Context *ctx, std::shared_ptr<dataPacket> packet) {
RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(packet->data);
bool is_higher_sequence_number = false;
if (packet->type == VIDEO_PACKET && !chead->isRtcp()) {
is_higher_sequence_number = isHigherSequenceNumber(packet);
if (!first_packet_received_) {
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);

if (is_higher_sequence_number) {
onVideoPacket(packet);
}
}

void RtpPaddingGeneratorHandler::sendPaddingPacket(std::shared_ptr<dataPacket> packet, uint8_t padding_size) {
if (padding_size == 0) {
return;
}

SequenceNumber sequence_number = translator_.generate();

auto padding_packet = RtpUtils::makePaddingPacket(packet, padding_size);

RtpHeader *rtp_header = reinterpret_cast<RtpHeader*>(padding_packet->data);

rtp_header->setSeqNumber(sequence_number.output);
padding_bitrate_ += padding_packet->length;

getContext()->fireWrite(padding_packet);
}

void RtpPaddingGeneratorHandler::onPacketWithMarkerSet(std::shared_ptr<dataPacket> packet) {
marker_rate_++;

for (int i = 0; i < number_of_full_padding_packets_; i++) {
sendPaddingPacket(packet, kMaxPaddingSize);
}
sendPaddingPacket(packet, last_padding_packet_size_);
}

bool RtpPaddingGeneratorHandler::isHigherSequenceNumber(std::shared_ptr<dataPacket> packet) {
RtpHeader *rtp_header = reinterpret_cast<RtpHeader*>(packet->data);
rtp_header_length_ = rtp_header->getHeaderLength();
uint16_t new_sequence_number = rtp_header->getSeqNumber();
SequenceNumber sequence_number = translator_.get(new_sequence_number, false);
rtp_header->setSeqNumber(sequence_number.output);
if (first_packet_received_ && RtpUtils::sequenceNumberLessThan(new_sequence_number, higher_sequence_number_)) {
return false;
}
higher_sequence_number_ = new_sequence_number;
return true;
}

void RtpPaddingGeneratorHandler::onVideoPacket(std::shared_ptr<dataPacket> packet) {
if (!enabled_) {
return;
}

recalculatePaddingRate();

RtpHeader *rtp_header = reinterpret_cast<RtpHeader*>(packet->data);
if (rtp_header->getMarker()) {
onPacketWithMarkerSet(packet);
}
}

uint64_t RtpPaddingGeneratorHandler::getStat(std::string stat_name) {
if (stats_->getNode()["total"].hasChild(stat_name)) {
StatNode & stat = stats_->getNode()["total"][stat_name];
return static_cast<MovingIntervalRateStat&>(stat).value();
}
return 0;
}

bool RtpPaddingGeneratorHandler::isTimeToCalculateBitrate() {
return (clock_->now() - last_rate_calculation_time_) >= kStatsPeriod;
}

void RtpPaddingGeneratorHandler::recalculatePaddingRate() {
if (!isTimeToCalculateBitrate()) {
return;
}

last_rate_calculation_time_ = clock_->now();

int64_t total_bitrate = getStat("bitrateCalculated");
int64_t padding_bitrate = padding_bitrate_.value();
int64_t media_bitrate = std::max(total_bitrate - padding_bitrate, int64_t(0));

uint64_t target_bitrate = getTargetBitrate();

int64_t target_padding_bitrate = target_bitrate - media_bitrate;

if (target_padding_bitrate <= 0) {
number_of_full_padding_packets_ = 0;
last_padding_packet_size_ = 0;
return;
}

uint64_t marker_rate = marker_rate_.value(std::chrono::seconds(2));
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_;

if (!fast_start_) {
target_bitrate = getStat("senderBitrateEstimation");
}

if (max_video_bw_ > 0) {
target_bitrate = std::min(target_bitrate, max_video_bw_);
}
return target_bitrate;
}

void RtpPaddingGeneratorHandler::enablePadding() {
enabled_ = true;
number_of_full_padding_packets_ = 0;
last_padding_packet_size_ = 0;
last_rate_calculation_time_ = clock_->now();
}

void RtpPaddingGeneratorHandler::disablePadding() {
enabled_ = false;
}

} // namespace erizo
Loading

0 comments on commit e1fa4e7

Please sign in to comment.