Reland "Reland "Refactors UlpFec and FlexFec to use a common interface.""
This is a reland of 49734dc0faa69616a58a1a95c7fc61a4610793cf Patchset 2 contains a fix for the fuzzer set up. Since we now parse an RtpPacket out of the fuzzer data, the header needs to be correct, otherwise we fail before even reaching the FEC code that we actually want to test. Bug: webrtc:11340, chromium:1052323, chromium:1055974 TBR=stefan@webrtc.org Original change's description: > Reland "Refactors UlpFec and FlexFec to use a common interface." > > This is a reland of 11af1d7444fd7438766b7bc52cbd64752d72e32e > > Original change's description: > > Refactors UlpFec and FlexFec to use a common interface. > > > > The new VideoFecGenerator is now injected into RtpSenderVideo, > > and generalizes the usage. > > This also prepares for being able to genera FEC in the RTP egress > > module. > > > > Bug: webrtc:11340 > > Change-Id: I8aa873129b2fb4131eb3399ee88f6ea2747155a3 > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168347 > > Reviewed-by: Stefan Holmer <stefan@webrtc.org> > > Reviewed-by: Sebastian Jansson <srte@webrtc.org> > > Reviewed-by: Rasmus Brandt <brandtr@webrtc.org> > > Commit-Queue: Erik Språng <sprang@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#30515} > > Bug: webrtc:11340, chromium:1052323 > Change-Id: Id646047365f1c46cca9e6f3e8eefa5151207b4a0 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168608 > Commit-Queue: Erik Språng <sprang@webrtc.org> > Reviewed-by: Stefan Holmer <stefan@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#30593} Bug: webrtc:11340, chromium:1052323 Change-Id: Ib8925f44e2edfcfeadc95c845c3bfc23822604ed Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/169222 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30724}
This commit is contained in:
parent
269d68f521
commit
f87536c9de
@ -36,9 +36,13 @@ namespace webrtc {
|
|||||||
|
|
||||||
namespace webrtc_internal_rtp_video_sender {
|
namespace webrtc_internal_rtp_video_sender {
|
||||||
|
|
||||||
RtpStreamSender::RtpStreamSender(std::unique_ptr<RtpRtcp> rtp_rtcp,
|
RtpStreamSender::RtpStreamSender(
|
||||||
std::unique_ptr<RTPSenderVideo> sender_video)
|
std::unique_ptr<RtpRtcp> rtp_rtcp,
|
||||||
: rtp_rtcp(std::move(rtp_rtcp)), sender_video(std::move(sender_video)) {}
|
std::unique_ptr<RTPSenderVideo> sender_video,
|
||||||
|
std::unique_ptr<VideoFecGenerator> fec_generator)
|
||||||
|
: rtp_rtcp(std::move(rtp_rtcp)),
|
||||||
|
sender_video(std::move(sender_video)),
|
||||||
|
fec_generator(std::move(fec_generator)) {}
|
||||||
|
|
||||||
RtpStreamSender::~RtpStreamSender() = default;
|
RtpStreamSender::~RtpStreamSender() = default;
|
||||||
|
|
||||||
@ -113,6 +117,67 @@ bool ShouldDisableRedAndUlpfec(bool flexfec_enabled,
|
|||||||
return should_disable_red_and_ulpfec;
|
return should_disable_red_and_ulpfec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(brandtr): Update this function when we support multistream protection.
|
||||||
|
std::unique_ptr<VideoFecGenerator> MaybeCreateFecGenerator(
|
||||||
|
Clock* clock,
|
||||||
|
const RtpConfig& rtp,
|
||||||
|
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
||||||
|
int simulcast_index) {
|
||||||
|
// If flexfec is configured that takes priority.
|
||||||
|
if (rtp.flexfec.payload_type >= 0) {
|
||||||
|
RTC_DCHECK_GE(rtp.flexfec.payload_type, 0);
|
||||||
|
RTC_DCHECK_LE(rtp.flexfec.payload_type, 127);
|
||||||
|
if (rtp.flexfec.ssrc == 0) {
|
||||||
|
RTC_LOG(LS_WARNING) << "FlexFEC is enabled, but no FlexFEC SSRC given. "
|
||||||
|
"Therefore disabling FlexFEC.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (rtp.flexfec.protected_media_ssrcs.empty()) {
|
||||||
|
RTC_LOG(LS_WARNING)
|
||||||
|
<< "FlexFEC is enabled, but no protected media SSRC given. "
|
||||||
|
"Therefore disabling FlexFEC.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtp.flexfec.protected_media_ssrcs.size() > 1) {
|
||||||
|
RTC_LOG(LS_WARNING)
|
||||||
|
<< "The supplied FlexfecConfig contained multiple protected "
|
||||||
|
"media streams, but our implementation currently only "
|
||||||
|
"supports protecting a single media stream. "
|
||||||
|
"To avoid confusion, disabling FlexFEC completely.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (absl::c_find(rtp.flexfec.protected_media_ssrcs,
|
||||||
|
rtp.ssrcs[simulcast_index]) ==
|
||||||
|
rtp.flexfec.protected_media_ssrcs.end()) {
|
||||||
|
// Media SSRC not among flexfec protected SSRCs.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RtpState* rtp_state = nullptr;
|
||||||
|
auto it = suspended_ssrcs.find(rtp.flexfec.ssrc);
|
||||||
|
if (it != suspended_ssrcs.end()) {
|
||||||
|
rtp_state = &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTC_DCHECK_EQ(1U, rtp.flexfec.protected_media_ssrcs.size());
|
||||||
|
return std::make_unique<FlexfecSender>(
|
||||||
|
rtp.flexfec.payload_type, rtp.flexfec.ssrc,
|
||||||
|
rtp.flexfec.protected_media_ssrcs[0], rtp.mid, rtp.extensions,
|
||||||
|
RTPSender::FecExtensionSizes(), rtp_state, clock);
|
||||||
|
} else if (rtp.ulpfec.red_payload_type >= 0 &&
|
||||||
|
rtp.ulpfec.ulpfec_payload_type >= 0 &&
|
||||||
|
!ShouldDisableRedAndUlpfec(/*flexfec_enabled=*/false, rtp)) {
|
||||||
|
// Flexfec not configured, but ulpfec is and is not disabled.
|
||||||
|
return std::make_unique<UlpfecGenerator>(
|
||||||
|
rtp.ulpfec.red_payload_type, rtp.ulpfec.ulpfec_payload_type, clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a single FEC is given.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<RtpStreamSender> CreateRtpStreamSenders(
|
std::vector<RtpStreamSender> CreateRtpStreamSenders(
|
||||||
Clock* clock,
|
Clock* clock,
|
||||||
const RtpConfig& rtp_config,
|
const RtpConfig& rtp_config,
|
||||||
@ -121,7 +186,7 @@ std::vector<RtpStreamSender> CreateRtpStreamSenders(
|
|||||||
Transport* send_transport,
|
Transport* send_transport,
|
||||||
RtcpBandwidthObserver* bandwidth_callback,
|
RtcpBandwidthObserver* bandwidth_callback,
|
||||||
RtpTransportControllerSendInterface* transport,
|
RtpTransportControllerSendInterface* transport,
|
||||||
FlexfecSender* flexfec_sender,
|
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
||||||
RtcEventLog* event_log,
|
RtcEventLog* event_log,
|
||||||
RateLimiter* retransmission_rate_limiter,
|
RateLimiter* retransmission_rate_limiter,
|
||||||
OverheadObserver* overhead_observer,
|
OverheadObserver* overhead_observer,
|
||||||
@ -161,18 +226,17 @@ std::vector<RtpStreamSender> CreateRtpStreamSenders(
|
|||||||
configuration.rtcp_report_interval_ms = rtcp_report_interval_ms;
|
configuration.rtcp_report_interval_ms = rtcp_report_interval_ms;
|
||||||
|
|
||||||
std::vector<RtpStreamSender> rtp_streams;
|
std::vector<RtpStreamSender> rtp_streams;
|
||||||
const std::vector<uint32_t>& flexfec_protected_ssrcs =
|
|
||||||
rtp_config.flexfec.protected_media_ssrcs;
|
|
||||||
RTC_DCHECK(rtp_config.rtx.ssrcs.empty() ||
|
RTC_DCHECK(rtp_config.rtx.ssrcs.empty() ||
|
||||||
rtp_config.rtx.ssrcs.size() == rtp_config.rtx.ssrcs.size());
|
rtp_config.rtx.ssrcs.size() == rtp_config.rtx.ssrcs.size());
|
||||||
for (size_t i = 0; i < rtp_config.ssrcs.size(); ++i) {
|
for (size_t i = 0; i < rtp_config.ssrcs.size(); ++i) {
|
||||||
|
RTPSenderVideo::Config video_config;
|
||||||
configuration.local_media_ssrc = rtp_config.ssrcs[i];
|
configuration.local_media_ssrc = rtp_config.ssrcs[i];
|
||||||
bool enable_flexfec = flexfec_sender != nullptr &&
|
|
||||||
std::find(flexfec_protected_ssrcs.begin(),
|
std::unique_ptr<VideoFecGenerator> fec_generator =
|
||||||
flexfec_protected_ssrcs.end(),
|
MaybeCreateFecGenerator(clock, rtp_config, suspended_ssrcs, i);
|
||||||
configuration.local_media_ssrc) !=
|
configuration.fec_generator = fec_generator.get();
|
||||||
flexfec_protected_ssrcs.end();
|
video_config.fec_generator = fec_generator.get();
|
||||||
configuration.flexfec_sender = enable_flexfec ? flexfec_sender : nullptr;
|
|
||||||
|
|
||||||
if (rtp_config.rtx.ssrcs.size() > i) {
|
if (rtp_config.rtx.ssrcs.size() > i) {
|
||||||
configuration.rtx_send_ssrc = rtp_config.rtx.ssrcs[i];
|
configuration.rtx_send_ssrc = rtp_config.rtx.ssrcs[i];
|
||||||
@ -188,76 +252,31 @@ std::vector<RtpStreamSender> CreateRtpStreamSenders(
|
|||||||
rtp_rtcp->SetStorePacketsStatus(true, kMinSendSidePacketHistorySize);
|
rtp_rtcp->SetStorePacketsStatus(true, kMinSendSidePacketHistorySize);
|
||||||
|
|
||||||
FieldTrialBasedConfig field_trial_config;
|
FieldTrialBasedConfig field_trial_config;
|
||||||
RTPSenderVideo::Config video_config;
|
|
||||||
video_config.clock = configuration.clock;
|
video_config.clock = configuration.clock;
|
||||||
video_config.rtp_sender = rtp_rtcp->RtpSender();
|
video_config.rtp_sender = rtp_rtcp->RtpSender();
|
||||||
video_config.flexfec_sender = configuration.flexfec_sender;
|
|
||||||
video_config.frame_encryptor = frame_encryptor;
|
video_config.frame_encryptor = frame_encryptor;
|
||||||
video_config.require_frame_encryption =
|
video_config.require_frame_encryption =
|
||||||
crypto_options.sframe.require_frame_encryption;
|
crypto_options.sframe.require_frame_encryption;
|
||||||
video_config.enable_retransmit_all_layers = false;
|
video_config.enable_retransmit_all_layers = false;
|
||||||
video_config.field_trials = &field_trial_config;
|
video_config.field_trials = &field_trial_config;
|
||||||
|
|
||||||
|
const bool using_flexfec =
|
||||||
|
fec_generator &&
|
||||||
|
fec_generator->GetFecType() == VideoFecGenerator::FecType::kFlexFec;
|
||||||
const bool should_disable_red_and_ulpfec =
|
const bool should_disable_red_and_ulpfec =
|
||||||
ShouldDisableRedAndUlpfec(enable_flexfec, rtp_config);
|
ShouldDisableRedAndUlpfec(using_flexfec, rtp_config);
|
||||||
if (rtp_config.ulpfec.red_payload_type != -1 &&
|
if (!should_disable_red_and_ulpfec &&
|
||||||
!should_disable_red_and_ulpfec) {
|
rtp_config.ulpfec.red_payload_type != -1) {
|
||||||
video_config.red_payload_type = rtp_config.ulpfec.red_payload_type;
|
video_config.red_payload_type = rtp_config.ulpfec.red_payload_type;
|
||||||
}
|
}
|
||||||
if (rtp_config.ulpfec.ulpfec_payload_type != -1 &&
|
|
||||||
!should_disable_red_and_ulpfec) {
|
|
||||||
video_config.ulpfec_payload_type = rtp_config.ulpfec.ulpfec_payload_type;
|
|
||||||
}
|
|
||||||
video_config.frame_transformer = std::move(frame_transformer);
|
video_config.frame_transformer = std::move(frame_transformer);
|
||||||
auto sender_video = std::make_unique<RTPSenderVideo>(video_config);
|
auto sender_video = std::make_unique<RTPSenderVideo>(video_config);
|
||||||
rtp_streams.emplace_back(std::move(rtp_rtcp), std::move(sender_video));
|
rtp_streams.emplace_back(std::move(rtp_rtcp), std::move(sender_video),
|
||||||
|
std::move(fec_generator));
|
||||||
}
|
}
|
||||||
return rtp_streams;
|
return rtp_streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(brandtr): Update this function when we support multistream protection.
|
|
||||||
std::unique_ptr<FlexfecSender> MaybeCreateFlexfecSender(
|
|
||||||
Clock* clock,
|
|
||||||
const RtpConfig& rtp,
|
|
||||||
const std::map<uint32_t, RtpState>& suspended_ssrcs) {
|
|
||||||
if (rtp.flexfec.payload_type < 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
RTC_DCHECK_GE(rtp.flexfec.payload_type, 0);
|
|
||||||
RTC_DCHECK_LE(rtp.flexfec.payload_type, 127);
|
|
||||||
if (rtp.flexfec.ssrc == 0) {
|
|
||||||
RTC_LOG(LS_WARNING) << "FlexFEC is enabled, but no FlexFEC SSRC given. "
|
|
||||||
"Therefore disabling FlexFEC.";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (rtp.flexfec.protected_media_ssrcs.empty()) {
|
|
||||||
RTC_LOG(LS_WARNING)
|
|
||||||
<< "FlexFEC is enabled, but no protected media SSRC given. "
|
|
||||||
"Therefore disabling FlexFEC.";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rtp.flexfec.protected_media_ssrcs.size() > 1) {
|
|
||||||
RTC_LOG(LS_WARNING)
|
|
||||||
<< "The supplied FlexfecConfig contained multiple protected "
|
|
||||||
"media streams, but our implementation currently only "
|
|
||||||
"supports protecting a single media stream. "
|
|
||||||
"To avoid confusion, disabling FlexFEC completely.";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RtpState* rtp_state = nullptr;
|
|
||||||
auto it = suspended_ssrcs.find(rtp.flexfec.ssrc);
|
|
||||||
if (it != suspended_ssrcs.end()) {
|
|
||||||
rtp_state = &it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
RTC_DCHECK_EQ(1U, rtp.flexfec.protected_media_ssrcs.size());
|
|
||||||
return std::make_unique<FlexfecSender>(
|
|
||||||
rtp.flexfec.payload_type, rtp.flexfec.ssrc,
|
|
||||||
rtp.flexfec.protected_media_ssrcs[0], rtp.mid, rtp.extensions,
|
|
||||||
RTPSender::FecExtensionSizes(), rtp_state, clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataRate CalculateOverheadRate(DataRate data_rate,
|
DataRate CalculateOverheadRate(DataRate data_rate,
|
||||||
DataSize packet_size,
|
DataSize packet_size,
|
||||||
DataSize overhead_per_packet) {
|
DataSize overhead_per_packet) {
|
||||||
@ -305,8 +324,6 @@ RtpVideoSender::RtpVideoSender(
|
|||||||
active_(false),
|
active_(false),
|
||||||
module_process_thread_(nullptr),
|
module_process_thread_(nullptr),
|
||||||
suspended_ssrcs_(std::move(suspended_ssrcs)),
|
suspended_ssrcs_(std::move(suspended_ssrcs)),
|
||||||
flexfec_sender_(
|
|
||||||
MaybeCreateFlexfecSender(clock, rtp_config, suspended_ssrcs_)),
|
|
||||||
fec_controller_(std::move(fec_controller)),
|
fec_controller_(std::move(fec_controller)),
|
||||||
fec_allowed_(true),
|
fec_allowed_(true),
|
||||||
rtp_streams_(CreateRtpStreamSenders(clock,
|
rtp_streams_(CreateRtpStreamSenders(clock,
|
||||||
@ -316,7 +333,7 @@ RtpVideoSender::RtpVideoSender(
|
|||||||
send_transport,
|
send_transport,
|
||||||
transport->GetBandwidthObserver(),
|
transport->GetBandwidthObserver(),
|
||||||
transport,
|
transport,
|
||||||
flexfec_sender_.get(),
|
suspended_ssrcs_,
|
||||||
event_log,
|
event_log,
|
||||||
retransmission_limiter,
|
retransmission_limiter,
|
||||||
this,
|
this,
|
||||||
@ -379,6 +396,7 @@ RtpVideoSender::RtpVideoSender(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fec_enabled = false;
|
||||||
for (const RtpStreamSender& stream : rtp_streams_) {
|
for (const RtpStreamSender& stream : rtp_streams_) {
|
||||||
// Simulcast has one module for each layer. Set the CNAME on all modules.
|
// Simulcast has one module for each layer. Set the CNAME on all modules.
|
||||||
stream.rtp_rtcp->SetCNAME(rtp_config_.c_name.c_str());
|
stream.rtp_rtcp->SetCNAME(rtp_config_.c_name.c_str());
|
||||||
@ -388,10 +406,13 @@ RtpVideoSender::RtpVideoSender(
|
|||||||
stream.rtp_rtcp->SetMaxRtpPacketSize(rtp_config_.max_packet_size);
|
stream.rtp_rtcp->SetMaxRtpPacketSize(rtp_config_.max_packet_size);
|
||||||
stream.rtp_rtcp->RegisterSendPayloadFrequency(rtp_config_.payload_type,
|
stream.rtp_rtcp->RegisterSendPayloadFrequency(rtp_config_.payload_type,
|
||||||
kVideoPayloadTypeFrequency);
|
kVideoPayloadTypeFrequency);
|
||||||
|
if (stream.fec_generator != nullptr) {
|
||||||
|
fec_enabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Currently, both ULPFEC and FlexFEC use the same FEC rate calculation logic,
|
// Currently, both ULPFEC and FlexFEC use the same FEC rate calculation logic,
|
||||||
// so enable that logic if either of those FEC schemes are enabled.
|
// so enable that logic if either of those FEC schemes are enabled.
|
||||||
fec_controller_->SetProtectionMethod(FecEnabled(), NackEnabled());
|
fec_controller_->SetProtectionMethod(fec_enabled, NackEnabled());
|
||||||
|
|
||||||
fec_controller_->SetProtectionCallback(this);
|
fec_controller_->SetProtectionCallback(this);
|
||||||
// Signal congestion controller this object is ready for OnPacket* callbacks.
|
// Signal congestion controller this object is ready for OnPacket* callbacks.
|
||||||
@ -559,14 +580,6 @@ void RtpVideoSender::OnBitrateAllocationUpdated(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpVideoSender::FecEnabled() const {
|
|
||||||
const bool flexfec_enabled = (flexfec_sender_ != nullptr);
|
|
||||||
const bool ulpfec_enabled =
|
|
||||||
!webrtc::field_trial::IsEnabled("WebRTC-DisableUlpFecExperiment") &&
|
|
||||||
(rtp_config_.ulpfec.ulpfec_payload_type >= 0);
|
|
||||||
return flexfec_enabled || ulpfec_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RtpVideoSender::NackEnabled() const {
|
bool RtpVideoSender::NackEnabled() const {
|
||||||
const bool nack_enabled = rtp_config_.nack.rtp_history_ms > 0;
|
const bool nack_enabled = rtp_config_.nack.rtp_history_ms > 0;
|
||||||
return nack_enabled;
|
return nack_enabled;
|
||||||
@ -661,6 +674,14 @@ std::map<uint32_t, RtpState> RtpVideoSender::GetRtpStates() const {
|
|||||||
uint32_t ssrc = rtp_config_.ssrcs[i];
|
uint32_t ssrc = rtp_config_.ssrcs[i];
|
||||||
RTC_DCHECK_EQ(ssrc, rtp_streams_[i].rtp_rtcp->SSRC());
|
RTC_DCHECK_EQ(ssrc, rtp_streams_[i].rtp_rtcp->SSRC());
|
||||||
rtp_states[ssrc] = rtp_streams_[i].rtp_rtcp->GetRtpState();
|
rtp_states[ssrc] = rtp_streams_[i].rtp_rtcp->GetRtpState();
|
||||||
|
|
||||||
|
VideoFecGenerator* fec_generator = rtp_streams_[i].fec_generator.get();
|
||||||
|
if (fec_generator &&
|
||||||
|
fec_generator->GetFecType() == VideoFecGenerator::FecType::kFlexFec) {
|
||||||
|
auto* flexfec_sender = static_cast<FlexfecSender*>(fec_generator);
|
||||||
|
uint32_t ssrc = rtp_config_.flexfec.ssrc;
|
||||||
|
rtp_states[ssrc] = flexfec_sender->GetRtpState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < rtp_config_.rtx.ssrcs.size(); ++i) {
|
for (size_t i = 0; i < rtp_config_.rtx.ssrcs.size(); ++i) {
|
||||||
@ -668,11 +689,6 @@ std::map<uint32_t, RtpState> RtpVideoSender::GetRtpStates() const {
|
|||||||
rtp_states[ssrc] = rtp_streams_[i].rtp_rtcp->GetRtxState();
|
rtp_states[ssrc] = rtp_streams_[i].rtp_rtcp->GetRtxState();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flexfec_sender_) {
|
|
||||||
uint32_t ssrc = rtp_config_.flexfec.ssrc;
|
|
||||||
rtp_states[ssrc] = flexfec_sender_->GetRtpState();
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtp_states;
|
return rtp_states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,7 +51,8 @@ namespace webrtc_internal_rtp_video_sender {
|
|||||||
// RtpVideoSender.
|
// RtpVideoSender.
|
||||||
struct RtpStreamSender {
|
struct RtpStreamSender {
|
||||||
RtpStreamSender(std::unique_ptr<RtpRtcp> rtp_rtcp,
|
RtpStreamSender(std::unique_ptr<RtpRtcp> rtp_rtcp,
|
||||||
std::unique_ptr<RTPSenderVideo> sender_video);
|
std::unique_ptr<RTPSenderVideo> sender_video,
|
||||||
|
std::unique_ptr<VideoFecGenerator> fec_generator);
|
||||||
~RtpStreamSender();
|
~RtpStreamSender();
|
||||||
|
|
||||||
RtpStreamSender(RtpStreamSender&&) = default;
|
RtpStreamSender(RtpStreamSender&&) = default;
|
||||||
@ -60,6 +61,7 @@ struct RtpStreamSender {
|
|||||||
// Note: Needs pointer stability.
|
// Note: Needs pointer stability.
|
||||||
std::unique_ptr<RtpRtcp> rtp_rtcp;
|
std::unique_ptr<RtpRtcp> rtp_rtcp;
|
||||||
std::unique_ptr<RTPSenderVideo> sender_video;
|
std::unique_ptr<RTPSenderVideo> sender_video;
|
||||||
|
std::unique_ptr<VideoFecGenerator> fec_generator;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc_internal_rtp_video_sender
|
} // namespace webrtc_internal_rtp_video_sender
|
||||||
@ -155,7 +157,6 @@ class RtpVideoSender : public RtpVideoSenderInterface,
|
|||||||
void ConfigureProtection();
|
void ConfigureProtection();
|
||||||
void ConfigureSsrcs();
|
void ConfigureSsrcs();
|
||||||
void ConfigureRids();
|
void ConfigureRids();
|
||||||
bool FecEnabled() const;
|
|
||||||
bool NackEnabled() const;
|
bool NackEnabled() const;
|
||||||
uint32_t GetPacketizationOverheadRate() const;
|
uint32_t GetPacketizationOverheadRate() const;
|
||||||
|
|
||||||
@ -173,8 +174,6 @@ class RtpVideoSender : public RtpVideoSenderInterface,
|
|||||||
rtc::ThreadChecker module_process_thread_checker_;
|
rtc::ThreadChecker module_process_thread_checker_;
|
||||||
std::map<uint32_t, RtpState> suspended_ssrcs_;
|
std::map<uint32_t, RtpState> suspended_ssrcs_;
|
||||||
|
|
||||||
std::unique_ptr<FlexfecSender> flexfec_sender_;
|
|
||||||
|
|
||||||
const std::unique_ptr<FecController> fec_controller_;
|
const std::unique_ptr<FecController> fec_controller_;
|
||||||
bool fec_allowed_ RTC_GUARDED_BY(crit_);
|
bool fec_allowed_ RTC_GUARDED_BY(crit_);
|
||||||
|
|
||||||
|
|||||||
@ -24,9 +24,9 @@ enum FecMaskType {
|
|||||||
|
|
||||||
// Struct containing forward error correction settings.
|
// Struct containing forward error correction settings.
|
||||||
struct FecProtectionParams {
|
struct FecProtectionParams {
|
||||||
int fec_rate;
|
int fec_rate = 0;
|
||||||
int max_fec_frames;
|
int max_fec_frames = 0;
|
||||||
FecMaskType fec_mask_type;
|
FecMaskType fec_mask_type = FecMaskType::kFecMaskRandom;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -214,6 +214,7 @@ rtc_library("rtp_rtcp") {
|
|||||||
"source/ulpfec_header_reader_writer.h",
|
"source/ulpfec_header_reader_writer.h",
|
||||||
"source/ulpfec_receiver_impl.cc",
|
"source/ulpfec_receiver_impl.cc",
|
||||||
"source/ulpfec_receiver_impl.h",
|
"source/ulpfec_receiver_impl.h",
|
||||||
|
"source/video_fec_generator.h",
|
||||||
"source/video_rtp_depacketizer.h",
|
"source/video_rtp_depacketizer.h",
|
||||||
"source/video_rtp_depacketizer_av1.cc",
|
"source/video_rtp_depacketizer_av1.cc",
|
||||||
"source/video_rtp_depacketizer_av1.h",
|
"source/video_rtp_depacketizer_av1.h",
|
||||||
|
|||||||
@ -21,7 +21,9 @@
|
|||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_header_extension_size.h"
|
#include "modules/rtp_rtcp/source/rtp_header_extension_size.h"
|
||||||
#include "modules/rtp_rtcp/source/ulpfec_generator.h"
|
#include "modules/rtp_rtcp/source/ulpfec_generator.h"
|
||||||
|
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
||||||
#include "rtc_base/random.h"
|
#include "rtc_base/random.h"
|
||||||
|
#include "rtc_base/rate_statistics.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ class RtpPacketToSend;
|
|||||||
// Note that this class is not thread safe, and thus requires external
|
// Note that this class is not thread safe, and thus requires external
|
||||||
// synchronization. Currently, this is done using the lock in PayloadRouter.
|
// synchronization. Currently, this is done using the lock in PayloadRouter.
|
||||||
|
|
||||||
class FlexfecSender {
|
class FlexfecSender : public VideoFecGenerator {
|
||||||
public:
|
public:
|
||||||
FlexfecSender(int payload_type,
|
FlexfecSender(int payload_type,
|
||||||
uint32_t ssrc,
|
uint32_t ssrc,
|
||||||
@ -43,26 +45,28 @@ class FlexfecSender {
|
|||||||
Clock* clock);
|
Clock* clock);
|
||||||
~FlexfecSender();
|
~FlexfecSender();
|
||||||
|
|
||||||
uint32_t ssrc() const { return ssrc_; }
|
FecType GetFecType() const override {
|
||||||
|
return VideoFecGenerator::FecType::kFlexFec;
|
||||||
|
}
|
||||||
|
absl::optional<uint32_t> FecSsrc() override { return ssrc_; }
|
||||||
|
|
||||||
// Sets the FEC rate, max frames sent before FEC packets are sent,
|
// Sets the FEC rate, max frames sent before FEC packets are sent,
|
||||||
// and what type of generator matrices are used.
|
// and what type of generator matrices are used.
|
||||||
void SetFecParameters(const FecProtectionParams& params);
|
void SetProtectionParameters(const FecProtectionParams& delta_params,
|
||||||
|
const FecProtectionParams& key_params) override;
|
||||||
|
|
||||||
// Adds a media packet to the internal buffer. When enough media packets
|
// Adds a media packet to the internal buffer. When enough media packets
|
||||||
// have been added, the FEC packets are generated and stored internally.
|
// have been added, the FEC packets are generated and stored internally.
|
||||||
// These FEC packets are then obtained by calling GetFecPackets().
|
// These FEC packets are then obtained by calling GetFecPackets().
|
||||||
// Returns true if the media packet was successfully added.
|
void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
|
||||||
bool AddRtpPacketAndGenerateFec(const RtpPacketToSend& packet);
|
|
||||||
|
|
||||||
// Returns true if there are generated FEC packets available.
|
|
||||||
bool FecAvailable() const;
|
|
||||||
|
|
||||||
// Returns generated FlexFEC packets.
|
// Returns generated FlexFEC packets.
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets();
|
std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() override;
|
||||||
|
|
||||||
// Returns the overhead, per packet, for FlexFEC.
|
// Returns the overhead, per packet, for FlexFEC.
|
||||||
size_t MaxPacketOverhead() const;
|
size_t MaxPacketOverhead() const override;
|
||||||
|
|
||||||
|
DataRate CurrentFecRate() const override;
|
||||||
|
|
||||||
// Only called on the VideoSendStream queue, after operation has shut down.
|
// Only called on the VideoSendStream queue, after operation has shut down.
|
||||||
RtpState GetRtpState();
|
RtpState GetRtpState();
|
||||||
@ -87,6 +91,9 @@ class FlexfecSender {
|
|||||||
UlpfecGenerator ulpfec_generator_;
|
UlpfecGenerator ulpfec_generator_;
|
||||||
const RtpHeaderExtensionMap rtp_header_extension_map_;
|
const RtpHeaderExtensionMap rtp_header_extension_map_;
|
||||||
const size_t header_extensions_size_;
|
const size_t header_extensions_size_;
|
||||||
|
|
||||||
|
rtc::CriticalSection crit_;
|
||||||
|
RateStatistics fec_bitrate_ RTC_GUARDED_BY(crit_);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -24,13 +24,13 @@
|
|||||||
#include "api/transport/webrtc_key_value_config.h"
|
#include "api/transport/webrtc_key_value_config.h"
|
||||||
#include "api/video/video_bitrate_allocation.h"
|
#include "api/video/video_bitrate_allocation.h"
|
||||||
#include "modules/include/module.h"
|
#include "modules/include/module.h"
|
||||||
#include "modules/rtp_rtcp/include/flexfec_sender.h"
|
|
||||||
#include "modules/rtp_rtcp/include/receive_statistics.h"
|
#include "modules/rtp_rtcp/include/receive_statistics.h"
|
||||||
#include "modules/rtp_rtcp/include/report_block_data.h"
|
#include "modules/rtp_rtcp/include/report_block_data.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_packet_sender.h"
|
#include "modules/rtp_rtcp/include/rtp_packet_sender.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
|
#include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
|
||||||
|
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
||||||
#include "rtc_base/constructor_magic.h"
|
#include "rtc_base/constructor_magic.h"
|
||||||
#include "rtc_base/deprecation.h"
|
#include "rtc_base/deprecation.h"
|
||||||
|
|
||||||
@ -94,9 +94,9 @@ class RtpRtcp : public Module, public RtcpFeedbackSenderInterface {
|
|||||||
// Spread any bursts of packets into smaller bursts to minimize packet loss.
|
// Spread any bursts of packets into smaller bursts to minimize packet loss.
|
||||||
RtpPacketSender* paced_sender = nullptr;
|
RtpPacketSender* paced_sender = nullptr;
|
||||||
|
|
||||||
// Generate FlexFEC packets.
|
// Generates FEC packets.
|
||||||
// TODO(brandtr): Remove when FlexfecSender is wired up to PacedSender.
|
// TODO(sprang): Wire up to RtpSenderEgress.
|
||||||
FlexfecSender* flexfec_sender = nullptr;
|
VideoFecGenerator* fec_generator = nullptr;
|
||||||
|
|
||||||
BitrateStatisticsObserver* send_bitrate_observer = nullptr;
|
BitrateStatisticsObserver* send_bitrate_observer = nullptr;
|
||||||
SendSideDelayObserver* send_side_delay_observer = nullptr;
|
SendSideDelayObserver* send_side_delay_observer = nullptr;
|
||||||
|
|||||||
@ -91,11 +91,13 @@ FlexfecSender::FlexfecSender(
|
|||||||
seq_num_(rtp_state ? rtp_state->sequence_number
|
seq_num_(rtp_state ? rtp_state->sequence_number
|
||||||
: random_.Rand(1, kMaxInitRtpSeqNumber)),
|
: random_.Rand(1, kMaxInitRtpSeqNumber)),
|
||||||
ulpfec_generator_(
|
ulpfec_generator_(
|
||||||
ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc)),
|
ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc),
|
||||||
|
clock_),
|
||||||
rtp_header_extension_map_(
|
rtp_header_extension_map_(
|
||||||
RegisterSupportedExtensions(rtp_header_extensions)),
|
RegisterSupportedExtensions(rtp_header_extensions)),
|
||||||
header_extensions_size_(
|
header_extensions_size_(
|
||||||
RtpHeaderExtensionSize(extension_sizes, rtp_header_extension_map_)) {
|
RtpHeaderExtensionSize(extension_sizes, rtp_header_extension_map_)),
|
||||||
|
fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {
|
||||||
// This object should not have been instantiated if FlexFEC is disabled.
|
// This object should not have been instantiated if FlexFEC is disabled.
|
||||||
RTC_DCHECK_GE(payload_type, 0);
|
RTC_DCHECK_GE(payload_type, 0);
|
||||||
RTC_DCHECK_LE(payload_type, 127);
|
RTC_DCHECK_LE(payload_type, 127);
|
||||||
@ -105,30 +107,30 @@ FlexfecSender::~FlexfecSender() = default;
|
|||||||
|
|
||||||
// We are reusing the implementation from UlpfecGenerator for SetFecParameters,
|
// We are reusing the implementation from UlpfecGenerator for SetFecParameters,
|
||||||
// AddRtpPacketAndGenerateFec, and FecAvailable.
|
// AddRtpPacketAndGenerateFec, and FecAvailable.
|
||||||
void FlexfecSender::SetFecParameters(const FecProtectionParams& params) {
|
void FlexfecSender::SetProtectionParameters(
|
||||||
ulpfec_generator_.SetFecParameters(params);
|
const FecProtectionParams& delta_params,
|
||||||
|
const FecProtectionParams& key_params) {
|
||||||
|
ulpfec_generator_.SetProtectionParameters(delta_params, key_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlexfecSender::AddRtpPacketAndGenerateFec(const RtpPacketToSend& packet) {
|
void FlexfecSender::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
|
||||||
// TODO(brandtr): Generalize this SSRC check when we support multistream
|
// TODO(brandtr): Generalize this SSRC check when we support multistream
|
||||||
// protection.
|
// protection.
|
||||||
RTC_DCHECK_EQ(packet.Ssrc(), protected_media_ssrc_);
|
RTC_DCHECK_EQ(packet.Ssrc(), protected_media_ssrc_);
|
||||||
return ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
ulpfec_generator_.AddPacketAndGenerateFec(packet);
|
||||||
packet.Buffer(), packet.headers_size()) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FlexfecSender::FecAvailable() const {
|
|
||||||
return ulpfec_generator_.FecAvailable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> FlexfecSender::GetFecPackets() {
|
std::vector<std::unique_ptr<RtpPacketToSend>> FlexfecSender::GetFecPackets() {
|
||||||
|
RTC_CHECK_RUNS_SERIALIZED(&ulpfec_generator_.race_checker_);
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets_to_send;
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets_to_send;
|
||||||
fec_packets_to_send.reserve(ulpfec_generator_.generated_fec_packets_.size());
|
fec_packets_to_send.reserve(ulpfec_generator_.generated_fec_packets_.size());
|
||||||
|
size_t total_fec_data_bytes = 0;
|
||||||
for (const auto* fec_packet : ulpfec_generator_.generated_fec_packets_) {
|
for (const auto* fec_packet : ulpfec_generator_.generated_fec_packets_) {
|
||||||
std::unique_ptr<RtpPacketToSend> fec_packet_to_send(
|
std::unique_ptr<RtpPacketToSend> fec_packet_to_send(
|
||||||
new RtpPacketToSend(&rtp_header_extension_map_));
|
new RtpPacketToSend(&rtp_header_extension_map_));
|
||||||
fec_packet_to_send->set_packet_type(
|
fec_packet_to_send->set_packet_type(
|
||||||
RtpPacketMediaType::kForwardErrorCorrection);
|
RtpPacketMediaType::kForwardErrorCorrection);
|
||||||
|
fec_packet_to_send->set_allow_retransmission(false);
|
||||||
|
|
||||||
// RTP header.
|
// RTP header.
|
||||||
fec_packet_to_send->SetMarker(false);
|
fec_packet_to_send->SetMarker(false);
|
||||||
@ -157,9 +159,13 @@ std::vector<std::unique_ptr<RtpPacketToSend>> FlexfecSender::GetFecPackets() {
|
|||||||
fec_packet_to_send->AllocatePayload(fec_packet->data.size());
|
fec_packet_to_send->AllocatePayload(fec_packet->data.size());
|
||||||
memcpy(payload, fec_packet->data.cdata(), fec_packet->data.size());
|
memcpy(payload, fec_packet->data.cdata(), fec_packet->data.size());
|
||||||
|
|
||||||
|
total_fec_data_bytes += fec_packet_to_send->size();
|
||||||
fec_packets_to_send.push_back(std::move(fec_packet_to_send));
|
fec_packets_to_send.push_back(std::move(fec_packet_to_send));
|
||||||
}
|
}
|
||||||
ulpfec_generator_.ResetState();
|
|
||||||
|
if (!fec_packets_to_send.empty()) {
|
||||||
|
ulpfec_generator_.ResetState();
|
||||||
|
}
|
||||||
|
|
||||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||||
if (!fec_packets_to_send.empty() &&
|
if (!fec_packets_to_send.empty() &&
|
||||||
@ -170,6 +176,9 @@ std::vector<std::unique_ptr<RtpPacketToSend>> FlexfecSender::GetFecPackets() {
|
|||||||
last_generated_packet_ms_ = now_ms;
|
last_generated_packet_ms_ = now_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc::CritScope cs(&crit_);
|
||||||
|
fec_bitrate_.Update(total_fec_data_bytes, now_ms);
|
||||||
|
|
||||||
return fec_packets_to_send;
|
return fec_packets_to_send;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +187,12 @@ size_t FlexfecSender::MaxPacketOverhead() const {
|
|||||||
return header_extensions_size_ + kFlexfecMaxHeaderSize;
|
return header_extensions_size_ + kFlexfecMaxHeaderSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataRate FlexfecSender::CurrentFecRate() const {
|
||||||
|
rtc::CritScope cs(&crit_);
|
||||||
|
return DataRate::BitsPerSec(
|
||||||
|
fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0));
|
||||||
|
}
|
||||||
|
|
||||||
RtpState FlexfecSender::GetRtpState() {
|
RtpState FlexfecSender::GetRtpState() {
|
||||||
RtpState rtp_state;
|
RtpState rtp_state;
|
||||||
rtp_state.sequence_number = seq_num_;
|
rtp_state.sequence_number = seq_num_;
|
||||||
|
|||||||
@ -55,7 +55,7 @@ std::unique_ptr<RtpPacketToSend> GenerateSingleFlexfecPacket(
|
|||||||
params.fec_mask_type = kFecMaskRandom;
|
params.fec_mask_type = kFecMaskRandom;
|
||||||
constexpr size_t kNumPackets = 4;
|
constexpr size_t kNumPackets = 4;
|
||||||
|
|
||||||
sender->SetFecParameters(params);
|
sender->SetProtectionParameters(params, params);
|
||||||
AugmentedPacketGenerator packet_generator(kMediaSsrc);
|
AugmentedPacketGenerator packet_generator(kMediaSsrc);
|
||||||
packet_generator.NewFrame(kNumPackets);
|
packet_generator.NewFrame(kNumPackets);
|
||||||
for (size_t i = 0; i < kNumPackets; ++i) {
|
for (size_t i = 0; i < kNumPackets; ++i) {
|
||||||
@ -63,13 +63,12 @@ std::unique_ptr<RtpPacketToSend> GenerateSingleFlexfecPacket(
|
|||||||
packet_generator.NextPacket(i, kPayloadLength);
|
packet_generator.NextPacket(i, kPayloadLength);
|
||||||
RtpPacketToSend rtp_packet(nullptr); // No header extensions.
|
RtpPacketToSend rtp_packet(nullptr); // No header extensions.
|
||||||
rtp_packet.Parse(packet->data);
|
rtp_packet.Parse(packet->data);
|
||||||
EXPECT_TRUE(sender->AddRtpPacketAndGenerateFec(rtp_packet));
|
sender->AddPacketAndGenerateFec(rtp_packet);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(sender->FecAvailable());
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
sender->GetFecPackets();
|
sender->GetFecPackets();
|
||||||
EXPECT_FALSE(sender->FecAvailable());
|
|
||||||
EXPECT_EQ(1U, fec_packets.size());
|
EXPECT_EQ(1U, fec_packets.size());
|
||||||
|
EXPECT_TRUE(sender->GetFecPackets().empty());
|
||||||
|
|
||||||
return std::move(fec_packets.front());
|
return std::move(fec_packets.front());
|
||||||
}
|
}
|
||||||
@ -82,7 +81,7 @@ TEST(FlexfecSenderTest, Ssrc) {
|
|||||||
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
||||||
nullptr /* rtp_state */, &clock);
|
nullptr /* rtp_state */, &clock);
|
||||||
|
|
||||||
EXPECT_EQ(kFlexfecSsrc, sender.ssrc());
|
EXPECT_EQ(kFlexfecSsrc, sender.FecSsrc());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
|
TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
|
||||||
@ -91,9 +90,7 @@ TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
|
|||||||
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
||||||
nullptr /* rtp_state */, &clock);
|
nullptr /* rtp_state */, &clock);
|
||||||
|
|
||||||
EXPECT_FALSE(sender.FecAvailable());
|
EXPECT_TRUE(sender.GetFecPackets().empty());
|
||||||
auto fec_packets = sender.GetFecPackets();
|
|
||||||
EXPECT_EQ(0U, fec_packets.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FlexfecSenderTest, ProtectOneFrameWithOneFecPacket) {
|
TEST(FlexfecSenderTest, ProtectOneFrameWithOneFecPacket) {
|
||||||
@ -124,7 +121,7 @@ TEST(FlexfecSenderTest, ProtectTwoFramesWithOneFecPacket) {
|
|||||||
FlexfecSender sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, kNoMid,
|
FlexfecSender sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, kNoMid,
|
||||||
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
||||||
nullptr /* rtp_state */, &clock);
|
nullptr /* rtp_state */, &clock);
|
||||||
sender.SetFecParameters(params);
|
sender.SetProtectionParameters(params, params);
|
||||||
|
|
||||||
AugmentedPacketGenerator packet_generator(kMediaSsrc);
|
AugmentedPacketGenerator packet_generator(kMediaSsrc);
|
||||||
for (size_t i = 0; i < kNumFrames; ++i) {
|
for (size_t i = 0; i < kNumFrames; ++i) {
|
||||||
@ -134,14 +131,13 @@ TEST(FlexfecSenderTest, ProtectTwoFramesWithOneFecPacket) {
|
|||||||
packet_generator.NextPacket(i, kPayloadLength);
|
packet_generator.NextPacket(i, kPayloadLength);
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
rtp_packet.Parse(packet->data);
|
rtp_packet.Parse(packet->data);
|
||||||
EXPECT_TRUE(sender.AddRtpPacketAndGenerateFec(rtp_packet));
|
sender.AddPacketAndGenerateFec(rtp_packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(sender.FecAvailable());
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
sender.GetFecPackets();
|
sender.GetFecPackets();
|
||||||
EXPECT_FALSE(sender.FecAvailable());
|
|
||||||
ASSERT_EQ(1U, fec_packets.size());
|
ASSERT_EQ(1U, fec_packets.size());
|
||||||
|
EXPECT_TRUE(sender.GetFecPackets().empty());
|
||||||
|
|
||||||
RtpPacketToSend* fec_packet = fec_packets.front().get();
|
RtpPacketToSend* fec_packet = fec_packets.front().get();
|
||||||
EXPECT_EQ(kRtpHeaderSize, fec_packet->headers_size());
|
EXPECT_EQ(kRtpHeaderSize, fec_packet->headers_size());
|
||||||
@ -164,7 +160,7 @@ TEST(FlexfecSenderTest, ProtectTwoFramesWithTwoFecPackets) {
|
|||||||
FlexfecSender sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, kNoMid,
|
FlexfecSender sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, kNoMid,
|
||||||
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
||||||
nullptr /* rtp_state */, &clock);
|
nullptr /* rtp_state */, &clock);
|
||||||
sender.SetFecParameters(params);
|
sender.SetProtectionParameters(params, params);
|
||||||
|
|
||||||
AugmentedPacketGenerator packet_generator(kMediaSsrc);
|
AugmentedPacketGenerator packet_generator(kMediaSsrc);
|
||||||
for (size_t i = 0; i < kNumFrames; ++i) {
|
for (size_t i = 0; i < kNumFrames; ++i) {
|
||||||
@ -174,13 +170,12 @@ TEST(FlexfecSenderTest, ProtectTwoFramesWithTwoFecPackets) {
|
|||||||
packet_generator.NextPacket(i, kPayloadLength);
|
packet_generator.NextPacket(i, kPayloadLength);
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
rtp_packet.Parse(packet->data);
|
rtp_packet.Parse(packet->data);
|
||||||
EXPECT_TRUE(sender.AddRtpPacketAndGenerateFec(rtp_packet));
|
sender.AddPacketAndGenerateFec(rtp_packet);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(sender.FecAvailable());
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
sender.GetFecPackets();
|
sender.GetFecPackets();
|
||||||
EXPECT_FALSE(sender.FecAvailable());
|
|
||||||
ASSERT_EQ(1U, fec_packets.size());
|
ASSERT_EQ(1U, fec_packets.size());
|
||||||
|
EXPECT_TRUE(sender.GetFecPackets().empty());
|
||||||
|
|
||||||
RtpPacketToSend* fec_packet = fec_packets.front().get();
|
RtpPacketToSend* fec_packet = fec_packets.front().get();
|
||||||
EXPECT_EQ(kRtpHeaderSize, fec_packet->headers_size());
|
EXPECT_EQ(kRtpHeaderSize, fec_packet->headers_size());
|
||||||
|
|||||||
@ -72,8 +72,11 @@ std::set<uint32_t> GetRegisteredSsrcs(const RtpRtcp::Configuration& config) {
|
|||||||
if (config.rtx_send_ssrc) {
|
if (config.rtx_send_ssrc) {
|
||||||
ssrcs.insert(*config.rtx_send_ssrc);
|
ssrcs.insert(*config.rtx_send_ssrc);
|
||||||
}
|
}
|
||||||
if (config.flexfec_sender) {
|
if (config.fec_generator) {
|
||||||
ssrcs.insert(config.flexfec_sender->ssrc());
|
absl::optional<uint32_t> flexfec_ssrc = config.fec_generator->FecSsrc();
|
||||||
|
if (flexfec_ssrc) {
|
||||||
|
ssrcs.insert(*flexfec_ssrc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ssrcs;
|
return ssrcs;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,12 +98,16 @@ class RtpPacketToSend : public RtpPacket {
|
|||||||
VideoTimingExtension::kNetwork2TimestampDeltaOffset);
|
VideoTimingExtension::kNetwork2TimestampDeltaOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indicates if packet is the first packet of a video frame.
|
||||||
void set_first_packet_of_frame(bool is_first_packet) {
|
void set_first_packet_of_frame(bool is_first_packet) {
|
||||||
is_first_packet_of_frame_ = is_first_packet;
|
is_first_packet_of_frame_ = is_first_packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_first_packet_of_frame() const { return is_first_packet_of_frame_; }
|
bool is_first_packet_of_frame() const { return is_first_packet_of_frame_; }
|
||||||
|
|
||||||
|
// Indicates if packet contains payload for a video key-frame.
|
||||||
|
void set_is_key_frame(bool is_key_frame) { is_key_frame_ = is_key_frame; }
|
||||||
|
bool is_key_frame() const { return is_key_frame_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t capture_time_ms_ = 0;
|
int64_t capture_time_ms_ = 0;
|
||||||
absl::optional<RtpPacketMediaType> packet_type_;
|
absl::optional<RtpPacketMediaType> packet_type_;
|
||||||
@ -111,6 +115,7 @@ class RtpPacketToSend : public RtpPacket {
|
|||||||
absl::optional<uint16_t> retransmitted_sequence_number_;
|
absl::optional<uint16_t> retransmitted_sequence_number_;
|
||||||
std::vector<uint8_t> application_data_;
|
std::vector<uint8_t> application_data_;
|
||||||
bool is_first_packet_of_frame_ = false;
|
bool is_first_packet_of_frame_ = false;
|
||||||
|
bool is_key_frame_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -102,9 +102,8 @@ RTPSender::RTPSender(const RtpRtcp::Configuration& config,
|
|||||||
audio_configured_(config.audio),
|
audio_configured_(config.audio),
|
||||||
ssrc_(config.local_media_ssrc),
|
ssrc_(config.local_media_ssrc),
|
||||||
rtx_ssrc_(config.rtx_send_ssrc),
|
rtx_ssrc_(config.rtx_send_ssrc),
|
||||||
flexfec_ssrc_(config.flexfec_sender
|
flexfec_ssrc_(config.fec_generator ? config.fec_generator->FecSsrc()
|
||||||
? absl::make_optional(config.flexfec_sender->ssrc())
|
: absl::nullopt),
|
||||||
: absl::nullopt),
|
|
||||||
packet_history_(packet_history),
|
packet_history_(packet_history),
|
||||||
paced_sender_(packet_sender),
|
paced_sender_(packet_sender),
|
||||||
sending_media_(true), // Default to sending media.
|
sending_media_(true), // Default to sending media.
|
||||||
|
|||||||
@ -57,9 +57,8 @@ RtpSenderEgress::RtpSenderEgress(const RtpRtcp::Configuration& config,
|
|||||||
RtpPacketHistory* packet_history)
|
RtpPacketHistory* packet_history)
|
||||||
: ssrc_(config.local_media_ssrc),
|
: ssrc_(config.local_media_ssrc),
|
||||||
rtx_ssrc_(config.rtx_send_ssrc),
|
rtx_ssrc_(config.rtx_send_ssrc),
|
||||||
flexfec_ssrc_(config.flexfec_sender
|
flexfec_ssrc_(config.fec_generator ? config.fec_generator->FecSsrc()
|
||||||
? absl::make_optional(config.flexfec_sender->ssrc())
|
: absl::nullopt),
|
||||||
: absl::nullopt),
|
|
||||||
populate_network2_timestamp_(config.populate_network2_timestamp),
|
populate_network2_timestamp_(config.populate_network2_timestamp),
|
||||||
send_side_bwe_with_overhead_(
|
send_side_bwe_with_overhead_(
|
||||||
IsEnabled("WebRTC-SendSideBwe-WithOverhead", config.field_trials)),
|
IsEnabled("WebRTC-SendSideBwe-WithOverhead", config.field_trials)),
|
||||||
|
|||||||
@ -272,7 +272,7 @@ class RtpSenderTest : public ::testing::TestWithParam<TestConfig> {
|
|||||||
config.outgoing_transport = &transport_;
|
config.outgoing_transport = &transport_;
|
||||||
config.local_media_ssrc = kSsrc;
|
config.local_media_ssrc = kSsrc;
|
||||||
config.rtx_send_ssrc = kRtxSsrc;
|
config.rtx_send_ssrc = kRtxSsrc;
|
||||||
config.flexfec_sender = &flexfec_sender_;
|
config.fec_generator = &flexfec_sender_;
|
||||||
config.event_log = &mock_rtc_event_log_;
|
config.event_log = &mock_rtc_event_log_;
|
||||||
config.send_packet_observer = &send_packet_observer_;
|
config.send_packet_observer = &send_packet_observer_;
|
||||||
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||||
@ -1225,7 +1225,7 @@ TEST_P(RtpSenderTest, SendFlexfecPackets) {
|
|||||||
config.outgoing_transport = &transport_;
|
config.outgoing_transport = &transport_;
|
||||||
config.paced_sender = &mock_paced_sender_;
|
config.paced_sender = &mock_paced_sender_;
|
||||||
config.local_media_ssrc = kSsrc;
|
config.local_media_ssrc = kSsrc;
|
||||||
config.flexfec_sender = &flexfec_sender_;
|
config.fec_generator = &flexfec_sender_;
|
||||||
config.event_log = &mock_rtc_event_log_;
|
config.event_log = &mock_rtc_event_log_;
|
||||||
config.send_packet_observer = &send_packet_observer_;
|
config.send_packet_observer = &send_packet_observer_;
|
||||||
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||||
@ -1239,7 +1239,7 @@ TEST_P(RtpSenderTest, SendFlexfecPackets) {
|
|||||||
RTPSenderVideo::Config video_config;
|
RTPSenderVideo::Config video_config;
|
||||||
video_config.clock = &fake_clock_;
|
video_config.clock = &fake_clock_;
|
||||||
video_config.rtp_sender = rtp_sender();
|
video_config.rtp_sender = rtp_sender();
|
||||||
video_config.flexfec_sender = &flexfec_sender;
|
video_config.fec_generator = &flexfec_sender;
|
||||||
video_config.field_trials = &field_trials;
|
video_config.field_trials = &field_trials;
|
||||||
RTPSenderVideo rtp_sender_video(video_config);
|
RTPSenderVideo rtp_sender_video(video_config);
|
||||||
|
|
||||||
@ -1311,7 +1311,7 @@ TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) {
|
|||||||
config.clock = &fake_clock_;
|
config.clock = &fake_clock_;
|
||||||
config.outgoing_transport = &transport_;
|
config.outgoing_transport = &transport_;
|
||||||
config.local_media_ssrc = kSsrc;
|
config.local_media_ssrc = kSsrc;
|
||||||
config.flexfec_sender = &flexfec_sender;
|
config.fec_generator = &flexfec_sender;
|
||||||
config.event_log = &mock_rtc_event_log_;
|
config.event_log = &mock_rtc_event_log_;
|
||||||
config.send_packet_observer = &send_packet_observer_;
|
config.send_packet_observer = &send_packet_observer_;
|
||||||
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||||
@ -1323,7 +1323,7 @@ TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) {
|
|||||||
RTPSenderVideo::Config video_config;
|
RTPSenderVideo::Config video_config;
|
||||||
video_config.clock = &fake_clock_;
|
video_config.clock = &fake_clock_;
|
||||||
video_config.rtp_sender = rtp_sender();
|
video_config.rtp_sender = rtp_sender();
|
||||||
video_config.flexfec_sender = &flexfec_sender;
|
video_config.fec_generator = &flexfec_sender;
|
||||||
video_config.field_trials = &field_trials;
|
video_config.field_trials = &field_trials;
|
||||||
RTPSenderVideo rtp_sender_video(video_config);
|
RTPSenderVideo rtp_sender_video(video_config);
|
||||||
|
|
||||||
@ -1583,7 +1583,7 @@ TEST_P(RtpSenderTest, FecOverheadRate) {
|
|||||||
config.outgoing_transport = &transport_;
|
config.outgoing_transport = &transport_;
|
||||||
config.paced_sender = &mock_paced_sender_;
|
config.paced_sender = &mock_paced_sender_;
|
||||||
config.local_media_ssrc = kSsrc;
|
config.local_media_ssrc = kSsrc;
|
||||||
config.flexfec_sender = &flexfec_sender;
|
config.fec_generator = &flexfec_sender;
|
||||||
config.event_log = &mock_rtc_event_log_;
|
config.event_log = &mock_rtc_event_log_;
|
||||||
config.send_packet_observer = &send_packet_observer_;
|
config.send_packet_observer = &send_packet_observer_;
|
||||||
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||||
@ -1595,7 +1595,7 @@ TEST_P(RtpSenderTest, FecOverheadRate) {
|
|||||||
RTPSenderVideo::Config video_config;
|
RTPSenderVideo::Config video_config;
|
||||||
video_config.clock = &fake_clock_;
|
video_config.clock = &fake_clock_;
|
||||||
video_config.rtp_sender = rtp_sender();
|
video_config.rtp_sender = rtp_sender();
|
||||||
video_config.flexfec_sender = &flexfec_sender;
|
video_config.fec_generator = &flexfec_sender;
|
||||||
video_config.field_trials = &field_trials;
|
video_config.field_trials = &field_trials;
|
||||||
RTPSenderVideo rtp_sender_video(video_config);
|
RTPSenderVideo rtp_sender_video(video_config);
|
||||||
// Parameters selected to generate a single FEC packet per media packet.
|
// Parameters selected to generate a single FEC packet per media packet.
|
||||||
@ -1777,12 +1777,14 @@ TEST_P(RtpSenderTestWithoutPacer, StreamDataCountersCallbacksUlpfec) {
|
|||||||
const uint8_t kPayloadType = 127;
|
const uint8_t kPayloadType = 127;
|
||||||
const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric;
|
const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric;
|
||||||
FieldTrialBasedConfig field_trials;
|
FieldTrialBasedConfig field_trials;
|
||||||
|
UlpfecGenerator ulpfec_generator(kRedPayloadType, kUlpfecPayloadType,
|
||||||
|
&fake_clock_);
|
||||||
RTPSenderVideo::Config video_config;
|
RTPSenderVideo::Config video_config;
|
||||||
video_config.clock = &fake_clock_;
|
video_config.clock = &fake_clock_;
|
||||||
video_config.rtp_sender = rtp_sender();
|
video_config.rtp_sender = rtp_sender();
|
||||||
video_config.field_trials = &field_trials;
|
video_config.field_trials = &field_trials;
|
||||||
video_config.red_payload_type = kRedPayloadType;
|
video_config.red_payload_type = kRedPayloadType;
|
||||||
video_config.ulpfec_payload_type = kUlpfecPayloadType;
|
video_config.fec_generator = &ulpfec_generator;
|
||||||
RTPSenderVideo rtp_sender_video(video_config);
|
RTPSenderVideo rtp_sender_video(video_config);
|
||||||
uint8_t payload[] = {47, 11, 32, 93, 89};
|
uint8_t payload[] = {47, 11, 32, 93, 89};
|
||||||
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
|
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
|
||||||
@ -2118,7 +2120,7 @@ TEST_P(RtpSenderTest, SendPacketUpdatesStats) {
|
|||||||
config.outgoing_transport = &transport_;
|
config.outgoing_transport = &transport_;
|
||||||
config.local_media_ssrc = kSsrc;
|
config.local_media_ssrc = kSsrc;
|
||||||
config.rtx_send_ssrc = kRtxSsrc;
|
config.rtx_send_ssrc = kRtxSsrc;
|
||||||
config.flexfec_sender = &flexfec_sender_;
|
config.fec_generator = &flexfec_sender_;
|
||||||
config.send_side_delay_observer = &send_side_delay_observer;
|
config.send_side_delay_observer = &send_side_delay_observer;
|
||||||
config.event_log = &mock_rtc_event_log_;
|
config.event_log = &mock_rtc_event_log_;
|
||||||
config.send_packet_observer = &send_packet_observer_;
|
config.send_packet_observer = &send_packet_observer_;
|
||||||
|
|||||||
@ -260,11 +260,7 @@ RTPSenderVideo::RTPSenderVideo(const Config& config)
|
|||||||
current_playout_delay_{-1, -1},
|
current_playout_delay_{-1, -1},
|
||||||
playout_delay_pending_(false),
|
playout_delay_pending_(false),
|
||||||
red_payload_type_(config.red_payload_type),
|
red_payload_type_(config.red_payload_type),
|
||||||
ulpfec_payload_type_(config.ulpfec_payload_type),
|
fec_generator_(config.fec_generator),
|
||||||
flexfec_sender_(config.flexfec_sender),
|
|
||||||
delta_fec_params_{0, 1, kFecMaskRandom},
|
|
||||||
key_fec_params_{0, 1, kFecMaskRandom},
|
|
||||||
fec_bitrate_(1000, RateStatistics::kBpsScale),
|
|
||||||
video_bitrate_(1000, RateStatistics::kBpsScale),
|
video_bitrate_(1000, RateStatistics::kBpsScale),
|
||||||
packetization_overhead_bitrate_(1000, RateStatistics::kBpsScale),
|
packetization_overhead_bitrate_(1000, RateStatistics::kBpsScale),
|
||||||
frame_encryptor_(config.frame_encryptor),
|
frame_encryptor_(config.frame_encryptor),
|
||||||
@ -293,83 +289,6 @@ RTPSenderVideo::~RTPSenderVideo() {
|
|||||||
frame_transformer_delegate_->Reset();
|
frame_transformer_delegate_->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPSenderVideo::AppendAsRedMaybeWithUlpfec(
|
|
||||||
std::unique_ptr<RtpPacketToSend> media_packet,
|
|
||||||
bool protect_media_packet,
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>>* packets) {
|
|
||||||
std::unique_ptr<RtpPacketToSend> red_packet(
|
|
||||||
new RtpPacketToSend(*media_packet));
|
|
||||||
BuildRedPayload(*media_packet, red_packet.get());
|
|
||||||
red_packet->SetPayloadType(*red_payload_type_);
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RedPacket>> fec_packets;
|
|
||||||
if (ulpfec_enabled()) {
|
|
||||||
if (protect_media_packet) {
|
|
||||||
if (exclude_transport_sequence_number_from_fec_experiment_) {
|
|
||||||
// See comments at the top of the file why experiment
|
|
||||||
// "WebRTC-kExcludeTransportSequenceNumberFromFec" is needed in
|
|
||||||
// conjunction with datagram transport.
|
|
||||||
// TODO(sukhanov): We may also need to implement it for flexfec_sender
|
|
||||||
// if we decide to keep this approach in the future.
|
|
||||||
uint16_t transport_senquence_number;
|
|
||||||
if (media_packet->GetExtension<webrtc::TransportSequenceNumber>(
|
|
||||||
&transport_senquence_number)) {
|
|
||||||
if (!media_packet->RemoveExtension(
|
|
||||||
webrtc::TransportSequenceNumber::kId)) {
|
|
||||||
RTC_NOTREACHED()
|
|
||||||
<< "Failed to remove transport sequence number, packet="
|
|
||||||
<< media_packet->ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
|
||||||
media_packet->Buffer(), media_packet->headers_size());
|
|
||||||
}
|
|
||||||
uint16_t num_fec_packets = ulpfec_generator_.NumAvailableFecPackets();
|
|
||||||
if (num_fec_packets > 0) {
|
|
||||||
uint16_t first_fec_sequence_number =
|
|
||||||
rtp_sender_->AllocateSequenceNumber(num_fec_packets);
|
|
||||||
fec_packets = ulpfec_generator_.GetUlpfecPacketsAsRed(
|
|
||||||
*red_payload_type_, *ulpfec_payload_type_, first_fec_sequence_number);
|
|
||||||
RTC_DCHECK_EQ(num_fec_packets, fec_packets.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send |red_packet| instead of |packet| for allocated sequence number.
|
|
||||||
red_packet->set_packet_type(RtpPacketMediaType::kVideo);
|
|
||||||
red_packet->set_allow_retransmission(media_packet->allow_retransmission());
|
|
||||||
packets->emplace_back(std::move(red_packet));
|
|
||||||
|
|
||||||
for (const auto& fec_packet : fec_packets) {
|
|
||||||
// TODO(danilchap): Make ulpfec_generator_ generate RtpPacketToSend to avoid
|
|
||||||
// reparsing them.
|
|
||||||
std::unique_ptr<RtpPacketToSend> rtp_packet(
|
|
||||||
new RtpPacketToSend(*media_packet));
|
|
||||||
RTC_CHECK(rtp_packet->Parse(fec_packet->data(), fec_packet->length()));
|
|
||||||
rtp_packet->set_capture_time_ms(media_packet->capture_time_ms());
|
|
||||||
rtp_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
|
|
||||||
rtp_packet->set_allow_retransmission(false);
|
|
||||||
RTC_DCHECK_EQ(fec_packet->length(), rtp_packet->size());
|
|
||||||
packets->emplace_back(std::move(rtp_packet));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTPSenderVideo::GenerateAndAppendFlexfec(
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>>* packets) {
|
|
||||||
RTC_DCHECK(flexfec_sender_);
|
|
||||||
|
|
||||||
if (flexfec_sender_->FecAvailable()) {
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
|
||||||
flexfec_sender_->GetFecPackets();
|
|
||||||
for (auto& fec_packet : fec_packets) {
|
|
||||||
fec_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
|
|
||||||
fec_packet->set_allow_retransmission(false);
|
|
||||||
packets->emplace_back(std::move(fec_packet));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTPSenderVideo::LogAndSendToNetwork(
|
void RTPSenderVideo::LogAndSendToNetwork(
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets,
|
std::vector<std::unique_ptr<RtpPacketToSend>> packets,
|
||||||
size_t unpacketized_payload_size) {
|
size_t unpacketized_payload_size) {
|
||||||
@ -388,16 +307,9 @@ void RTPSenderVideo::LogAndSendToNetwork(
|
|||||||
rtc::CritScope cs(&stats_crit_);
|
rtc::CritScope cs(&stats_crit_);
|
||||||
size_t packetized_payload_size = 0;
|
size_t packetized_payload_size = 0;
|
||||||
for (const auto& packet : packets) {
|
for (const auto& packet : packets) {
|
||||||
switch (*packet->packet_type()) {
|
if (*packet->packet_type() == RtpPacketMediaType::kVideo) {
|
||||||
case RtpPacketMediaType::kVideo:
|
video_bitrate_.Update(packet->size(), now_ms);
|
||||||
video_bitrate_.Update(packet->size(), now_ms);
|
packetized_payload_size += packet->payload_size();
|
||||||
packetized_payload_size += packet->payload_size();
|
|
||||||
break;
|
|
||||||
case RtpPacketMediaType::kForwardErrorCorrection:
|
|
||||||
fec_bitrate_.Update(packet->size(), clock_->TimeInMilliseconds());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// AV1 packetizer may produce less packetized bytes than unpacketized.
|
// AV1 packetizer may produce less packetized bytes than unpacketized.
|
||||||
@ -412,39 +324,31 @@ void RTPSenderVideo::LogAndSendToNetwork(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t RTPSenderVideo::FecPacketOverhead() const {
|
size_t RTPSenderVideo::FecPacketOverhead() const {
|
||||||
if (flexfec_enabled())
|
size_t overhead = fec_generator_ ? fec_generator_->MaxPacketOverhead() : 0u;
|
||||||
return flexfec_sender_->MaxPacketOverhead();
|
|
||||||
|
|
||||||
size_t overhead = 0;
|
|
||||||
if (red_enabled()) {
|
if (red_enabled()) {
|
||||||
// The RED overhead is due to a small header.
|
// The RED overhead is due to a small header.
|
||||||
overhead += kRedForFecHeaderLength;
|
overhead += kRedForFecHeaderLength;
|
||||||
}
|
|
||||||
if (ulpfec_enabled()) {
|
// TODO(bugs.webrtc.org/11340): Move this into UlpfecGenerator.
|
||||||
// For ULPFEC, the overhead is the FEC headers plus RED for FEC header
|
if (fec_generator_ &&
|
||||||
// (see above) plus anything in RTP header beyond the 12 bytes base header
|
fec_generator_->GetFecType() == VideoFecGenerator::FecType::kUlpFec) {
|
||||||
// (CSRC list, extensions...)
|
// For ULPFEC, the overhead is the FEC headers plus RED for FEC header
|
||||||
// This reason for the header extensions to be included here is that
|
// (see above) plus anything in RTP header beyond the 12 bytes base header
|
||||||
// from an FEC viewpoint, they are part of the payload to be protected.
|
// (CSRC list, extensions...)
|
||||||
// (The base RTP header is already protected by the FEC header.)
|
// This reason for the header extensions to be included here is that
|
||||||
overhead += ulpfec_generator_.MaxPacketOverhead() +
|
// from an FEC viewpoint, they are part of the payload to be protected.
|
||||||
(rtp_sender_->RtpHeaderLength() - kRtpHeaderSize);
|
// (The base RTP header is already protected by the FEC header.)
|
||||||
|
overhead += rtp_sender_->RtpHeaderLength() - kRtpHeaderSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return overhead;
|
return overhead;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPSenderVideo::SetFecParameters(const FecProtectionParams& delta_params,
|
void RTPSenderVideo::SetFecParameters(const FecProtectionParams& delta_params,
|
||||||
const FecProtectionParams& key_params) {
|
const FecProtectionParams& key_params) {
|
||||||
rtc::CritScope cs(&crit_);
|
if (fec_generator_) {
|
||||||
delta_fec_params_ = delta_params;
|
fec_generator_->SetProtectionParameters(delta_params, key_params);
|
||||||
key_fec_params_ = key_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::optional<uint32_t> RTPSenderVideo::FlexfecSsrc() const {
|
|
||||||
if (flexfec_sender_) {
|
|
||||||
return flexfec_sender_->ssrc();
|
|
||||||
}
|
}
|
||||||
return absl::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPSenderVideo::SetVideoStructure(
|
void RTPSenderVideo::SetVideoStructure(
|
||||||
@ -565,19 +469,6 @@ bool RTPSenderVideo::SendVideo(
|
|||||||
transmit_color_space_next_frame_ ? !IsBaseLayer(video_header) : false;
|
transmit_color_space_next_frame_ ? !IsBaseLayer(video_header) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flexfec_enabled() || ulpfec_enabled()) {
|
|
||||||
rtc::CritScope cs(&crit_);
|
|
||||||
// FEC settings.
|
|
||||||
const FecProtectionParams& fec_params =
|
|
||||||
video_header.frame_type == VideoFrameType::kVideoFrameKey
|
|
||||||
? key_fec_params_
|
|
||||||
: delta_fec_params_;
|
|
||||||
if (flexfec_enabled())
|
|
||||||
flexfec_sender_->SetFecParameters(fec_params);
|
|
||||||
if (ulpfec_enabled())
|
|
||||||
ulpfec_generator_.SetFecParameters(fec_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maximum size of packet including rtp headers.
|
// Maximum size of packet including rtp headers.
|
||||||
// Extra space left in case packet will be resent using fec or rtx.
|
// Extra space left in case packet will be resent using fec or rtx.
|
||||||
int packet_capacity = rtp_sender_->MaxRtpPacketSize() - FecPacketOverhead() -
|
int packet_capacity = rtp_sender_->MaxRtpPacketSize() - FecPacketOverhead() -
|
||||||
@ -769,21 +660,40 @@ bool RTPSenderVideo::SendVideo(
|
|||||||
packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds());
|
packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (protect_packet && fec_generator_) {
|
||||||
|
if (red_enabled() &&
|
||||||
|
exclude_transport_sequence_number_from_fec_experiment_) {
|
||||||
|
// See comments at the top of the file why experiment
|
||||||
|
// "WebRTC-kExcludeTransportSequenceNumberFromFec" is needed in
|
||||||
|
// conjunction with datagram transport.
|
||||||
|
// TODO(sukhanov): We may also need to implement it for flexfec_sender
|
||||||
|
// if we decide to keep this approach in the future.
|
||||||
|
uint16_t transport_senquence_number;
|
||||||
|
if (packet->GetExtension<webrtc::TransportSequenceNumber>(
|
||||||
|
&transport_senquence_number)) {
|
||||||
|
if (!packet->RemoveExtension(webrtc::TransportSequenceNumber::kId)) {
|
||||||
|
RTC_NOTREACHED()
|
||||||
|
<< "Failed to remove transport sequence number, packet="
|
||||||
|
<< packet->ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fec_generator_->AddPacketAndGenerateFec(*packet);
|
||||||
|
}
|
||||||
|
|
||||||
if (red_enabled()) {
|
if (red_enabled()) {
|
||||||
AppendAsRedMaybeWithUlpfec(std::move(packet), protect_packet,
|
std::unique_ptr<RtpPacketToSend> red_packet(new RtpPacketToSend(*packet));
|
||||||
&rtp_packets);
|
BuildRedPayload(*packet, red_packet.get());
|
||||||
|
red_packet->SetPayloadType(*red_payload_type_);
|
||||||
|
|
||||||
|
// Send |red_packet| instead of |packet| for allocated sequence number.
|
||||||
|
red_packet->set_packet_type(RtpPacketMediaType::kVideo);
|
||||||
|
red_packet->set_allow_retransmission(packet->allow_retransmission());
|
||||||
|
rtp_packets.emplace_back(std::move(red_packet));
|
||||||
} else {
|
} else {
|
||||||
packet->set_packet_type(RtpPacketMediaType::kVideo);
|
packet->set_packet_type(RtpPacketMediaType::kVideo);
|
||||||
const RtpPacketToSend& media_packet = *packet;
|
|
||||||
rtp_packets.emplace_back(std::move(packet));
|
rtp_packets.emplace_back(std::move(packet));
|
||||||
if (flexfec_enabled()) {
|
|
||||||
// TODO(brandtr): Remove the FlexFEC code path when FlexfecSender
|
|
||||||
// is wired up to PacedSender instead.
|
|
||||||
if (protect_packet) {
|
|
||||||
flexfec_sender_->AddRtpPacketAndGenerateFec(media_packet);
|
|
||||||
}
|
|
||||||
GenerateAndAppendFlexfec(&rtp_packets);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first_frame) {
|
if (first_frame) {
|
||||||
@ -798,6 +708,22 @@ bool RTPSenderVideo::SendVideo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fec_generator_) {
|
||||||
|
// Fetch any FEC packets generated from the media frame and add them to
|
||||||
|
// the list of packets to send.
|
||||||
|
auto fec_packets = fec_generator_->GetFecPackets();
|
||||||
|
|
||||||
|
// TODO(bugs.webrtc.org/11340): Move sequence number assignment into
|
||||||
|
// UlpfecGenerator.
|
||||||
|
const bool generate_sequence_numbers = !fec_generator_->FecSsrc();
|
||||||
|
for (auto& fec_packet : fec_packets) {
|
||||||
|
if (generate_sequence_numbers) {
|
||||||
|
rtp_sender_->AssignSequenceNumber(fec_packet.get());
|
||||||
|
}
|
||||||
|
rtp_packets.emplace_back(std::move(fec_packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LogAndSendToNetwork(std::move(rtp_packets), unpacketized_payload_size);
|
LogAndSendToNetwork(std::move(rtp_packets), unpacketized_payload_size);
|
||||||
|
|
||||||
TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, "timestamp",
|
TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, "timestamp",
|
||||||
@ -830,8 +756,7 @@ uint32_t RTPSenderVideo::VideoBitrateSent() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RTPSenderVideo::FecOverheadRate() const {
|
uint32_t RTPSenderVideo::FecOverheadRate() const {
|
||||||
rtc::CritScope cs(&stats_crit_);
|
return fec_generator_ ? fec_generator_->CurrentFecRate().bps<uint32_t>() : 0u;
|
||||||
return fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
|
uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
|
||||||
|
|||||||
@ -24,14 +24,13 @@
|
|||||||
#include "api/video/video_codec_type.h"
|
#include "api/video/video_codec_type.h"
|
||||||
#include "api/video/video_frame_type.h"
|
#include "api/video/video_frame_type.h"
|
||||||
#include "modules/include/module_common_types.h"
|
#include "modules/include/module_common_types.h"
|
||||||
#include "modules/rtp_rtcp/include/flexfec_sender.h"
|
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
#include "modules/rtp_rtcp/source/absolute_capture_time_sender.h"
|
#include "modules/rtp_rtcp/source/absolute_capture_time_sender.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
|
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_sender.h"
|
#include "modules/rtp_rtcp/source/rtp_sender.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h"
|
#include "modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_video_header.h"
|
#include "modules/rtp_rtcp/source/rtp_video_header.h"
|
||||||
#include "modules/rtp_rtcp/source/ulpfec_generator.h"
|
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
||||||
#include "rtc_base/critical_section.h"
|
#include "rtc_base/critical_section.h"
|
||||||
#include "rtc_base/one_time_event.h"
|
#include "rtc_base/one_time_event.h"
|
||||||
#include "rtc_base/race_checker.h"
|
#include "rtc_base/race_checker.h"
|
||||||
@ -71,11 +70,11 @@ class RTPSenderVideo {
|
|||||||
Clock* clock = nullptr;
|
Clock* clock = nullptr;
|
||||||
RTPSender* rtp_sender = nullptr;
|
RTPSender* rtp_sender = nullptr;
|
||||||
FlexfecSender* flexfec_sender = nullptr;
|
FlexfecSender* flexfec_sender = nullptr;
|
||||||
|
VideoFecGenerator* fec_generator = nullptr;
|
||||||
FrameEncryptorInterface* frame_encryptor = nullptr;
|
FrameEncryptorInterface* frame_encryptor = nullptr;
|
||||||
bool require_frame_encryption = false;
|
bool require_frame_encryption = false;
|
||||||
bool enable_retransmit_all_layers = false;
|
bool enable_retransmit_all_layers = false;
|
||||||
absl::optional<int> red_payload_type;
|
absl::optional<int> red_payload_type;
|
||||||
absl::optional<int> ulpfec_payload_type;
|
|
||||||
const WebRtcKeyValueConfig* field_trials = nullptr;
|
const WebRtcKeyValueConfig* field_trials = nullptr;
|
||||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer;
|
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer;
|
||||||
};
|
};
|
||||||
@ -115,13 +114,9 @@ class RTPSenderVideo {
|
|||||||
|
|
||||||
// FlexFEC/ULPFEC.
|
// FlexFEC/ULPFEC.
|
||||||
// Set FEC rates, max frames before FEC is sent, and type of FEC masks.
|
// Set FEC rates, max frames before FEC is sent, and type of FEC masks.
|
||||||
// Returns false on failure.
|
|
||||||
void SetFecParameters(const FecProtectionParams& delta_params,
|
void SetFecParameters(const FecProtectionParams& delta_params,
|
||||||
const FecProtectionParams& key_params);
|
const FecProtectionParams& key_params);
|
||||||
|
|
||||||
// FlexFEC.
|
|
||||||
absl::optional<uint32_t> FlexfecSsrc() const;
|
|
||||||
|
|
||||||
uint32_t VideoBitrateSent() const;
|
uint32_t VideoBitrateSent() const;
|
||||||
uint32_t FecOverheadRate() const;
|
uint32_t FecOverheadRate() const;
|
||||||
|
|
||||||
@ -150,27 +145,12 @@ class RTPSenderVideo {
|
|||||||
|
|
||||||
size_t FecPacketOverhead() const RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_);
|
size_t FecPacketOverhead() const RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_);
|
||||||
|
|
||||||
void AppendAsRedMaybeWithUlpfec(
|
|
||||||
std::unique_ptr<RtpPacketToSend> media_packet,
|
|
||||||
bool protect_media_packet,
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>>* packets)
|
|
||||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_);
|
|
||||||
|
|
||||||
// TODO(brandtr): Remove the FlexFEC functions when FlexfecSender has been
|
|
||||||
// moved to PacedSender.
|
|
||||||
void GenerateAndAppendFlexfec(
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>>* packets);
|
|
||||||
|
|
||||||
void LogAndSendToNetwork(
|
void LogAndSendToNetwork(
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets,
|
std::vector<std::unique_ptr<RtpPacketToSend>> packets,
|
||||||
size_t unpacketized_payload_size);
|
size_t unpacketized_payload_size);
|
||||||
|
|
||||||
bool red_enabled() const { return red_payload_type_.has_value(); }
|
bool red_enabled() const { return red_payload_type_.has_value(); }
|
||||||
|
|
||||||
bool ulpfec_enabled() const { return ulpfec_payload_type_.has_value(); }
|
|
||||||
|
|
||||||
bool flexfec_enabled() const { return flexfec_sender_ != nullptr; }
|
|
||||||
|
|
||||||
bool UpdateConditionalRetransmit(uint8_t temporal_id,
|
bool UpdateConditionalRetransmit(uint8_t temporal_id,
|
||||||
int64_t expected_retransmission_time_ms)
|
int64_t expected_retransmission_time_ms)
|
||||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(stats_crit_);
|
RTC_EXCLUSIVE_LOCKS_REQUIRED(stats_crit_);
|
||||||
@ -201,22 +181,10 @@ class RTPSenderVideo {
|
|||||||
// Should never be held when calling out of this class.
|
// Should never be held when calling out of this class.
|
||||||
rtc::CriticalSection crit_;
|
rtc::CriticalSection crit_;
|
||||||
|
|
||||||
// RED/ULPFEC.
|
|
||||||
const absl::optional<int> red_payload_type_;
|
const absl::optional<int> red_payload_type_;
|
||||||
const absl::optional<int> ulpfec_payload_type_;
|
VideoFecGenerator* const fec_generator_;
|
||||||
UlpfecGenerator ulpfec_generator_ RTC_GUARDED_BY(send_checker_);
|
|
||||||
|
|
||||||
// FlexFEC.
|
|
||||||
FlexfecSender* const flexfec_sender_;
|
|
||||||
|
|
||||||
// FEC parameters, applicable to either ULPFEC or FlexFEC.
|
|
||||||
FecProtectionParams delta_fec_params_ RTC_GUARDED_BY(crit_);
|
|
||||||
FecProtectionParams key_fec_params_ RTC_GUARDED_BY(crit_);
|
|
||||||
|
|
||||||
rtc::CriticalSection stats_crit_;
|
rtc::CriticalSection stats_crit_;
|
||||||
// Bitrate used for FEC payload, RED headers, RTP headers for FEC packets
|
|
||||||
// and any padding overhead.
|
|
||||||
RateStatistics fec_bitrate_ RTC_GUARDED_BY(stats_crit_);
|
|
||||||
// Bitrate used for video payload and RTP headers.
|
// Bitrate used for video payload and RTP headers.
|
||||||
RateStatistics video_bitrate_ RTC_GUARDED_BY(stats_crit_);
|
RateStatistics video_bitrate_ RTC_GUARDED_BY(stats_crit_);
|
||||||
RateStatistics packetization_overhead_bitrate_ RTC_GUARDED_BY(stats_crit_);
|
RateStatistics packetization_overhead_bitrate_ RTC_GUARDED_BY(stats_crit_);
|
||||||
|
|||||||
@ -131,7 +131,7 @@ class TestRtpSenderVideo : public RTPSenderVideo {
|
|||||||
Config config;
|
Config config;
|
||||||
config.clock = clock;
|
config.clock = clock;
|
||||||
config.rtp_sender = rtp_sender;
|
config.rtp_sender = rtp_sender;
|
||||||
config.flexfec_sender = flexfec_sender;
|
config.fec_generator = flexfec_sender;
|
||||||
config.field_trials = &field_trials;
|
config.field_trials = &field_trials;
|
||||||
return config;
|
return config;
|
||||||
}()) {}
|
}()) {}
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
|
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
|
#include "rtc_base/critical_section.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -62,128 +63,119 @@ constexpr uint32_t kUnknownSsrc = 0;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
RedPacket::RedPacket(size_t length)
|
UlpfecGenerator::Params::Params() = default;
|
||||||
: data_(new uint8_t[length]), length_(length), header_length_(0) {}
|
UlpfecGenerator::Params::Params(FecProtectionParams delta_params,
|
||||||
|
FecProtectionParams keyframe_params)
|
||||||
|
: delta_params(delta_params), keyframe_params(keyframe_params) {}
|
||||||
|
|
||||||
RedPacket::~RedPacket() = default;
|
UlpfecGenerator::UlpfecGenerator(int red_payload_type,
|
||||||
|
int ulpfec_payload_type,
|
||||||
void RedPacket::CreateHeader(const uint8_t* rtp_header,
|
Clock* clock)
|
||||||
size_t header_length,
|
: red_payload_type_(red_payload_type),
|
||||||
int red_payload_type,
|
ulpfec_payload_type_(ulpfec_payload_type),
|
||||||
int payload_type) {
|
clock_(clock),
|
||||||
RTC_DCHECK_LE(header_length + kRedForFecHeaderLength, length_);
|
fec_(ForwardErrorCorrection::CreateUlpfec(kUnknownSsrc)),
|
||||||
memcpy(data_.get(), rtp_header, header_length);
|
|
||||||
// Replace payload type.
|
|
||||||
data_[1] &= 0x80;
|
|
||||||
data_[1] += red_payload_type;
|
|
||||||
// Add RED header
|
|
||||||
// f-bit always 0
|
|
||||||
data_[header_length] = static_cast<uint8_t>(payload_type);
|
|
||||||
header_length_ = header_length + kRedForFecHeaderLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RedPacket::SetSeqNum(int seq_num) {
|
|
||||||
RTC_DCHECK_GE(seq_num, 0);
|
|
||||||
RTC_DCHECK_LT(seq_num, 1 << 16);
|
|
||||||
|
|
||||||
ByteWriter<uint16_t>::WriteBigEndian(&data_[2], seq_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RedPacket::AssignPayload(const uint8_t* payload, size_t length) {
|
|
||||||
RTC_DCHECK_LE(header_length_ + length, length_);
|
|
||||||
memcpy(data_.get() + header_length_, payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RedPacket::ClearMarkerBit() {
|
|
||||||
data_[1] &= 0x7F;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* RedPacket::data() const {
|
|
||||||
return data_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t RedPacket::length() const {
|
|
||||||
return length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
UlpfecGenerator::UlpfecGenerator()
|
|
||||||
: UlpfecGenerator(ForwardErrorCorrection::CreateUlpfec(kUnknownSsrc)) {}
|
|
||||||
|
|
||||||
UlpfecGenerator::UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec)
|
|
||||||
: fec_(std::move(fec)),
|
|
||||||
last_media_packet_rtp_header_length_(0),
|
|
||||||
num_protected_frames_(0),
|
num_protected_frames_(0),
|
||||||
min_num_media_packets_(1) {
|
min_num_media_packets_(1),
|
||||||
memset(¶ms_, 0, sizeof(params_));
|
keyframe_in_process_(false),
|
||||||
memset(&new_params_, 0, sizeof(new_params_));
|
fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {}
|
||||||
}
|
|
||||||
|
// Used by FlexFecSender, payload types are unused.
|
||||||
|
UlpfecGenerator::UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec,
|
||||||
|
Clock* clock)
|
||||||
|
: red_payload_type_(0),
|
||||||
|
ulpfec_payload_type_(0),
|
||||||
|
clock_(clock),
|
||||||
|
fec_(std::move(fec)),
|
||||||
|
num_protected_frames_(0),
|
||||||
|
min_num_media_packets_(1),
|
||||||
|
keyframe_in_process_(false),
|
||||||
|
fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {}
|
||||||
|
|
||||||
UlpfecGenerator::~UlpfecGenerator() = default;
|
UlpfecGenerator::~UlpfecGenerator() = default;
|
||||||
|
|
||||||
void UlpfecGenerator::SetFecParameters(const FecProtectionParams& params) {
|
void UlpfecGenerator::SetProtectionParameters(
|
||||||
RTC_DCHECK_GE(params.fec_rate, 0);
|
const FecProtectionParams& delta_params,
|
||||||
RTC_DCHECK_LE(params.fec_rate, 255);
|
const FecProtectionParams& key_params) {
|
||||||
|
RTC_DCHECK_GE(delta_params.fec_rate, 0);
|
||||||
|
RTC_DCHECK_LE(delta_params.fec_rate, 255);
|
||||||
|
RTC_DCHECK_GE(key_params.fec_rate, 0);
|
||||||
|
RTC_DCHECK_LE(key_params.fec_rate, 255);
|
||||||
// Store the new params and apply them for the next set of FEC packets being
|
// Store the new params and apply them for the next set of FEC packets being
|
||||||
// produced.
|
// produced.
|
||||||
new_params_ = params;
|
rtc::CritScope cs(&crit_);
|
||||||
if (params.fec_rate > kHighProtectionThreshold) {
|
pending_params_.emplace(delta_params, key_params);
|
||||||
min_num_media_packets_ = kMinMediaPackets;
|
|
||||||
} else {
|
|
||||||
min_num_media_packets_ = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int UlpfecGenerator::AddRtpPacketAndGenerateFec(
|
void UlpfecGenerator::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
|
||||||
const rtc::CopyOnWriteBuffer& data_buffer,
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
size_t rtp_header_length) {
|
|
||||||
RTC_DCHECK(generated_fec_packets_.empty());
|
RTC_DCHECK(generated_fec_packets_.empty());
|
||||||
|
|
||||||
if (media_packets_.empty()) {
|
if (media_packets_.empty()) {
|
||||||
params_ = new_params_;
|
rtc::CritScope cs(&crit_);
|
||||||
|
if (pending_params_) {
|
||||||
|
current_params_ = *pending_params_;
|
||||||
|
pending_params_.reset();
|
||||||
|
|
||||||
|
if (CurrentParams().fec_rate > kHighProtectionThreshold) {
|
||||||
|
min_num_media_packets_ = kMinMediaPackets;
|
||||||
|
} else {
|
||||||
|
min_num_media_packets_ = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyframe_in_process_ = packet.is_key_frame();
|
||||||
}
|
}
|
||||||
|
RTC_DCHECK_EQ(packet.is_key_frame(), keyframe_in_process_);
|
||||||
|
|
||||||
bool complete_frame = false;
|
bool complete_frame = false;
|
||||||
const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
|
const bool marker_bit = packet.Marker();
|
||||||
if (media_packets_.size() < kUlpfecMaxMediaPackets) {
|
if (media_packets_.size() < kUlpfecMaxMediaPackets) {
|
||||||
// Our packet masks can only protect up to |kUlpfecMaxMediaPackets| packets.
|
// Our packet masks can only protect up to |kUlpfecMaxMediaPackets| packets.
|
||||||
std::unique_ptr<ForwardErrorCorrection::Packet> packet(
|
auto fec_packet = std::make_unique<ForwardErrorCorrection::Packet>();
|
||||||
new ForwardErrorCorrection::Packet());
|
fec_packet->data = packet.Buffer();
|
||||||
RTC_DCHECK_GE(data_buffer.size(), rtp_header_length);
|
media_packets_.push_back(std::move(fec_packet));
|
||||||
packet->data = data_buffer;
|
|
||||||
media_packets_.push_back(std::move(packet));
|
// Keep a copy of the last RTP packet, so we can copy the RTP header
|
||||||
// Keep track of the RTP header length, so we can copy the RTP header
|
// from it when creating newly generated ULPFEC+RED packets.
|
||||||
// from |packet| to newly generated ULPFEC+RED packets.
|
RTC_DCHECK_GE(packet.headers_size(), kRtpHeaderSize);
|
||||||
RTC_DCHECK_GE(rtp_header_length, kRtpHeaderSize);
|
last_media_packet_ = packet;
|
||||||
last_media_packet_rtp_header_length_ = rtp_header_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (marker_bit) {
|
if (marker_bit) {
|
||||||
++num_protected_frames_;
|
++num_protected_frames_;
|
||||||
complete_frame = true;
|
complete_frame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto params = CurrentParams();
|
||||||
|
|
||||||
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
|
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
|
||||||
// (1) the excess overhead (actual overhead - requested/target overhead) is
|
// (1) the excess overhead (actual overhead - requested/target overhead) is
|
||||||
// less than |kMaxExcessOverhead|, and
|
// less than |kMaxExcessOverhead|, and
|
||||||
// (2) at least |min_num_media_packets_| media packets is reached.
|
// (2) at least |min_num_media_packets_| media packets is reached.
|
||||||
if (complete_frame &&
|
if (complete_frame &&
|
||||||
(num_protected_frames_ == params_.max_fec_frames ||
|
(num_protected_frames_ == params.max_fec_frames ||
|
||||||
(ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
|
(ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
|
||||||
// We are not using Unequal Protection feature of the parity erasure code.
|
// We are not using Unequal Protection feature of the parity erasure code.
|
||||||
constexpr int kNumImportantPackets = 0;
|
constexpr int kNumImportantPackets = 0;
|
||||||
constexpr bool kUseUnequalProtection = false;
|
constexpr bool kUseUnequalProtection = false;
|
||||||
int ret = fec_->EncodeFec(media_packets_, params_.fec_rate,
|
fec_->EncodeFec(media_packets_, params.fec_rate, kNumImportantPackets,
|
||||||
kNumImportantPackets, kUseUnequalProtection,
|
kUseUnequalProtection, params.fec_mask_type,
|
||||||
params_.fec_mask_type, &generated_fec_packets_);
|
&generated_fec_packets_);
|
||||||
if (generated_fec_packets_.empty()) {
|
if (generated_fec_packets_.empty()) {
|
||||||
ResetState();
|
ResetState();
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UlpfecGenerator::ExcessOverheadBelowMax() const {
|
bool UlpfecGenerator::ExcessOverheadBelowMax() const {
|
||||||
return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
|
|
||||||
|
return ((Overhead() - CurrentParams().fec_rate) < kMaxExcessOverhead);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UlpfecGenerator::MinimumMediaPacketsReached() const {
|
bool UlpfecGenerator::MinimumMediaPacketsReached() const {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
float average_num_packets_per_frame =
|
float average_num_packets_per_frame =
|
||||||
static_cast<float>(media_packets_.size()) / num_protected_frames_;
|
static_cast<float>(media_packets_.size()) / num_protected_frames_;
|
||||||
int num_media_packets = static_cast<int>(media_packets_.size());
|
int num_media_packets = static_cast<int>(media_packets_.size());
|
||||||
@ -196,61 +188,79 @@ bool UlpfecGenerator::MinimumMediaPacketsReached() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UlpfecGenerator::FecAvailable() const {
|
const FecProtectionParams& UlpfecGenerator::CurrentParams() const {
|
||||||
return !generated_fec_packets_.empty();
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
}
|
return keyframe_in_process_ ? current_params_.keyframe_params
|
||||||
|
: current_params_.delta_params;
|
||||||
size_t UlpfecGenerator::NumAvailableFecPackets() const {
|
|
||||||
return generated_fec_packets_.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t UlpfecGenerator::MaxPacketOverhead() const {
|
size_t UlpfecGenerator::MaxPacketOverhead() const {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
return fec_->MaxPacketOverhead();
|
return fec_->MaxPacketOverhead();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RedPacket>> UlpfecGenerator::GetUlpfecPacketsAsRed(
|
std::vector<std::unique_ptr<RtpPacketToSend>> UlpfecGenerator::GetFecPackets() {
|
||||||
int red_payload_type,
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
int ulpfec_payload_type,
|
if (generated_fec_packets_.empty()) {
|
||||||
uint16_t first_seq_num) {
|
return std::vector<std::unique_ptr<RtpPacketToSend>>();
|
||||||
std::vector<std::unique_ptr<RedPacket>> red_packets;
|
}
|
||||||
red_packets.reserve(generated_fec_packets_.size());
|
|
||||||
RTC_DCHECK(!media_packets_.empty());
|
// Wrap FEC packet (including FEC headers) in a RED packet. Since the
|
||||||
ForwardErrorCorrection::Packet* last_media_packet =
|
// FEC packets in |generated_fec_packets_| don't have RTP headers, we
|
||||||
media_packets_.back().get();
|
// reuse the header from the last media packet.
|
||||||
uint16_t seq_num = first_seq_num;
|
RTC_CHECK(last_media_packet_.has_value());
|
||||||
|
last_media_packet_->SetPayloadSize(0);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets;
|
||||||
|
fec_packets.reserve(generated_fec_packets_.size());
|
||||||
|
|
||||||
|
size_t total_fec_size_bytes = 0;
|
||||||
for (const auto* fec_packet : generated_fec_packets_) {
|
for (const auto* fec_packet : generated_fec_packets_) {
|
||||||
// Wrap FEC packet (including FEC headers) in a RED packet. Since the
|
std::unique_ptr<RtpPacketToSend> red_packet =
|
||||||
// FEC packets in |generated_fec_packets_| don't have RTP headers, we
|
std::make_unique<RtpPacketToSend>(*last_media_packet_);
|
||||||
// reuse the header from the last media packet.
|
red_packet->SetPayloadType(red_payload_type_);
|
||||||
RTC_DCHECK_GT(last_media_packet_rtp_header_length_, 0);
|
red_packet->SetMarker(false);
|
||||||
std::unique_ptr<RedPacket> red_packet(
|
uint8_t* payload_buffer = red_packet->SetPayloadSize(
|
||||||
new RedPacket(last_media_packet_rtp_header_length_ +
|
kRedForFecHeaderLength + fec_packet->data.size());
|
||||||
kRedForFecHeaderLength + fec_packet->data.size()));
|
// Primary RED header with F bit unset.
|
||||||
red_packet->CreateHeader(last_media_packet->data.data(),
|
// See https://tools.ietf.org/html/rfc2198#section-3
|
||||||
last_media_packet_rtp_header_length_,
|
payload_buffer[0] = ulpfec_payload_type_; // RED header.
|
||||||
red_payload_type, ulpfec_payload_type);
|
memcpy(&payload_buffer[1], fec_packet->data.data(),
|
||||||
red_packet->SetSeqNum(seq_num++);
|
fec_packet->data.size());
|
||||||
red_packet->ClearMarkerBit();
|
total_fec_size_bytes += red_packet->size();
|
||||||
red_packet->AssignPayload(fec_packet->data.data(), fec_packet->data.size());
|
red_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
|
||||||
red_packets.push_back(std::move(red_packet));
|
red_packet->set_allow_retransmission(false);
|
||||||
|
fec_packets.push_back(std::move(red_packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetState();
|
ResetState();
|
||||||
|
|
||||||
return red_packets;
|
rtc::CritScope cs(&crit_);
|
||||||
|
fec_bitrate_.Update(total_fec_size_bytes, clock_->TimeInMilliseconds());
|
||||||
|
|
||||||
|
return fec_packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataRate UlpfecGenerator::CurrentFecRate() const {
|
||||||
|
rtc::CritScope cs(&crit_);
|
||||||
|
return DataRate::BitsPerSec(
|
||||||
|
fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
int UlpfecGenerator::Overhead() const {
|
int UlpfecGenerator::Overhead() const {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
RTC_DCHECK(!media_packets_.empty());
|
RTC_DCHECK(!media_packets_.empty());
|
||||||
int num_fec_packets =
|
int num_fec_packets =
|
||||||
fec_->NumFecPackets(media_packets_.size(), params_.fec_rate);
|
fec_->NumFecPackets(media_packets_.size(), CurrentParams().fec_rate);
|
||||||
|
|
||||||
// Return the overhead in Q8.
|
// Return the overhead in Q8.
|
||||||
return (num_fec_packets << 8) / media_packets_.size();
|
return (num_fec_packets << 8) / media_packets_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UlpfecGenerator::ResetState() {
|
void UlpfecGenerator::ResetState() {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
media_packets_.clear();
|
media_packets_.clear();
|
||||||
last_media_packet_rtp_header_length_ = 0;
|
last_media_packet_.reset();
|
||||||
generated_fec_packets_.clear();
|
generated_fec_packets_.clear();
|
||||||
num_protected_frames_ = 0;
|
num_protected_frames_ = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,63 +20,54 @@
|
|||||||
|
|
||||||
#include "modules/include/module_fec_types.h"
|
#include "modules/include/module_fec_types.h"
|
||||||
#include "modules/rtp_rtcp/source/forward_error_correction.h"
|
#include "modules/rtp_rtcp/source/forward_error_correction.h"
|
||||||
|
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
||||||
|
#include "rtc_base/critical_section.h"
|
||||||
|
#include "rtc_base/race_checker.h"
|
||||||
|
#include "rtc_base/rate_statistics.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
class FlexfecSender;
|
class FlexfecSender;
|
||||||
|
|
||||||
class RedPacket {
|
class UlpfecGenerator : public VideoFecGenerator {
|
||||||
public:
|
|
||||||
explicit RedPacket(size_t length);
|
|
||||||
~RedPacket();
|
|
||||||
|
|
||||||
void CreateHeader(const uint8_t* rtp_header,
|
|
||||||
size_t header_length,
|
|
||||||
int red_payload_type,
|
|
||||||
int payload_type);
|
|
||||||
void SetSeqNum(int seq_num);
|
|
||||||
void AssignPayload(const uint8_t* payload, size_t length);
|
|
||||||
void ClearMarkerBit();
|
|
||||||
uint8_t* data() const;
|
|
||||||
size_t length() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<uint8_t[]> data_;
|
|
||||||
size_t length_;
|
|
||||||
size_t header_length_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class UlpfecGenerator {
|
|
||||||
friend class FlexfecSender;
|
friend class FlexfecSender;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UlpfecGenerator();
|
UlpfecGenerator(int red_payload_type, int ulpfec_payload_type, Clock* clock);
|
||||||
~UlpfecGenerator();
|
~UlpfecGenerator();
|
||||||
|
|
||||||
void SetFecParameters(const FecProtectionParams& params);
|
FecType GetFecType() const override {
|
||||||
|
return VideoFecGenerator::FecType::kUlpFec;
|
||||||
|
}
|
||||||
|
absl::optional<uint32_t> FecSsrc() override { return absl::nullopt; }
|
||||||
|
|
||||||
|
void SetProtectionParameters(const FecProtectionParams& delta_params,
|
||||||
|
const FecProtectionParams& key_params) override;
|
||||||
|
|
||||||
// Adds a media packet to the internal buffer. When enough media packets
|
// Adds a media packet to the internal buffer. When enough media packets
|
||||||
// have been added, the FEC packets are generated and stored internally.
|
// have been added, the FEC packets are generated and stored internally.
|
||||||
// These FEC packets are then obtained by calling GetFecPacketsAsRed().
|
// These FEC packets are then obtained by calling GetFecPacketsAsRed().
|
||||||
int AddRtpPacketAndGenerateFec(const rtc::CopyOnWriteBuffer& data_buffer,
|
void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
|
||||||
size_t rtp_header_length);
|
|
||||||
|
|
||||||
// Returns true if there are generated FEC packets available.
|
|
||||||
bool FecAvailable() const;
|
|
||||||
|
|
||||||
size_t NumAvailableFecPackets() const;
|
|
||||||
|
|
||||||
// Returns the overhead, per packet, for FEC (and possibly RED).
|
// Returns the overhead, per packet, for FEC (and possibly RED).
|
||||||
size_t MaxPacketOverhead() const;
|
size_t MaxPacketOverhead() const override;
|
||||||
|
|
||||||
// Returns generated FEC packets with RED headers added.
|
std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() override;
|
||||||
std::vector<std::unique_ptr<RedPacket>> GetUlpfecPacketsAsRed(
|
|
||||||
int red_payload_type,
|
// Current rate of FEC packets generated, including all RTP-level headers.
|
||||||
int ulpfec_payload_type,
|
DataRate CurrentFecRate() const override;
|
||||||
uint16_t first_seq_num);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec);
|
struct Params {
|
||||||
|
Params();
|
||||||
|
Params(FecProtectionParams delta_params,
|
||||||
|
FecProtectionParams keyframe_params);
|
||||||
|
|
||||||
|
FecProtectionParams delta_params;
|
||||||
|
FecProtectionParams keyframe_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec, Clock* clock);
|
||||||
|
|
||||||
// Overhead is defined as relative to the number of media packets, and not
|
// Overhead is defined as relative to the number of media packets, and not
|
||||||
// relative to total number of packets. This definition is inherited from the
|
// relative to total number of packets. This definition is inherited from the
|
||||||
@ -97,16 +88,31 @@ class UlpfecGenerator {
|
|||||||
// (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
|
// (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
|
||||||
bool MinimumMediaPacketsReached() const;
|
bool MinimumMediaPacketsReached() const;
|
||||||
|
|
||||||
|
const FecProtectionParams& CurrentParams() const;
|
||||||
|
|
||||||
void ResetState();
|
void ResetState();
|
||||||
|
|
||||||
std::unique_ptr<ForwardErrorCorrection> fec_;
|
const int red_payload_type_;
|
||||||
ForwardErrorCorrection::PacketList media_packets_;
|
const int ulpfec_payload_type_;
|
||||||
size_t last_media_packet_rtp_header_length_;
|
Clock* const clock_;
|
||||||
std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_;
|
|
||||||
int num_protected_frames_;
|
rtc::RaceChecker race_checker_;
|
||||||
int min_num_media_packets_;
|
const std::unique_ptr<ForwardErrorCorrection> fec_
|
||||||
FecProtectionParams params_;
|
RTC_GUARDED_BY(race_checker_);
|
||||||
FecProtectionParams new_params_;
|
ForwardErrorCorrection::PacketList media_packets_
|
||||||
|
RTC_GUARDED_BY(race_checker_);
|
||||||
|
absl::optional<RtpPacketToSend> last_media_packet_
|
||||||
|
RTC_GUARDED_BY(race_checker_);
|
||||||
|
std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_
|
||||||
|
RTC_GUARDED_BY(race_checker_);
|
||||||
|
int num_protected_frames_ RTC_GUARDED_BY(race_checker_);
|
||||||
|
int min_num_media_packets_ RTC_GUARDED_BY(race_checker_);
|
||||||
|
Params current_params_ RTC_GUARDED_BY(race_checker_);
|
||||||
|
bool keyframe_in_process_ RTC_GUARDED_BY(race_checker_);
|
||||||
|
|
||||||
|
rtc::CriticalSection crit_;
|
||||||
|
absl::optional<Params> pending_params_ RTC_GUARDED_BY(crit_);
|
||||||
|
RateStatistics fec_bitrate_ RTC_GUARDED_BY(crit_);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -35,11 +35,8 @@ void VerifyHeader(uint16_t seq_num,
|
|||||||
uint32_t timestamp,
|
uint32_t timestamp,
|
||||||
int red_payload_type,
|
int red_payload_type,
|
||||||
int fec_payload_type,
|
int fec_payload_type,
|
||||||
RedPacket* packet,
|
bool marker_bit,
|
||||||
bool marker_bit) {
|
const rtc::CopyOnWriteBuffer& data) {
|
||||||
EXPECT_GT(packet->length(), kRtpHeaderSize);
|
|
||||||
EXPECT_TRUE(packet->data() != NULL);
|
|
||||||
uint8_t* data = packet->data();
|
|
||||||
// Marker bit not set.
|
// Marker bit not set.
|
||||||
EXPECT_EQ(marker_bit ? 0x80 : 0, data[1] & 0x80);
|
EXPECT_EQ(marker_bit ? 0x80 : 0, data[1] & 0x80);
|
||||||
EXPECT_EQ(red_payload_type, data[1] & 0x7F);
|
EXPECT_EQ(red_payload_type, data[1] & 0x7F);
|
||||||
@ -52,8 +49,12 @@ void VerifyHeader(uint16_t seq_num,
|
|||||||
|
|
||||||
class UlpfecGeneratorTest : public ::testing::Test {
|
class UlpfecGeneratorTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
UlpfecGeneratorTest() : packet_generator_(kMediaSsrc) {}
|
UlpfecGeneratorTest()
|
||||||
|
: fake_clock_(1),
|
||||||
|
ulpfec_generator_(kRedPayloadType, kFecPayloadType, &fake_clock_),
|
||||||
|
packet_generator_(kMediaSsrc) {}
|
||||||
|
|
||||||
|
SimulatedClock fake_clock_;
|
||||||
UlpfecGenerator ulpfec_generator_;
|
UlpfecGenerator ulpfec_generator_;
|
||||||
AugmentedPacketGenerator packet_generator_;
|
AugmentedPacketGenerator packet_generator_;
|
||||||
};
|
};
|
||||||
@ -81,24 +82,22 @@ TEST_F(UlpfecGeneratorTest, NoEmptyFecWithSeqNumGaps) {
|
|||||||
protected_packets.push_back({21, 0, 55, 0});
|
protected_packets.push_back({21, 0, 55, 0});
|
||||||
protected_packets.push_back({13, 3, 57, 1});
|
protected_packets.push_back({13, 3, 57, 1});
|
||||||
FecProtectionParams params = {117, 3, kFecMaskBursty};
|
FecProtectionParams params = {117, 3, kFecMaskBursty};
|
||||||
ulpfec_generator_.SetFecParameters(params);
|
ulpfec_generator_.SetProtectionParameters(params, params);
|
||||||
uint8_t packet[28] = {0};
|
|
||||||
for (Packet p : protected_packets) {
|
for (Packet p : protected_packets) {
|
||||||
if (p.marker_bit) {
|
RtpPacketToSend packet(nullptr);
|
||||||
packet[1] |= 0x80;
|
packet.SetMarker(p.marker_bit);
|
||||||
|
packet.AllocateExtension(RTPExtensionType::kRtpExtensionMid,
|
||||||
|
p.header_size - packet.headers_size());
|
||||||
|
packet.SetSequenceNumber(p.seq_num);
|
||||||
|
packet.AllocatePayload(p.payload_size);
|
||||||
|
ulpfec_generator_.AddPacketAndGenerateFec(packet);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
|
ulpfec_generator_.GetFecPackets();
|
||||||
|
if (!p.marker_bit) {
|
||||||
|
EXPECT_TRUE(fec_packets.empty());
|
||||||
} else {
|
} else {
|
||||||
packet[1] &= ~0x80;
|
EXPECT_FALSE(fec_packets.empty());
|
||||||
}
|
|
||||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[2], p.seq_num);
|
|
||||||
ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
|
||||||
rtc::CopyOnWriteBuffer(packet, p.payload_size + p.header_size),
|
|
||||||
p.header_size);
|
|
||||||
size_t num_fec_packets = ulpfec_generator_.NumAvailableFecPackets();
|
|
||||||
if (num_fec_packets > 0) {
|
|
||||||
std::vector<std::unique_ptr<RedPacket>> fec_packets =
|
|
||||||
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType,
|
|
||||||
kFecPayloadType, 100);
|
|
||||||
EXPECT_EQ(num_fec_packets, fec_packets.size());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,24 +112,28 @@ TEST_F(UlpfecGeneratorTest, OneFrameFec) {
|
|||||||
constexpr size_t kNumPackets = 4;
|
constexpr size_t kNumPackets = 4;
|
||||||
FecProtectionParams params = {15, 3, kFecMaskRandom};
|
FecProtectionParams params = {15, 3, kFecMaskRandom};
|
||||||
packet_generator_.NewFrame(kNumPackets);
|
packet_generator_.NewFrame(kNumPackets);
|
||||||
ulpfec_generator_.SetFecParameters(params); // Expecting one FEC packet.
|
// Expecting one FEC packet.
|
||||||
|
ulpfec_generator_.SetProtectionParameters(params, params);
|
||||||
uint32_t last_timestamp = 0;
|
uint32_t last_timestamp = 0;
|
||||||
for (size_t i = 0; i < kNumPackets; ++i) {
|
for (size_t i = 0; i < kNumPackets; ++i) {
|
||||||
std::unique_ptr<AugmentedPacket> packet =
|
std::unique_ptr<AugmentedPacket> packet =
|
||||||
packet_generator_.NextPacket(i, 10);
|
packet_generator_.NextPacket(i, 10);
|
||||||
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(packet->data,
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
kRtpHeaderSize));
|
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
||||||
|
ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
|
||||||
last_timestamp = packet->header.timestamp;
|
last_timestamp = packet->header.timestamp;
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(ulpfec_generator_.FecAvailable());
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
ulpfec_generator_.GetFecPackets();
|
||||||
std::vector<std::unique_ptr<RedPacket>> red_packets =
|
EXPECT_EQ(fec_packets.size(), 1u);
|
||||||
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
||||||
seq_num);
|
fec_packets[0]->SetSequenceNumber(seq_num);
|
||||||
EXPECT_FALSE(ulpfec_generator_.FecAvailable());
|
EXPECT_TRUE(ulpfec_generator_.GetFecPackets().empty());
|
||||||
ASSERT_EQ(1u, red_packets.size());
|
|
||||||
VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType,
|
EXPECT_EQ(fec_packets[0]->headers_size(), kRtpHeaderSize);
|
||||||
red_packets.front().get(), false);
|
|
||||||
|
VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType, false,
|
||||||
|
fec_packets[0]->Buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UlpfecGeneratorTest, TwoFrameFec) {
|
TEST_F(UlpfecGeneratorTest, TwoFrameFec) {
|
||||||
@ -145,27 +148,27 @@ TEST_F(UlpfecGeneratorTest, TwoFrameFec) {
|
|||||||
constexpr size_t kNumFrames = 2;
|
constexpr size_t kNumFrames = 2;
|
||||||
|
|
||||||
FecProtectionParams params = {15, 3, kFecMaskRandom};
|
FecProtectionParams params = {15, 3, kFecMaskRandom};
|
||||||
ulpfec_generator_.SetFecParameters(params); // Expecting one FEC packet.
|
// Expecting one FEC packet.
|
||||||
|
ulpfec_generator_.SetProtectionParameters(params, params);
|
||||||
uint32_t last_timestamp = 0;
|
uint32_t last_timestamp = 0;
|
||||||
for (size_t i = 0; i < kNumFrames; ++i) {
|
for (size_t i = 0; i < kNumFrames; ++i) {
|
||||||
packet_generator_.NewFrame(kNumPackets);
|
packet_generator_.NewFrame(kNumPackets);
|
||||||
for (size_t j = 0; j < kNumPackets; ++j) {
|
for (size_t j = 0; j < kNumPackets; ++j) {
|
||||||
std::unique_ptr<AugmentedPacket> packet =
|
std::unique_ptr<AugmentedPacket> packet =
|
||||||
packet_generator_.NextPacket(i * kNumPackets + j, 10);
|
packet_generator_.NextPacket(i * kNumPackets + j, 10);
|
||||||
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
packet->data, kRtpHeaderSize));
|
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
||||||
|
ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
|
||||||
last_timestamp = packet->header.timestamp;
|
last_timestamp = packet->header.timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(ulpfec_generator_.FecAvailable());
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
|
ulpfec_generator_.GetFecPackets();
|
||||||
|
EXPECT_EQ(fec_packets.size(), 1u);
|
||||||
const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
||||||
std::vector<std::unique_ptr<RedPacket>> red_packets =
|
fec_packets[0]->SetSequenceNumber(seq_num);
|
||||||
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType, false,
|
||||||
seq_num);
|
fec_packets[0]->Buffer());
|
||||||
EXPECT_FALSE(ulpfec_generator_.FecAvailable());
|
|
||||||
ASSERT_EQ(1u, red_packets.size());
|
|
||||||
VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType,
|
|
||||||
red_packets.front().get(), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UlpfecGeneratorTest, MixedMediaRtpHeaderLengths) {
|
TEST_F(UlpfecGeneratorTest, MixedMediaRtpHeaderLengths) {
|
||||||
@ -174,34 +177,43 @@ TEST_F(UlpfecGeneratorTest, MixedMediaRtpHeaderLengths) {
|
|||||||
|
|
||||||
// Only one frame required to generate FEC.
|
// Only one frame required to generate FEC.
|
||||||
FecProtectionParams params = {127, 1, kFecMaskRandom};
|
FecProtectionParams params = {127, 1, kFecMaskRandom};
|
||||||
ulpfec_generator_.SetFecParameters(params);
|
ulpfec_generator_.SetProtectionParameters(params, params);
|
||||||
|
|
||||||
// Fill up internal buffer with media packets with short RTP header length.
|
// Fill up internal buffer with media packets with short RTP header length.
|
||||||
packet_generator_.NewFrame(kUlpfecMaxMediaPackets + 1);
|
packet_generator_.NewFrame(kUlpfecMaxMediaPackets + 1);
|
||||||
for (size_t i = 0; i < kUlpfecMaxMediaPackets; ++i) {
|
for (size_t i = 0; i < kUlpfecMaxMediaPackets; ++i) {
|
||||||
std::unique_ptr<AugmentedPacket> packet =
|
std::unique_ptr<AugmentedPacket> packet =
|
||||||
packet_generator_.NextPacket(i, 10);
|
packet_generator_.NextPacket(i, 10);
|
||||||
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
packet->data, kShortRtpHeaderLength));
|
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
||||||
EXPECT_FALSE(ulpfec_generator_.FecAvailable());
|
EXPECT_EQ(rtp_packet.headers_size(), kShortRtpHeaderLength);
|
||||||
|
ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
|
||||||
|
EXPECT_TRUE(ulpfec_generator_.GetFecPackets().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kick off FEC generation with media packet with long RTP header length.
|
// Kick off FEC generation with media packet with long RTP header length.
|
||||||
// Since the internal buffer is full, this packet will not be protected.
|
// Since the internal buffer is full, this packet will not be protected.
|
||||||
std::unique_ptr<AugmentedPacket> packet =
|
std::unique_ptr<AugmentedPacket> packet =
|
||||||
packet_generator_.NextPacket(kUlpfecMaxMediaPackets, 10);
|
packet_generator_.NextPacket(kUlpfecMaxMediaPackets, 10);
|
||||||
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
packet->data, kLongRtpHeaderLength));
|
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
||||||
EXPECT_TRUE(ulpfec_generator_.FecAvailable());
|
EXPECT_TRUE(rtp_packet.SetPayloadSize(0) != nullptr);
|
||||||
|
const uint32_t csrcs[]{1};
|
||||||
|
rtp_packet.SetCsrcs(csrcs);
|
||||||
|
|
||||||
|
EXPECT_EQ(rtp_packet.headers_size(), kLongRtpHeaderLength);
|
||||||
|
|
||||||
|
ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
|
||||||
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
|
ulpfec_generator_.GetFecPackets();
|
||||||
|
EXPECT_FALSE(fec_packets.empty());
|
||||||
|
|
||||||
// Ensure that the RED header is placed correctly, i.e. the correct
|
// Ensure that the RED header is placed correctly, i.e. the correct
|
||||||
// RTP header length was used in the RED packet creation.
|
// RTP header length was used in the RED packet creation.
|
||||||
const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
||||||
std::vector<std::unique_ptr<RedPacket>> red_packets =
|
for (const auto& fec_packet : fec_packets) {
|
||||||
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
fec_packet->SetSequenceNumber(seq_num++);
|
||||||
seq_num);
|
EXPECT_EQ(kFecPayloadType, fec_packet->data()[kShortRtpHeaderLength]);
|
||||||
for (const auto& red_packet : red_packets) {
|
|
||||||
EXPECT_EQ(kFecPayloadType, red_packet->data()[kShortRtpHeaderLength]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
modules/rtp_rtcp/source/video_fec_generator.h
Normal file
51
modules/rtp_rtcp/source/video_fec_generator.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_FEC_GENERATOR_H_
|
||||||
|
#define MODULES_RTP_RTCP_SOURCE_VIDEO_FEC_GENERATOR_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/units/data_rate.h"
|
||||||
|
#include "modules/include/module_fec_types.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class VideoFecGenerator {
|
||||||
|
public:
|
||||||
|
VideoFecGenerator() = default;
|
||||||
|
virtual ~VideoFecGenerator() = default;
|
||||||
|
|
||||||
|
enum class FecType { kFlexFec, kUlpFec };
|
||||||
|
virtual FecType GetFecType() const = 0;
|
||||||
|
// Returns the SSRC used for FEC packets (i.e. FlexFec SSRC).
|
||||||
|
virtual absl::optional<uint32_t> FecSsrc() = 0;
|
||||||
|
// Returns the overhead, in bytes per packet, for FEC (and possibly RED).
|
||||||
|
virtual size_t MaxPacketOverhead() const = 0;
|
||||||
|
// Current rate of FEC packets generated, including all RTP-level headers.
|
||||||
|
virtual DataRate CurrentFecRate() const = 0;
|
||||||
|
// Set FEC rates, max frames before FEC is sent, and type of FEC masks.
|
||||||
|
virtual void SetProtectionParameters(
|
||||||
|
const FecProtectionParams& delta_params,
|
||||||
|
const FecProtectionParams& key_params) = 0;
|
||||||
|
// Called on new media packet to be protected. The generator may choose
|
||||||
|
// to generate FEC packets at this time, if so they will be stored in an
|
||||||
|
// internal buffer.
|
||||||
|
virtual void AddPacketAndGenerateFec(const RtpPacketToSend& packet) = 0;
|
||||||
|
// Get (and remove) and FEC packets pending in the generator. These packets
|
||||||
|
// will lack sequence numbers, that needs to be set externally.
|
||||||
|
// TODO(bugs.webrtc.org/11340): Actually FlexFec sets seq#, fix that!
|
||||||
|
virtual std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
#endif // MODULES_RTP_RTCP_SOURCE_VIDEO_FEC_GENERATOR_H_
|
||||||
@ -149,6 +149,7 @@ webrtc_fuzzer_test("ulpfec_generator_fuzzer") {
|
|||||||
"../../modules/rtp_rtcp:rtp_rtcp_format",
|
"../../modules/rtp_rtcp:rtp_rtcp_format",
|
||||||
"../../rtc_base:checks",
|
"../../rtc_base:checks",
|
||||||
"../../rtc_base:rtc_base_approved",
|
"../../rtc_base:rtc_base_approved",
|
||||||
|
"../../system_wrappers",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
|
|||||||
FecProtectionParams params = {
|
FecProtectionParams params = {
|
||||||
data[i++], static_cast<int>(data[i++] % 100),
|
data[i++], static_cast<int>(data[i++] % 100),
|
||||||
data[i++] <= 127 ? kFecMaskRandom : kFecMaskBursty};
|
data[i++] <= 127 ? kFecMaskRandom : kFecMaskBursty};
|
||||||
sender.SetFecParameters(params);
|
sender.SetProtectionParameters(params, params);
|
||||||
uint16_t seq_num = data[i++];
|
uint16_t seq_num = data[i++];
|
||||||
|
|
||||||
while (i + 1 < size) {
|
while (i + 1 < size) {
|
||||||
@ -59,11 +59,8 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
|
|||||||
RtpPacketToSend rtp_packet(nullptr);
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
if (!rtp_packet.Parse(packet.get(), kRtpHeaderSize + payload_size))
|
if (!rtp_packet.Parse(packet.get(), kRtpHeaderSize + payload_size))
|
||||||
break;
|
break;
|
||||||
sender.AddRtpPacketAndGenerateFec(rtp_packet);
|
sender.AddPacketAndGenerateFec(rtp_packet);
|
||||||
if (sender.FecAvailable()) {
|
sender.GetFecPackets();
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
|
||||||
sender.GetFecPackets();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "modules/rtp_rtcp/source/ulpfec_generator.h"
|
#include "modules/rtp_rtcp/source/ulpfec_generator.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/copy_on_write_buffer.h"
|
#include "rtc_base/copy_on_write_buffer.h"
|
||||||
|
#include "system_wrappers/include/clock.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -25,13 +26,14 @@ constexpr uint8_t kRedPayloadType = 97;
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void FuzzOneInput(const uint8_t* data, size_t size) {
|
void FuzzOneInput(const uint8_t* data, size_t size) {
|
||||||
UlpfecGenerator generator;
|
SimulatedClock clock(1);
|
||||||
|
UlpfecGenerator generator(kRedPayloadType, kFecPayloadType, &clock);
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
if (size < 4)
|
if (size < 4)
|
||||||
return;
|
return;
|
||||||
FecProtectionParams params = {
|
FecProtectionParams params = {
|
||||||
data[i++] % 128, static_cast<int>(data[i++] % 10), kFecMaskBursty};
|
data[i++] % 128, static_cast<int>(data[i++] % 10), kFecMaskBursty};
|
||||||
generator.SetFecParameters(params);
|
generator.SetProtectionParameters(params, params);
|
||||||
uint16_t seq_num = data[i++];
|
uint16_t seq_num = data[i++];
|
||||||
uint16_t prev_seq_num = 0;
|
uint16_t prev_seq_num = 0;
|
||||||
while (i + 3 < size) {
|
while (i + 3 < size) {
|
||||||
@ -41,6 +43,9 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
|
|||||||
break;
|
break;
|
||||||
rtc::CopyOnWriteBuffer packet(&data[i], payload_size + rtp_header_length);
|
rtc::CopyOnWriteBuffer packet(&data[i], payload_size + rtp_header_length);
|
||||||
packet.EnsureCapacity(IP_PACKET_SIZE);
|
packet.EnsureCapacity(IP_PACKET_SIZE);
|
||||||
|
// Write a valid parsable header (version = 2, no padding, no extensions,
|
||||||
|
// no CSRCs).
|
||||||
|
ByteWriter<uint8_t>::WriteBigEndian(&packet[0], 2 << 6);
|
||||||
// Make sure sequence numbers are increasing.
|
// Make sure sequence numbers are increasing.
|
||||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[2], seq_num++);
|
ByteWriter<uint16_t>::WriteBigEndian(&packet[2], seq_num++);
|
||||||
i += payload_size + rtp_header_length;
|
i += payload_size + rtp_header_length;
|
||||||
@ -51,16 +56,15 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
|
|||||||
// number became out of order.
|
// number became out of order.
|
||||||
if (protect && IsNewerSequenceNumber(seq_num, prev_seq_num) &&
|
if (protect && IsNewerSequenceNumber(seq_num, prev_seq_num) &&
|
||||||
seq_num < prev_seq_num + kUlpfecMaxMediaPackets) {
|
seq_num < prev_seq_num + kUlpfecMaxMediaPackets) {
|
||||||
generator.AddRtpPacketAndGenerateFec(packet, rtp_header_length);
|
RtpPacketToSend rtp_packet(nullptr);
|
||||||
|
// Check that we actually have a parsable packet, we want to fuzz FEC
|
||||||
|
// logic, not RTP header parsing.
|
||||||
|
RTC_CHECK(rtp_packet.Parse(packet));
|
||||||
|
generator.AddPacketAndGenerateFec(rtp_packet);
|
||||||
prev_seq_num = seq_num;
|
prev_seq_num = seq_num;
|
||||||
}
|
}
|
||||||
const size_t num_fec_packets = generator.NumAvailableFecPackets();
|
|
||||||
if (num_fec_packets > 0) {
|
generator.GetFecPackets();
|
||||||
std::vector<std::unique_ptr<RedPacket>> fec_packets =
|
|
||||||
generator.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
|
||||||
100);
|
|
||||||
RTC_CHECK_EQ(num_fec_packets, fec_packets.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user