Skip to content

Commit

Permalink
Merge pull request lynckia#40 from ging/master
Browse files Browse the repository at this point in the history
Merge from ging:master
  • Loading branch information
jcague committed Jan 4, 2017
2 parents 27fdece + 332fe07 commit 5446838
Show file tree
Hide file tree
Showing 21 changed files with 1,275 additions and 216 deletions.
26 changes: 26 additions & 0 deletions erizo/src/erizo/lib/Clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,31 @@ using clock = std::chrono::steady_clock;
using time_point = std::chrono::steady_clock::time_point;
using duration = std::chrono::steady_clock::duration;

class Clock {
public:
virtual time_point now() = 0;
};

class SteadyClock : public Clock {
public:
time_point now() override {
return clock::now();
}
};

class SimulatedClock : public Clock {
public:
SimulatedClock() : now_{clock::now()} {}

time_point now() override {
return now_;
}

void advanceTime(duration duration) {
now_ += duration;
}
private:
time_point now_;
};
} // namespace erizo
#endif // ERIZO_SRC_ERIZO_LIB_CLOCK_H_
221 changes: 221 additions & 0 deletions erizo/src/erizo/media/SyntheticInput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#include "media/SyntheticInput.h"

#include <algorithm>

#include "lib/Clock.h"
#include "lib/ClockUtils.h"
#include "rtp/RtpHeaders.h"

static constexpr auto kPeriod = std::chrono::milliseconds(20);
static constexpr size_t kMaxPacketSize = 1200;
static constexpr uint8_t kMaxConsecutiveTicks = 20;
static constexpr size_t kVp8PayloadType = 100;
static constexpr size_t kOpusPayloadType = 111;
static constexpr uint32_t kDefaultVideoSsrc = 55543;
static constexpr uint32_t kDefaultAudioSsrc = 44444;
static constexpr auto kAudioPeriod = std::chrono::milliseconds(20);
static constexpr size_t kVideoFramesPerSecond = 15;
static constexpr auto kVideoPeriod = std::chrono::milliseconds(1000 / kVideoFramesPerSecond);
static constexpr auto kDefaultVideoKeyframePeriod = std::chrono::seconds(120);
static constexpr uint32_t kDefaultVideoBitrate = 300000; // bps
static constexpr uint32_t kDefaultAudioBitrate = 30000; // bps
static constexpr uint32_t kVideoSampleRate = 90000; // Hz
static constexpr uint32_t kAudioSampleRate = 48000; // Hz

namespace erizo {
DEFINE_LOGGER(SyntheticInput, "media.SyntheticInput");
SyntheticInput::SyntheticInput(SyntheticInputConfig config,
std::shared_ptr<Worker> worker,
std::shared_ptr<Clock> the_clock)
: clock_{the_clock},
config_{config},
worker_{worker},
video_avg_frame_size_{0},
video_dev_frame_size_{0},
video_avg_keyframe_size_{0},
video_dev_keyframe_size_{0},
video_period_{kVideoPeriod},
audio_frame_size_{0},
audio_period_{kAudioPeriod},
generator_{random_device_()},
running_{false},
video_seq_number_{0},
audio_seq_number_{0},
video_ssrc_{kDefaultVideoSsrc},
audio_ssrc_{kDefaultAudioSsrc},
total_packets_nacked_{0},
video_pt_{kVp8PayloadType},
audio_pt_{kOpusPayloadType},
next_video_frame_time_{clock_->now() + video_period_},
next_audio_frame_time_{clock_->now() + audio_period_},
last_video_keyframe_time_{clock_->now()},
consecutive_ticks_{0},
keyframe_requested_{true} {
calculateSizeAndPeriod(kDefaultVideoBitrate, kDefaultAudioBitrate);
}

SyntheticInput::~SyntheticInput() {
close();
}

void SyntheticInput::start() {
if (running_) {
return;
}
running_ = true;
std::weak_ptr<SyntheticInput> weak_this = shared_from_this();
worker_->scheduleEvery([weak_this] {
if (auto this_ptr = weak_this.lock()) {
if (!this_ptr->running_) {
return false;
}
this_ptr->tick();
return true;
}
return false;
}, kPeriod);
}

void SyntheticInput::tick() {
// This method will be called periodically to send audio/video frames
time_point now = clock_->now();
if (now >= next_audio_frame_time_) {
sendAudioFrame(audio_frame_size_);
next_audio_frame_time_ += audio_period_;
}
if (now >= next_video_frame_time_) {
bool is_keyframe = false;
size_t frame_size = getRandomValue(video_avg_frame_size_, video_dev_frame_size_);
if (now - last_video_keyframe_time_ > kDefaultVideoKeyframePeriod || keyframe_requested_) {
is_keyframe = true;
frame_size = getRandomValue(video_avg_keyframe_size_, video_dev_keyframe_size_);
}
while (frame_size > kMaxPacketSize) {
sendVideoframe(is_keyframe, false, kMaxPacketSize);
is_keyframe = false;
frame_size = frame_size - kMaxPacketSize;
}
sendVideoframe(is_keyframe, true, frame_size);

next_video_frame_time_ += video_period_;
}
now = clock_->now();
if ((next_video_frame_time_ <= now || next_audio_frame_time_ <= now) && consecutive_ticks_ < kMaxConsecutiveTicks) {
consecutive_ticks_++;
tick();
} else {
consecutive_ticks_ = 0;
}
}

int SyntheticInput::sendPLI() {
keyframe_requested_ = true;
return 0;
}

void SyntheticInput::sendVideoframe(bool is_keyframe, bool is_marker, uint32_t size) {
erizo::RtpHeader *header = new erizo::RtpHeader();
header->setSeqNumber(video_seq_number_++);
header->setTimestamp(ClockUtils::timePointToMs(clock_->now()) * kVideoSampleRate / 1000);
header->setSSRC(video_ssrc_);
header->setMarker(is_marker);
header->setPayloadType(video_pt_);
char packet_buffer[kMaxPacketSize];
memset(packet_buffer, 0, size);
char* data_pointer;
char* parsing_pointer;
memcpy(packet_buffer, reinterpret_cast<char*>(header), header->getHeaderLength());
data_pointer = packet_buffer + header->getHeaderLength();
parsing_pointer = data_pointer;
*parsing_pointer = 0x10;
parsing_pointer++;
*parsing_pointer = is_keyframe ? 0x00 : 0x01;

if (is_keyframe) {
last_video_keyframe_time_ = clock_->now();
keyframe_requested_ = false;
}
if (videoSink_) {
videoSink_->deliverVideoData(packet_buffer, size);
}
delete header;
}

void SyntheticInput::sendAudioFrame(uint32_t size) {
erizo::RtpHeader *header = new erizo::RtpHeader();
header->setSeqNumber(audio_seq_number_++);
header->setTimestamp(ClockUtils::timePointToMs(clock_->now()) * (kAudioSampleRate / 1000));
header->setSSRC(audio_ssrc_);
header->setMarker(true);
header->setPayloadType(audio_pt_);
char packet_buffer[kMaxPacketSize];
memset(packet_buffer, 0, size);
memcpy(packet_buffer, reinterpret_cast<char*>(header), header->getHeaderLength());
if (audioSink_) {
audioSink_->deliverAudioData(packet_buffer, size);
}
delete header;
}

void SyntheticInput::close() {
running_ = false;
}

void SyntheticInput::calculateSizeAndPeriod(uint32_t video_bitrate, uint32_t audio_bitrate) {
video_bitrate = std::min(video_bitrate, config_.getMaxVideoBitrate());
video_bitrate = std::max(video_bitrate, config_.getMinVideoBitrate());

auto video_period = std::chrono::duration_cast<std::chrono::milliseconds>(video_period_);
auto audio_period = std::chrono::duration_cast<std::chrono::milliseconds>(audio_period_);

video_avg_frame_size_ = video_period.count() * video_bitrate / 8000;
video_dev_frame_size_ = video_avg_frame_size_ * 0.1;
video_avg_keyframe_size_ = video_period.count() * video_bitrate / 8000;
video_dev_keyframe_size_ = video_avg_keyframe_size_ * 0.01;
audio_frame_size_ = audio_period.count() * audio_bitrate / 8000;
}

int SyntheticInput::deliverFeedback_(char* buf, int len) {
RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(buf);
if (chead->isFeedback()) {
if (chead->getBlockCount() == 0 && (chead->getLength()+1) * 4 == len) {
return 0;
}
char* moving_buf = buf;
int rtcp_length = 0;
int total_length = 0;
do {
moving_buf += rtcp_length;
chead = reinterpret_cast<RtcpHeader*>(moving_buf);
rtcp_length = (ntohs(chead->length) + 1) * 4;
total_length += rtcp_length;
switch (chead->packettype) {
case RTCP_RTP_Feedback_PT:
// NACKs are already handled by WebRtcConnection. RRs won't be handled.
total_packets_nacked_++;
break;
case RTCP_PS_Feedback_PT:
switch (chead->getBlockCount()) {
case RTCP_PLI_FMT:
case RTCP_FIR_FMT:
sendPLI();
break;
case RTCP_AFB:
char *unique_id = reinterpret_cast<char*>(&chead->report.rembPacket.uniqueid);
if (!strncmp(unique_id, "REMB", 4)) {
uint64_t bitrate = chead->getBrMantis() << chead->getBrExp();
calculateSizeAndPeriod(bitrate, kDefaultAudioBitrate);
}
break;
}
}
} while (total_length < len);
}
return 0;
}

uint32_t SyntheticInput::getRandomValue(uint32_t average, uint32_t deviation) {
std::normal_distribution<> distr(average, deviation);
return std::round(distr(generator_));
}
} // namespace erizo
85 changes: 85 additions & 0 deletions erizo/src/erizo/media/SyntheticInput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifndef ERIZO_SRC_ERIZO_MEDIA_SYNTHETICINPUT_H_
#define ERIZO_SRC_ERIZO_MEDIA_SYNTHETICINPUT_H_

#include <chrono> // NOLINT
#include <random>

#include "./logger.h"
#include "./MediaDefinitions.h"
#include "thread/Worker.h"
#include "lib/Clock.h"

namespace erizo {

class SyntheticInputConfig {
public:
SyntheticInputConfig(uint32_t audio_bitrate, uint32_t min_video_bitrate, uint32_t max_video_bitrate) :
audio_bitrate_{audio_bitrate}, min_video_bitrate_{min_video_bitrate}, max_video_bitrate_{max_video_bitrate} {}

uint32_t getMinVideoBitrate() {
return min_video_bitrate_;
}

uint32_t getMaxVideoBitrate() {
return max_video_bitrate_;
}

uint32_t getAudioBitrate() {
return audio_bitrate_;
}

private:
uint32_t audio_bitrate_;
uint32_t min_video_bitrate_;
uint32_t max_video_bitrate_;
};

class SyntheticInput : public MediaSource, public FeedbackSink, public std::enable_shared_from_this<SyntheticInput> {
DECLARE_LOGGER();

public:
explicit SyntheticInput(SyntheticInputConfig config, std::shared_ptr<Worker> worker,
std::shared_ptr<Clock> the_clock = std::make_shared<SteadyClock>());
virtual ~SyntheticInput();
int sendPLI() override;
void close() override;
void start();

private:
void tick();
void calculateSizeAndPeriod(uint32_t video_bitrate, uint32_t audio_bitrate);
int deliverFeedback_(char* buf, int len) override;
void sendVideoframe(bool is_keyframe, bool is_marker, uint32_t size);
void sendAudioFrame(uint32_t size);
uint32_t getRandomValue(uint32_t average, uint32_t variation);
void scheduleEvery(duration period);

private:
std::shared_ptr<Clock> clock_;
SyntheticInputConfig config_;
std::shared_ptr<Worker> worker_;
uint32_t video_avg_frame_size_;
uint32_t video_dev_frame_size_;
uint32_t video_avg_keyframe_size_;
uint32_t video_dev_keyframe_size_;
duration video_period_;
uint32_t audio_frame_size_;
duration audio_period_;
std::random_device random_device_;
std::mt19937 generator_;
bool running_;
uint32_t video_seq_number_;
uint32_t audio_seq_number_;
uint32_t video_ssrc_;
uint32_t audio_ssrc_;
uint32_t total_packets_nacked_;
size_t video_pt_;
size_t audio_pt_;
time_point next_video_frame_time_;
time_point next_audio_frame_time_;
time_point last_video_keyframe_time_;
uint8_t consecutive_ticks_;
std::atomic<bool> keyframe_requested_;
};
} // namespace erizo
#endif // ERIZO_SRC_ERIZO_MEDIA_SYNTHETICINPUT_H_
6 changes: 3 additions & 3 deletions erizo/src/erizo/rtp/BandwidthEstimationHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ const uint32_t BandwidthEstimationHandler::kRembMinimumBitrate = 20000;
const unsigned int kSendThresholdPercent = 97;

std::unique_ptr<RemoteBitrateEstimator> RemoteBitrateEstimatorPicker::pickEstimator(bool using_absolute_send_time,
Clock* const clock,
webrtc::Clock* const clock,
RemoteBitrateObserver *observer) {
std::unique_ptr<RemoteBitrateEstimator> rbe;
if (using_absolute_send_time) {
rbe.reset(new RemoteBitrateEstimatorAbsSendTime(observer, clock));
rbe.reset(new webrtc::RemoteBitrateEstimatorAbsSendTime(observer, clock));
} else {
rbe.reset(new RemoteBitrateEstimatorSingleStream(observer, clock));
rbe.reset(new webrtc::RemoteBitrateEstimatorSingleStream(observer, clock));
}
return rbe;
}
Expand Down
5 changes: 2 additions & 3 deletions erizo/src/erizo/rtp/BandwidthEstimationHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ class WebRtcConnection;

using webrtc::RemoteBitrateEstimator;
using webrtc::RemoteBitrateObserver;
using webrtc::Clock;
using webrtc::RtpHeaderExtensionMap;

class RemoteBitrateEstimatorPicker {
public:
virtual std::unique_ptr<RemoteBitrateEstimator> pickEstimator(bool using_absolute_send_time,
Clock* const clock, RemoteBitrateObserver *observer);
webrtc::Clock* const clock, RemoteBitrateObserver *observer);
};

class BandwidthEstimationHandler: public Handler, public RemoteBitrateObserver,
Expand Down Expand Up @@ -60,7 +59,7 @@ class BandwidthEstimationHandler: public Handler, public RemoteBitrateObserver,

WebRtcConnection *connection_;
std::shared_ptr<Worker> worker_;
Clock* const clock_;
webrtc::Clock* const clock_;
std::shared_ptr<RemoteBitrateEstimatorPicker> picker_;
std::unique_ptr<RemoteBitrateEstimator> rbe_;
bool using_absolute_send_time_;
Expand Down
Loading

0 comments on commit 5446838

Please sign in to comment.