Revert "Reland "Refactors UlpFec and FlexFec to use a common interface.""
This reverts commit 49734dc0faa69616a58a1a95c7fc61a4610793cf. Reason for revert: Still something wrong with ulpfec fuzzer setup. 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} TBR=sprang@webrtc.org,stefan@webrtc.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: webrtc:11340, chromium:1052323 Change-Id: I920ce0a48a08768d7a98a563e2b66bd6eb8602b7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/169121 Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Stefan Holmer <stefan@webrtc.org> Commit-Queue: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30616}
This commit is contained in:
@ -36,13 +36,9 @@ namespace webrtc {
|
|||||||
|
|
||||||
namespace webrtc_internal_rtp_video_sender {
|
namespace webrtc_internal_rtp_video_sender {
|
||||||
|
|
||||||
RtpStreamSender::RtpStreamSender(
|
RtpStreamSender::RtpStreamSender(std::unique_ptr<RtpRtcp> rtp_rtcp,
|
||||||
std::unique_ptr<RtpRtcp> rtp_rtcp,
|
std::unique_ptr<RTPSenderVideo> sender_video)
|
||||||
std::unique_ptr<RTPSenderVideo> sender_video,
|
: rtp_rtcp(std::move(rtp_rtcp)), sender_video(std::move(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;
|
||||||
|
|
||||||
@ -117,67 +113,6 @@ 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,
|
||||||
@ -186,7 +121,7 @@ std::vector<RtpStreamSender> CreateRtpStreamSenders(
|
|||||||
Transport* send_transport,
|
Transport* send_transport,
|
||||||
RtcpBandwidthObserver* bandwidth_callback,
|
RtcpBandwidthObserver* bandwidth_callback,
|
||||||
RtpTransportControllerSendInterface* transport,
|
RtpTransportControllerSendInterface* transport,
|
||||||
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
FlexfecSender* flexfec_sender,
|
||||||
RtcEventLog* event_log,
|
RtcEventLog* event_log,
|
||||||
RateLimiter* retransmission_rate_limiter,
|
RateLimiter* retransmission_rate_limiter,
|
||||||
OverheadObserver* overhead_observer,
|
OverheadObserver* overhead_observer,
|
||||||
@ -225,17 +160,18 @@ 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::unique_ptr<VideoFecGenerator> fec_generator =
|
std::find(flexfec_protected_ssrcs.begin(),
|
||||||
MaybeCreateFecGenerator(clock, rtp_config, suspended_ssrcs, i);
|
flexfec_protected_ssrcs.end(),
|
||||||
configuration.fec_generator = fec_generator.get();
|
configuration.local_media_ssrc) !=
|
||||||
video_config.fec_generator = fec_generator.get();
|
flexfec_protected_ssrcs.end();
|
||||||
|
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];
|
||||||
@ -251,31 +187,75 @@ 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(using_flexfec, rtp_config);
|
ShouldDisableRedAndUlpfec(enable_flexfec, rtp_config);
|
||||||
if (!should_disable_red_and_ulpfec &&
|
if (rtp_config.ulpfec.red_payload_type != -1 &&
|
||||||
rtp_config.ulpfec.red_payload_type != -1) {
|
!should_disable_red_and_ulpfec) {
|
||||||
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;
|
||||||
|
}
|
||||||
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) {
|
||||||
@ -322,6 +302,8 @@ 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,
|
||||||
@ -331,7 +313,7 @@ RtpVideoSender::RtpVideoSender(
|
|||||||
send_transport,
|
send_transport,
|
||||||
transport->GetBandwidthObserver(),
|
transport->GetBandwidthObserver(),
|
||||||
transport,
|
transport,
|
||||||
suspended_ssrcs_,
|
flexfec_sender_.get(),
|
||||||
event_log,
|
event_log,
|
||||||
retransmission_limiter,
|
retransmission_limiter,
|
||||||
this,
|
this,
|
||||||
@ -393,7 +375,6 @@ 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());
|
||||||
@ -403,13 +384,10 @@ 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(fec_enabled, NackEnabled());
|
fec_controller_->SetProtectionMethod(FecEnabled(), 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.
|
||||||
@ -577,6 +555,14 @@ 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;
|
||||||
@ -671,14 +657,6 @@ 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) {
|
||||||
@ -686,6 +664,11 @@ 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,8 +51,7 @@ 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;
|
||||||
@ -61,7 +60,6 @@ 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
|
||||||
@ -156,6 +154,7 @@ 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,6 +172,8 @@ 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 = 0;
|
int fec_rate;
|
||||||
int max_fec_frames = 0;
|
int max_fec_frames;
|
||||||
FecMaskType fec_mask_type = FecMaskType::kFecMaskRandom;
|
FecMaskType fec_mask_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -210,7 +210,6 @@ 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,9 +21,7 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@ -33,7 +31,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 : public VideoFecGenerator {
|
class FlexfecSender {
|
||||||
public:
|
public:
|
||||||
FlexfecSender(int payload_type,
|
FlexfecSender(int payload_type,
|
||||||
uint32_t ssrc,
|
uint32_t ssrc,
|
||||||
@ -45,28 +43,26 @@ class FlexfecSender : public VideoFecGenerator {
|
|||||||
Clock* clock);
|
Clock* clock);
|
||||||
~FlexfecSender();
|
~FlexfecSender();
|
||||||
|
|
||||||
FecType GetFecType() const override {
|
uint32_t ssrc() const { return ssrc_; }
|
||||||
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 SetProtectionParameters(const FecProtectionParams& delta_params,
|
void SetFecParameters(const FecProtectionParams& 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().
|
||||||
void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
|
// Returns true if the media packet was successfully added.
|
||||||
|
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() override;
|
std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets();
|
||||||
|
|
||||||
// Returns the overhead, per packet, for FlexFEC.
|
// Returns the overhead, per packet, for FlexFEC.
|
||||||
size_t MaxPacketOverhead() const override;
|
size_t MaxPacketOverhead() const;
|
||||||
|
|
||||||
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();
|
||||||
@ -91,9 +87,6 @@ class FlexfecSender : public VideoFecGenerator {
|
|||||||
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
|
||||||
|
|||||||
@ -22,13 +22,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"
|
||||||
|
|
||||||
@ -92,9 +92,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;
|
||||||
|
|
||||||
// Generates FEC packets.
|
// Generate FlexFEC packets.
|
||||||
// TODO(sprang): Wire up to RtpSenderEgress.
|
// TODO(brandtr): Remove when FlexfecSender is wired up to PacedSender.
|
||||||
VideoFecGenerator* fec_generator = nullptr;
|
FlexfecSender* flexfec_sender = nullptr;
|
||||||
|
|
||||||
BitrateStatisticsObserver* send_bitrate_observer = nullptr;
|
BitrateStatisticsObserver* send_bitrate_observer = nullptr;
|
||||||
SendSideDelayObserver* send_side_delay_observer = nullptr;
|
SendSideDelayObserver* send_side_delay_observer = nullptr;
|
||||||
|
|||||||
@ -91,13 +91,11 @@ 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);
|
||||||
@ -107,30 +105,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::SetProtectionParameters(
|
void FlexfecSender::SetFecParameters(const FecProtectionParams& params) {
|
||||||
const FecProtectionParams& delta_params,
|
ulpfec_generator_.SetFecParameters(params);
|
||||||
const FecProtectionParams& key_params) {
|
|
||||||
ulpfec_generator_.SetProtectionParameters(delta_params, key_params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlexfecSender::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
|
bool FlexfecSender::AddRtpPacketAndGenerateFec(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_);
|
||||||
ulpfec_generator_.AddPacketAndGenerateFec(packet);
|
return ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
||||||
|
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);
|
||||||
@ -159,13 +157,9 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fec_packets_to_send.empty()) {
|
|
||||||
ulpfec_generator_.ResetState();
|
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() &&
|
||||||
@ -176,9 +170,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,12 +178,6 @@ 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->SetProtectionParameters(params, params);
|
sender->SetFecParameters(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,12 +63,13 @@ 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);
|
||||||
sender->AddPacketAndGenerateFec(rtp_packet);
|
EXPECT_TRUE(sender->AddRtpPacketAndGenerateFec(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());
|
||||||
}
|
}
|
||||||
@ -81,7 +82,7 @@ TEST(FlexfecSenderTest, Ssrc) {
|
|||||||
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
||||||
nullptr /* rtp_state */, &clock);
|
nullptr /* rtp_state */, &clock);
|
||||||
|
|
||||||
EXPECT_EQ(kFlexfecSsrc, sender.FecSsrc());
|
EXPECT_EQ(kFlexfecSsrc, sender.ssrc());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
|
TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
|
||||||
@ -90,7 +91,9 @@ TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
|
|||||||
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
|
||||||
nullptr /* rtp_state */, &clock);
|
nullptr /* rtp_state */, &clock);
|
||||||
|
|
||||||
EXPECT_TRUE(sender.GetFecPackets().empty());
|
EXPECT_FALSE(sender.FecAvailable());
|
||||||
|
auto fec_packets = sender.GetFecPackets();
|
||||||
|
EXPECT_EQ(0U, fec_packets.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FlexfecSenderTest, ProtectOneFrameWithOneFecPacket) {
|
TEST(FlexfecSenderTest, ProtectOneFrameWithOneFecPacket) {
|
||||||
@ -121,7 +124,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.SetProtectionParameters(params, params);
|
sender.SetFecParameters(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) {
|
||||||
@ -131,13 +134,14 @@ 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);
|
||||||
sender.AddPacketAndGenerateFec(rtp_packet);
|
EXPECT_TRUE(sender.AddRtpPacketAndGenerateFec(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());
|
||||||
@ -160,7 +164,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.SetProtectionParameters(params, params);
|
sender.SetFecParameters(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) {
|
||||||
@ -170,12 +174,13 @@ 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);
|
||||||
sender.AddPacketAndGenerateFec(rtp_packet);
|
EXPECT_TRUE(sender.AddRtpPacketAndGenerateFec(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,11 +72,8 @@ 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.fec_generator) {
|
if (config.flexfec_sender) {
|
||||||
absl::optional<uint32_t> flexfec_ssrc = config.fec_generator->FecSsrc();
|
ssrcs.insert(config.flexfec_sender->ssrc());
|
||||||
if (flexfec_ssrc) {
|
|
||||||
ssrcs.insert(*flexfec_ssrc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ssrcs;
|
return ssrcs;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,15 +98,11 @@ 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_; }
|
|
||||||
|
|
||||||
// Indicates if packet contains payload for a video key-frame.
|
bool is_first_packet_of_frame() const { return is_first_packet_of_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;
|
||||||
@ -115,7 +111,6 @@ 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,7 +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.fec_generator ? config.fec_generator->FecSsrc()
|
flexfec_ssrc_(config.flexfec_sender
|
||||||
|
? 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),
|
||||||
|
|||||||
@ -57,7 +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.fec_generator ? config.fec_generator->FecSsrc()
|
flexfec_ssrc_(config.flexfec_sender
|
||||||
|
? 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_(
|
||||||
|
|||||||
@ -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.fec_generator = &flexfec_sender_;
|
config.flexfec_sender = &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.fec_generator = &flexfec_sender_;
|
config.flexfec_sender = &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.fec_generator = &flexfec_sender;
|
video_config.flexfec_sender = &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.fec_generator = &flexfec_sender;
|
config.flexfec_sender = &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.fec_generator = &flexfec_sender;
|
video_config.flexfec_sender = &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.fec_generator = &flexfec_sender;
|
config.flexfec_sender = &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.fec_generator = &flexfec_sender;
|
video_config.flexfec_sender = &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,14 +1777,12 @@ 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.fec_generator = &ulpfec_generator;
|
video_config.ulpfec_payload_type = kUlpfecPayloadType;
|
||||||
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(
|
||||||
@ -2120,7 +2118,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.fec_generator = &flexfec_sender_;
|
config.flexfec_sender = &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_;
|
||||||
|
|||||||
@ -259,7 +259,11 @@ 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),
|
||||||
fec_generator_(config.fec_generator),
|
ulpfec_payload_type_(config.ulpfec_payload_type),
|
||||||
|
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),
|
||||||
@ -275,6 +279,83 @@ RTPSenderVideo::RTPSenderVideo(const Config& config)
|
|||||||
|
|
||||||
RTPSenderVideo::~RTPSenderVideo() {}
|
RTPSenderVideo::~RTPSenderVideo() {}
|
||||||
|
|
||||||
|
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) {
|
||||||
@ -293,9 +374,16 @@ 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) {
|
||||||
if (*packet->packet_type() == RtpPacketMediaType::kVideo) {
|
switch (*packet->packet_type()) {
|
||||||
|
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.
|
||||||
@ -310,31 +398,39 @@ void RTPSenderVideo::LogAndSendToNetwork(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t RTPSenderVideo::FecPacketOverhead() const {
|
size_t RTPSenderVideo::FecPacketOverhead() const {
|
||||||
size_t overhead = fec_generator_ ? fec_generator_->MaxPacketOverhead() : 0u;
|
if (flexfec_enabled())
|
||||||
|
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;
|
||||||
|
}
|
||||||
// TODO(bugs.webrtc.org/11340): Move this into UlpfecGenerator.
|
if (ulpfec_enabled()) {
|
||||||
if (fec_generator_ &&
|
|
||||||
fec_generator_->GetFecType() == VideoFecGenerator::FecType::kUlpFec) {
|
|
||||||
// For ULPFEC, the overhead is the FEC headers plus RED for FEC header
|
// For ULPFEC, the overhead is the FEC headers plus RED for FEC header
|
||||||
// (see above) plus anything in RTP header beyond the 12 bytes base header
|
// (see above) plus anything in RTP header beyond the 12 bytes base header
|
||||||
// (CSRC list, extensions...)
|
// (CSRC list, extensions...)
|
||||||
// This reason for the header extensions to be included here is that
|
// This reason for the header extensions to be included here is that
|
||||||
// from an FEC viewpoint, they are part of the payload to be protected.
|
// from an FEC viewpoint, they are part of the payload to be protected.
|
||||||
// (The base RTP header is already protected by the FEC header.)
|
// (The base RTP header is already protected by the FEC header.)
|
||||||
overhead += rtp_sender_->RtpHeaderLength() - kRtpHeaderSize;
|
overhead += ulpfec_generator_.MaxPacketOverhead() +
|
||||||
}
|
(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) {
|
||||||
if (fec_generator_) {
|
rtc::CritScope cs(&crit_);
|
||||||
fec_generator_->SetProtectionParameters(delta_params, key_params);
|
delta_fec_params_ = delta_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(
|
||||||
@ -445,6 +541,19 @@ 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() -
|
||||||
@ -636,40 +745,21 @@ 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()) {
|
||||||
std::unique_ptr<RtpPacketToSend> red_packet(new RtpPacketToSend(*packet));
|
AppendAsRedMaybeWithUlpfec(std::move(packet), protect_packet,
|
||||||
BuildRedPayload(*packet, red_packet.get());
|
&rtp_packets);
|
||||||
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) {
|
||||||
@ -684,22 +774,6 @@ 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",
|
||||||
@ -713,7 +787,8 @@ uint32_t RTPSenderVideo::VideoBitrateSent() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RTPSenderVideo::FecOverheadRate() const {
|
uint32_t RTPSenderVideo::FecOverheadRate() const {
|
||||||
return fec_generator_ ? fec_generator_->CurrentFecRate().bps<uint32_t>() : 0u;
|
rtc::CritScope cs(&stats_crit_);
|
||||||
|
return fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
|
uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
|
||||||
|
|||||||
@ -22,12 +22,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_video_header.h"
|
#include "modules/rtp_rtcp/source/rtp_video_header.h"
|
||||||
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
#include "modules/rtp_rtcp/source/ulpfec_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"
|
||||||
@ -67,11 +68,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,9 +99,13 @@ 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;
|
||||||
|
|
||||||
@ -129,12 +134,27 @@ 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_);
|
||||||
@ -165,10 +185,22 @@ 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_;
|
||||||
VideoFecGenerator* const fec_generator_;
|
const absl::optional<int> ulpfec_payload_type_;
|
||||||
|
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_);
|
||||||
|
|||||||
@ -130,7 +130,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.fec_generator = flexfec_sender;
|
config.flexfec_sender = flexfec_sender;
|
||||||
config.field_trials = &field_trials;
|
config.field_trials = &field_trials;
|
||||||
return config;
|
return config;
|
||||||
}()) {}
|
}()) {}
|
||||||
|
|||||||
@ -22,7 +22,6 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@ -63,119 +62,128 @@ constexpr uint32_t kUnknownSsrc = 0;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
UlpfecGenerator::Params::Params() = default;
|
RedPacket::RedPacket(size_t length)
|
||||||
UlpfecGenerator::Params::Params(FecProtectionParams delta_params,
|
: data_(new uint8_t[length]), length_(length), header_length_(0) {}
|
||||||
FecProtectionParams keyframe_params)
|
|
||||||
: delta_params(delta_params), keyframe_params(keyframe_params) {}
|
|
||||||
|
|
||||||
UlpfecGenerator::UlpfecGenerator(int red_payload_type,
|
RedPacket::~RedPacket() = default;
|
||||||
int ulpfec_payload_type,
|
|
||||||
Clock* clock)
|
|
||||||
: red_payload_type_(red_payload_type),
|
|
||||||
ulpfec_payload_type_(ulpfec_payload_type),
|
|
||||||
clock_(clock),
|
|
||||||
fec_(ForwardErrorCorrection::CreateUlpfec(kUnknownSsrc)),
|
|
||||||
num_protected_frames_(0),
|
|
||||||
min_num_media_packets_(1),
|
|
||||||
keyframe_in_process_(false),
|
|
||||||
fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {}
|
|
||||||
|
|
||||||
// Used by FlexFecSender, payload types are unused.
|
void RedPacket::CreateHeader(const uint8_t* rtp_header,
|
||||||
UlpfecGenerator::UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec,
|
size_t header_length,
|
||||||
Clock* clock)
|
int red_payload_type,
|
||||||
: red_payload_type_(0),
|
int payload_type) {
|
||||||
ulpfec_payload_type_(0),
|
RTC_DCHECK_LE(header_length + kRedForFecHeaderLength, length_);
|
||||||
clock_(clock),
|
memcpy(data_.get(), rtp_header, header_length);
|
||||||
fec_(std::move(fec)),
|
// 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) {
|
||||||
keyframe_in_process_(false),
|
memset(¶ms_, 0, sizeof(params_));
|
||||||
fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {}
|
memset(&new_params_, 0, sizeof(new_params_));
|
||||||
|
}
|
||||||
|
|
||||||
UlpfecGenerator::~UlpfecGenerator() = default;
|
UlpfecGenerator::~UlpfecGenerator() = default;
|
||||||
|
|
||||||
void UlpfecGenerator::SetProtectionParameters(
|
void UlpfecGenerator::SetFecParameters(const FecProtectionParams& params) {
|
||||||
const FecProtectionParams& delta_params,
|
RTC_DCHECK_GE(params.fec_rate, 0);
|
||||||
const FecProtectionParams& key_params) {
|
RTC_DCHECK_LE(params.fec_rate, 255);
|
||||||
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.
|
||||||
rtc::CritScope cs(&crit_);
|
new_params_ = params;
|
||||||
pending_params_.emplace(delta_params, key_params);
|
if (params.fec_rate > kHighProtectionThreshold) {
|
||||||
}
|
|
||||||
|
|
||||||
void UlpfecGenerator::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
|
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
|
||||||
RTC_DCHECK(generated_fec_packets_.empty());
|
|
||||||
|
|
||||||
if (media_packets_.empty()) {
|
|
||||||
rtc::CritScope cs(&crit_);
|
|
||||||
if (pending_params_) {
|
|
||||||
current_params_ = *pending_params_;
|
|
||||||
pending_params_.reset();
|
|
||||||
|
|
||||||
if (CurrentParams().fec_rate > kHighProtectionThreshold) {
|
|
||||||
min_num_media_packets_ = kMinMediaPackets;
|
min_num_media_packets_ = kMinMediaPackets;
|
||||||
} else {
|
} else {
|
||||||
min_num_media_packets_ = 1;
|
min_num_media_packets_ = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyframe_in_process_ = packet.is_key_frame();
|
int UlpfecGenerator::AddRtpPacketAndGenerateFec(
|
||||||
|
const rtc::CopyOnWriteBuffer& data_buffer,
|
||||||
|
size_t rtp_header_length) {
|
||||||
|
RTC_DCHECK(generated_fec_packets_.empty());
|
||||||
|
if (media_packets_.empty()) {
|
||||||
|
params_ = new_params_;
|
||||||
}
|
}
|
||||||
RTC_DCHECK_EQ(packet.is_key_frame(), keyframe_in_process_);
|
|
||||||
|
|
||||||
bool complete_frame = false;
|
bool complete_frame = false;
|
||||||
const bool marker_bit = packet.Marker();
|
const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
|
||||||
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.
|
||||||
auto fec_packet = std::make_unique<ForwardErrorCorrection::Packet>();
|
std::unique_ptr<ForwardErrorCorrection::Packet> packet(
|
||||||
fec_packet->data = packet.Buffer();
|
new ForwardErrorCorrection::Packet());
|
||||||
media_packets_.push_back(std::move(fec_packet));
|
RTC_DCHECK_GE(data_buffer.size(), rtp_header_length);
|
||||||
|
packet->data = data_buffer;
|
||||||
// Keep a copy of the last RTP packet, so we can copy the RTP header
|
media_packets_.push_back(std::move(packet));
|
||||||
// from it when creating newly generated ULPFEC+RED packets.
|
// Keep track of the RTP header length, so we can copy the RTP header
|
||||||
RTC_DCHECK_GE(packet.headers_size(), kRtpHeaderSize);
|
// from |packet| to newly generated ULPFEC+RED packets.
|
||||||
last_media_packet_ = packet;
|
RTC_DCHECK_GE(rtp_header_length, kRtpHeaderSize);
|
||||||
|
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;
|
||||||
fec_->EncodeFec(media_packets_, params.fec_rate, kNumImportantPackets,
|
int ret = fec_->EncodeFec(media_packets_, params_.fec_rate,
|
||||||
kUseUnequalProtection, params.fec_mask_type,
|
kNumImportantPackets, kUseUnequalProtection,
|
||||||
&generated_fec_packets_);
|
params_.fec_mask_type, &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 {
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
|
||||||
|
|
||||||
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());
|
||||||
@ -188,79 +196,61 @@ bool UlpfecGenerator::MinimumMediaPacketsReached() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FecProtectionParams& UlpfecGenerator::CurrentParams() const {
|
bool UlpfecGenerator::FecAvailable() const {
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
return !generated_fec_packets_.empty();
|
||||||
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<RtpPacketToSend>> UlpfecGenerator::GetFecPackets() {
|
std::vector<std::unique_ptr<RedPacket>> UlpfecGenerator::GetUlpfecPacketsAsRed(
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
int red_payload_type,
|
||||||
if (generated_fec_packets_.empty()) {
|
int ulpfec_payload_type,
|
||||||
return std::vector<std::unique_ptr<RtpPacketToSend>>();
|
uint16_t first_seq_num) {
|
||||||
}
|
std::vector<std::unique_ptr<RedPacket>> red_packets;
|
||||||
|
red_packets.reserve(generated_fec_packets_.size());
|
||||||
|
RTC_DCHECK(!media_packets_.empty());
|
||||||
|
ForwardErrorCorrection::Packet* last_media_packet =
|
||||||
|
media_packets_.back().get();
|
||||||
|
uint16_t seq_num = first_seq_num;
|
||||||
|
for (const auto* fec_packet : generated_fec_packets_) {
|
||||||
// Wrap FEC packet (including FEC headers) in a RED packet. Since the
|
// Wrap FEC packet (including FEC headers) in a RED packet. Since the
|
||||||
// FEC packets in |generated_fec_packets_| don't have RTP headers, we
|
// FEC packets in |generated_fec_packets_| don't have RTP headers, we
|
||||||
// reuse the header from the last media packet.
|
// reuse the header from the last media packet.
|
||||||
RTC_CHECK(last_media_packet_.has_value());
|
RTC_DCHECK_GT(last_media_packet_rtp_header_length_, 0);
|
||||||
last_media_packet_->SetPayloadSize(0);
|
std::unique_ptr<RedPacket> red_packet(
|
||||||
|
new RedPacket(last_media_packet_rtp_header_length_ +
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets;
|
kRedForFecHeaderLength + fec_packet->data.size()));
|
||||||
fec_packets.reserve(generated_fec_packets_.size());
|
red_packet->CreateHeader(last_media_packet->data.data(),
|
||||||
|
last_media_packet_rtp_header_length_,
|
||||||
size_t total_fec_size_bytes = 0;
|
red_payload_type, ulpfec_payload_type);
|
||||||
for (const auto* fec_packet : generated_fec_packets_) {
|
red_packet->SetSeqNum(seq_num++);
|
||||||
std::unique_ptr<RtpPacketToSend> red_packet =
|
red_packet->ClearMarkerBit();
|
||||||
std::make_unique<RtpPacketToSend>(*last_media_packet_);
|
red_packet->AssignPayload(fec_packet->data.data(), fec_packet->data.size());
|
||||||
red_packet->SetPayloadType(red_payload_type_);
|
red_packets.push_back(std::move(red_packet));
|
||||||
red_packet->SetMarker(false);
|
|
||||||
uint8_t* payload_buffer = red_packet->SetPayloadSize(
|
|
||||||
kRedForFecHeaderLength + fec_packet->data.size());
|
|
||||||
// Primary RED header with F bit unset.
|
|
||||||
// See https://tools.ietf.org/html/rfc2198#section-3
|
|
||||||
payload_buffer[0] = ulpfec_payload_type_; // RED header.
|
|
||||||
memcpy(&payload_buffer[1], fec_packet->data.data(),
|
|
||||||
fec_packet->data.size());
|
|
||||||
total_fec_size_bytes += red_packet->size();
|
|
||||||
red_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
|
|
||||||
red_packet->set_allow_retransmission(false);
|
|
||||||
fec_packets.push_back(std::move(red_packet));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetState();
|
ResetState();
|
||||||
|
|
||||||
rtc::CritScope cs(&crit_);
|
return red_packets;
|
||||||
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(), CurrentParams().fec_rate);
|
fec_->NumFecPackets(media_packets_.size(), params_.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_.reset();
|
last_media_packet_rtp_header_length_ = 0;
|
||||||
generated_fec_packets_.clear();
|
generated_fec_packets_.clear();
|
||||||
num_protected_frames_ = 0;
|
num_protected_frames_ = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,54 +20,63 @@
|
|||||||
|
|
||||||
#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 UlpfecGenerator : public VideoFecGenerator {
|
class RedPacket {
|
||||||
|
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(int red_payload_type, int ulpfec_payload_type, Clock* clock);
|
UlpfecGenerator();
|
||||||
~UlpfecGenerator();
|
~UlpfecGenerator();
|
||||||
|
|
||||||
FecType GetFecType() const override {
|
void SetFecParameters(const FecProtectionParams& params);
|
||||||
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().
|
||||||
void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
|
int AddRtpPacketAndGenerateFec(const rtc::CopyOnWriteBuffer& data_buffer,
|
||||||
|
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 override;
|
size_t MaxPacketOverhead() const;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() override;
|
// Returns generated FEC packets with RED headers added.
|
||||||
|
std::vector<std::unique_ptr<RedPacket>> GetUlpfecPacketsAsRed(
|
||||||
// Current rate of FEC packets generated, including all RTP-level headers.
|
int red_payload_type,
|
||||||
DataRate CurrentFecRate() const override;
|
int ulpfec_payload_type,
|
||||||
|
uint16_t first_seq_num);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Params {
|
explicit UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec);
|
||||||
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
|
||||||
@ -88,31 +97,16 @@ class UlpfecGenerator : public VideoFecGenerator {
|
|||||||
// (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();
|
||||||
|
|
||||||
const int red_payload_type_;
|
std::unique_ptr<ForwardErrorCorrection> fec_;
|
||||||
const int ulpfec_payload_type_;
|
ForwardErrorCorrection::PacketList media_packets_;
|
||||||
Clock* const clock_;
|
size_t last_media_packet_rtp_header_length_;
|
||||||
|
std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_;
|
||||||
rtc::RaceChecker race_checker_;
|
int num_protected_frames_;
|
||||||
const std::unique_ptr<ForwardErrorCorrection> fec_
|
int min_num_media_packets_;
|
||||||
RTC_GUARDED_BY(race_checker_);
|
FecProtectionParams params_;
|
||||||
ForwardErrorCorrection::PacketList media_packets_
|
FecProtectionParams new_params_;
|
||||||
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,8 +35,11 @@ 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,
|
||||||
bool marker_bit,
|
RedPacket* packet,
|
||||||
const rtc::CopyOnWriteBuffer& data) {
|
bool marker_bit) {
|
||||||
|
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);
|
||||||
@ -49,12 +52,8 @@ void VerifyHeader(uint16_t seq_num,
|
|||||||
|
|
||||||
class UlpfecGeneratorTest : public ::testing::Test {
|
class UlpfecGeneratorTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
UlpfecGeneratorTest()
|
UlpfecGeneratorTest() : packet_generator_(kMediaSsrc) {}
|
||||||
: 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_;
|
||||||
};
|
};
|
||||||
@ -82,22 +81,24 @@ 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_.SetProtectionParameters(params, params);
|
ulpfec_generator_.SetFecParameters(params);
|
||||||
|
uint8_t packet[28] = {0};
|
||||||
for (Packet p : protected_packets) {
|
for (Packet p : protected_packets) {
|
||||||
RtpPacketToSend packet(nullptr);
|
if (p.marker_bit) {
|
||||||
packet.SetMarker(p.marker_bit);
|
packet[1] |= 0x80;
|
||||||
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 {
|
||||||
EXPECT_FALSE(fec_packets.empty());
|
packet[1] &= ~0x80;
|
||||||
|
}
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,28 +113,24 @@ 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);
|
||||||
// Expecting one FEC packet.
|
ulpfec_generator_.SetFecParameters(params); // 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);
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(packet->data,
|
||||||
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
kRtpHeaderSize));
|
||||||
ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
|
|
||||||
last_timestamp = packet->header.timestamp;
|
last_timestamp = packet->header.timestamp;
|
||||||
}
|
}
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
EXPECT_TRUE(ulpfec_generator_.FecAvailable());
|
||||||
ulpfec_generator_.GetFecPackets();
|
const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
||||||
EXPECT_EQ(fec_packets.size(), 1u);
|
std::vector<std::unique_ptr<RedPacket>> red_packets =
|
||||||
uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
||||||
fec_packets[0]->SetSequenceNumber(seq_num);
|
seq_num);
|
||||||
EXPECT_TRUE(ulpfec_generator_.GetFecPackets().empty());
|
EXPECT_FALSE(ulpfec_generator_.FecAvailable());
|
||||||
|
ASSERT_EQ(1u, red_packets.size());
|
||||||
EXPECT_EQ(fec_packets[0]->headers_size(), kRtpHeaderSize);
|
VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType,
|
||||||
|
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) {
|
||||||
@ -148,27 +145,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};
|
||||||
// Expecting one FEC packet.
|
ulpfec_generator_.SetFecParameters(params); // 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);
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
||||||
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
packet->data, kRtpHeaderSize));
|
||||||
ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
|
|
||||||
last_timestamp = packet->header.timestamp;
|
last_timestamp = packet->header.timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
EXPECT_TRUE(ulpfec_generator_.FecAvailable());
|
||||||
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();
|
||||||
fec_packets[0]->SetSequenceNumber(seq_num);
|
std::vector<std::unique_ptr<RedPacket>> red_packets =
|
||||||
VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType, false,
|
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
||||||
fec_packets[0]->Buffer());
|
seq_num);
|
||||||
|
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) {
|
||||||
@ -177,43 +174,34 @@ 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_.SetProtectionParameters(params, params);
|
ulpfec_generator_.SetFecParameters(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);
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
||||||
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
packet->data, kShortRtpHeaderLength));
|
||||||
EXPECT_EQ(rtp_packet.headers_size(), kShortRtpHeaderLength);
|
EXPECT_FALSE(ulpfec_generator_.FecAvailable());
|
||||||
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);
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
|
||||||
EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
|
packet->data, kLongRtpHeaderLength));
|
||||||
EXPECT_TRUE(rtp_packet.SetPayloadSize(0) != nullptr);
|
EXPECT_TRUE(ulpfec_generator_.FecAvailable());
|
||||||
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.
|
||||||
uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
|
||||||
for (const auto& fec_packet : fec_packets) {
|
std::vector<std::unique_ptr<RedPacket>> red_packets =
|
||||||
fec_packet->SetSequenceNumber(seq_num++);
|
ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
|
||||||
EXPECT_EQ(kFecPayloadType, fec_packet->data()[kShortRtpHeaderLength]);
|
seq_num);
|
||||||
|
for (const auto& red_packet : red_packets) {
|
||||||
|
EXPECT_EQ(kFecPayloadType, red_packet->data()[kShortRtpHeaderLength]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_
|
|
||||||
@ -148,7 +148,6 @@ 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.SetProtectionParameters(params, params);
|
sender.SetFecParameters(params);
|
||||||
uint16_t seq_num = data[i++];
|
uint16_t seq_num = data[i++];
|
||||||
|
|
||||||
while (i + 1 < size) {
|
while (i + 1 < size) {
|
||||||
@ -59,9 +59,12 @@ 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.AddPacketAndGenerateFec(rtp_packet);
|
sender.AddRtpPacketAndGenerateFec(rtp_packet);
|
||||||
|
if (sender.FecAvailable()) {
|
||||||
|
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
|
||||||
sender.GetFecPackets();
|
sender.GetFecPackets();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@ -26,14 +25,13 @@ 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) {
|
||||||
SimulatedClock clock(1);
|
UlpfecGenerator generator;
|
||||||
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.SetProtectionParameters(params, params);
|
generator.SetFecParameters(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) {
|
||||||
@ -53,13 +51,16 @@ 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) {
|
||||||
RtpPacketToSend rtp_packet(nullptr);
|
generator.AddRtpPacketAndGenerateFec(packet, rtp_header_length);
|
||||||
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();
|
||||||
generator.GetFecPackets();
|
if (num_fec_packets > 0) {
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user