forked from lynckia/licode
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request lynckia#40 from ging/master
Merge from ging:master
- Loading branch information
Showing
21 changed files
with
1,275 additions
and
216 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.