Removes RPLR based FEC controller.

This is not used and adds a lot of maintenance overhead to
the code since it requires that the transport feedback adapter
communicates directly with audio send stream.

This also means that the packet loss tracker used as input for
this can be removed and a lot of wiring up code overall.

Bug: webrtc:9883
Change-Id: I25689fb622ed89cbb378c27212a159485f5f53be
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/156502
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Reviewed-by: Minyue Li <minyue@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29667}
This commit is contained in:
Sebastian Jansson 2019-10-31 13:53:53 +01:00 committed by Commit Bot
parent d1ea4c93d3
commit cd2a92f8e0
30 changed files with 9 additions and 2284 deletions

View file

@ -82,7 +82,9 @@ void AudioEncoder::OnReceivedUplinkPacketLossFraction(
float uplink_packet_loss_fraction) {} float uplink_packet_loss_fraction) {}
void AudioEncoder::OnReceivedUplinkRecoverablePacketLossFraction( void AudioEncoder::OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) {} float uplink_recoverable_packet_loss_fraction) {
RTC_NOTREACHED();
}
void AudioEncoder::OnReceivedTargetAudioBitrate(int target_audio_bitrate_bps) { void AudioEncoder::OnReceivedTargetAudioBitrate(int target_audio_bitrate_bps) {
OnReceivedUplinkBandwidth(target_audio_bitrate_bps, absl::nullopt); OnReceivedUplinkBandwidth(target_audio_bitrate_bps, absl::nullopt);

View file

@ -210,10 +210,7 @@ class AudioEncoder {
virtual void OnReceivedUplinkPacketLossFraction( virtual void OnReceivedUplinkPacketLossFraction(
float uplink_packet_loss_fraction); float uplink_packet_loss_fraction);
// Provides 1st-order-FEC-recoverable uplink packet loss rate to this encoder RTC_DEPRECATED virtual void OnReceivedUplinkRecoverablePacketLossFraction(
// to allow it to adapt.
// |uplink_recoverable_packet_loss_fraction| is in the range [0.0, 1.0].
virtual void OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction); float uplink_recoverable_packet_loss_fraction);
// Provides target audio bitrate to this encoder to allow it to adapt. // Provides target audio bitrate to this encoder to allow it to adapt.

View file

@ -33,8 +33,6 @@ rtc_library("audio") {
"null_audio_poller.h", "null_audio_poller.h",
"remix_resample.cc", "remix_resample.cc",
"remix_resample.h", "remix_resample.h",
"transport_feedback_packet_loss_tracker.cc",
"transport_feedback_packet_loss_tracker.h",
] ]
deps = [ deps = [
@ -123,7 +121,6 @@ if (rtc_include_tests) {
"remix_resample_unittest.cc", "remix_resample_unittest.cc",
"test/audio_stats_test.cc", "test/audio_stats_test.cc",
"test/media_transport_test.cc", "test/media_transport_test.cc",
"transport_feedback_packet_loss_tracker_unittest.cc",
] ]
deps = [ deps = [
":audio", ":audio",

View file

@ -42,10 +42,6 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
// TODO(eladalon): Subsequent CL will make these values experiment-dependent.
constexpr size_t kPacketLossTrackerMaxWindowSizeMs = 15000;
constexpr size_t kPacketLossRateMinNumAckedPackets = 50;
constexpr size_t kRecoverablePacketLossRateMinNumAckedPairs = 40;
void UpdateEventLogStreamConfig(RtcEventLog* event_log, void UpdateEventLogStreamConfig(RtcEventLog* event_log,
const AudioSendStream::Config& config, const AudioSendStream::Config& config,
@ -162,9 +158,6 @@ AudioSendStream::AudioSendStream(
!field_trial::IsDisabled("WebRTC-Audio-LegacyOverhead")), !field_trial::IsDisabled("WebRTC-Audio-LegacyOverhead")),
bitrate_allocator_(bitrate_allocator), bitrate_allocator_(bitrate_allocator),
rtp_transport_(rtp_transport), rtp_transport_(rtp_transport),
packet_loss_tracker_(kPacketLossTrackerMaxWindowSizeMs,
kPacketLossRateMinNumAckedPackets,
kRecoverablePacketLossRateMinNumAckedPairs),
rtp_rtcp_module_(nullptr), rtp_rtcp_module_(nullptr),
suspended_rtp_state_(suspended_rtp_state) { suspended_rtp_state_(suspended_rtp_state) {
RTC_LOG(LS_INFO) << "AudioSendStream: " << config.rtp.ssrc; RTC_LOG(LS_INFO) << "AudioSendStream: " << config.rtp.ssrc;
@ -195,15 +188,12 @@ AudioSendStream::AudioSendStream(
ConfigureStream(config, true); ConfigureStream(config, true);
pacer_thread_checker_.Detach(); pacer_thread_checker_.Detach();
// Signal congestion controller this object is ready for OnPacket* callbacks.
rtp_transport_->RegisterPacketFeedbackObserver(this);
} }
AudioSendStream::~AudioSendStream() { AudioSendStream::~AudioSendStream() {
RTC_DCHECK(worker_thread_checker_.IsCurrent()); RTC_DCHECK(worker_thread_checker_.IsCurrent());
RTC_LOG(LS_INFO) << "~AudioSendStream: " << config_.rtp.ssrc; RTC_LOG(LS_INFO) << "~AudioSendStream: " << config_.rtp.ssrc;
RTC_DCHECK(!sending_); RTC_DCHECK(!sending_);
rtp_transport_->DeRegisterPacketFeedbackObserver(this);
channel_send_->ResetSenderCongestionControlObjects(); channel_send_->ResetSenderCongestionControlObjects();
// Blocking call to synchronize state with worker queue to ensure that there // Blocking call to synchronize state with worker queue to ensure that there
// are no pending tasks left that keeps references to audio. // are no pending tasks left that keeps references to audio.
@ -258,8 +248,6 @@ void AudioSendStream::ConfigureStream(
const auto& old_config = config_; const auto& old_config = config_;
config_cs_.Enter();
// Configuration parameters which cannot be changed. // Configuration parameters which cannot be changed.
RTC_DCHECK(first_time || RTC_DCHECK(first_time ||
old_config.send_transport == new_config.send_transport); old_config.send_transport == new_config.send_transport);
@ -284,8 +272,6 @@ void AudioSendStream::ConfigureStream(
const ExtensionIds old_ids = FindExtensionIds(old_config.rtp.extensions); const ExtensionIds old_ids = FindExtensionIds(old_config.rtp.extensions);
const ExtensionIds new_ids = FindExtensionIds(new_config.rtp.extensions); const ExtensionIds new_ids = FindExtensionIds(new_config.rtp.extensions);
config_cs_.Leave();
// Audio level indication // Audio level indication
if (first_time || new_ids.audio_level != old_ids.audio_level) { if (first_time || new_ids.audio_level != old_ids.audio_level) {
channel_send_->SetSendAudioLevelIndicationStatus(new_ids.audio_level != 0, channel_send_->SetSendAudioLevelIndicationStatus(new_ids.audio_level != 0,
@ -328,7 +314,6 @@ void AudioSendStream::ConfigureStream(
channel_send_->RegisterSenderCongestionControlObjects(rtp_transport_, channel_send_->RegisterSenderCongestionControlObjects(rtp_transport_,
bandwidth_observer); bandwidth_observer);
} }
config_cs_.Enter();
// MID RTP header extension. // MID RTP header extension.
if ((first_time || new_ids.mid != old_ids.mid || if ((first_time || new_ids.mid != old_ids.mid ||
new_config.rtp.mid != old_config.rtp.mid) && new_config.rtp.mid != old_config.rtp.mid) &&
@ -352,7 +337,6 @@ void AudioSendStream::ConfigureStream(
ReconfigureBitrateObserver(new_config); ReconfigureBitrateObserver(new_config);
} }
config_ = new_config; config_ = new_config;
config_cs_.Leave();
} }
void AudioSendStream::Start() { void AudioSendStream::Start() {
@ -513,45 +497,6 @@ uint32_t AudioSendStream::OnBitrateUpdated(BitrateAllocationUpdate update) {
return 0; return 0;
} }
void AudioSendStream::OnPacketAdded(uint32_t ssrc, uint16_t seq_num) {
RTC_DCHECK(pacer_thread_checker_.IsCurrent());
// Only packets that belong to this stream are of interest.
bool same_ssrc;
{
rtc::CritScope lock(&config_cs_);
same_ssrc = ssrc == config_.rtp.ssrc;
}
if (same_ssrc) {
rtc::CritScope lock(&packet_loss_tracker_cs_);
// TODO(eladalon): This function call could potentially reset the window,
// setting both PLR and RPLR to unknown. Consider (during upcoming
// refactoring) passing an indication of such an event.
packet_loss_tracker_.OnPacketAdded(seq_num, clock_->TimeInMilliseconds());
}
}
void AudioSendStream::OnPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector) {
RTC_DCHECK(worker_thread_checker_.IsCurrent());
absl::optional<float> plr;
absl::optional<float> rplr;
{
rtc::CritScope lock(&packet_loss_tracker_cs_);
packet_loss_tracker_.OnPacketFeedbackVector(packet_feedback_vector);
plr = packet_loss_tracker_.GetPacketLossRate();
rplr = packet_loss_tracker_.GetRecoverablePacketLossRate();
}
// TODO(eladalon): If R/PLR go back to unknown, no indication is given that
// the previously sent value is no longer relevant. This will be taken care
// of with some refactoring which is now being done.
if (plr) {
channel_send_->OnTwccBasedUplinkPacketLossRate(*plr);
}
if (rplr) {
channel_send_->OnRecoverableUplinkPacketLossRate(*rplr);
}
}
void AudioSendStream::SetTransportOverhead( void AudioSendStream::SetTransportOverhead(
int transport_overhead_per_packet_bytes) { int transport_overhead_per_packet_bytes) {
RTC_DCHECK(worker_thread_checker_.IsCurrent()); RTC_DCHECK(worker_thread_checker_.IsCurrent());

View file

@ -17,7 +17,6 @@
#include "audio/audio_level.h" #include "audio/audio_level.h"
#include "audio/channel_send.h" #include "audio/channel_send.h"
#include "audio/transport_feedback_packet_loss_tracker.h"
#include "call/audio_send_stream.h" #include "call/audio_send_stream.h"
#include "call/audio_state.h" #include "call/audio_state.h"
#include "call/bitrate_allocator.h" #include "call/bitrate_allocator.h"
@ -54,7 +53,6 @@ class AudioState;
class AudioSendStream final : public webrtc::AudioSendStream, class AudioSendStream final : public webrtc::AudioSendStream,
public webrtc::BitrateAllocatorObserver, public webrtc::BitrateAllocatorObserver,
public webrtc::PacketFeedbackObserver,
public webrtc::OverheadObserver { public webrtc::OverheadObserver {
public: public:
AudioSendStream(Clock* clock, AudioSendStream(Clock* clock,
@ -100,11 +98,6 @@ class AudioSendStream final : public webrtc::AudioSendStream,
// Implements BitrateAllocatorObserver. // Implements BitrateAllocatorObserver.
uint32_t OnBitrateUpdated(BitrateAllocationUpdate update) override; uint32_t OnBitrateUpdated(BitrateAllocationUpdate update) override;
// From PacketFeedbackObserver.
void OnPacketAdded(uint32_t ssrc, uint16_t seq_num) override;
void OnPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector) override;
void SetTransportOverhead(int transport_overhead_per_packet_bytes); void SetTransportOverhead(int transport_overhead_per_packet_bytes);
// OverheadObserver override reports audio packetization overhead from // OverheadObserver override reports audio packetization overhead from
@ -170,7 +163,6 @@ class AudioSendStream final : public webrtc::AudioSendStream,
const bool send_side_bwe_with_overhead_; const bool send_side_bwe_with_overhead_;
const AudioAllocationConfig allocation_settings_; const AudioAllocationConfig allocation_settings_;
rtc::CriticalSection config_cs_;
webrtc::AudioSendStream::Config config_; webrtc::AudioSendStream::Config config_;
rtc::scoped_refptr<webrtc::AudioState> audio_state_; rtc::scoped_refptr<webrtc::AudioState> audio_state_;
const std::unique_ptr<voe::ChannelSendInterface> channel_send_; const std::unique_ptr<voe::ChannelSendInterface> channel_send_;
@ -189,10 +181,6 @@ class AudioSendStream final : public webrtc::AudioSendStream,
RTC_GUARDED_BY(worker_queue_); RTC_GUARDED_BY(worker_queue_);
RtpTransportControllerSendInterface* const rtp_transport_; RtpTransportControllerSendInterface* const rtp_transport_;
rtc::CriticalSection packet_loss_tracker_cs_;
TransportFeedbackPacketLossTracker packet_loss_tracker_
RTC_GUARDED_BY(&packet_loss_tracker_cs_);
RtpRtcp* rtp_rtcp_module_; RtpRtcp* rtp_rtcp_module_;
absl::optional<RtpState> const suspended_rtp_state_; absl::optional<RtpState> const suspended_rtp_state_;

View file

@ -700,29 +700,6 @@ TEST(AudioSendStreamTest, DontRecreateEncoder) {
send_stream->Reconfigure(helper.config()); send_stream->Reconfigure(helper.config());
} }
// Allow to check for race conditions under tsan.
// This mimicks the situation where 'ModuleProcessThread' (pacer thread) is
// launched by webrtc::RtpTransportControllerSend::RtpTransportControllerSend().
TEST(AudioSendStreamTest, RaceFree) {
ConfigHelper helper(false, false);
// Sanity checks: copy-pasted from DontRecreateEncoder test.
EXPECT_CALL(*helper.channel_send(), SetEncoderForMock(_, _))
.WillOnce(Return());
EXPECT_CALL(*helper.channel_send(), RegisterCngPayloadType(105, 8000));
helper.config().send_codec_spec =
AudioSendStream::Config::SendCodecSpec(9, kG722Format);
helper.config().send_codec_spec->cng_payload_type = 105;
auto send_stream = helper.CreateAudioSendStream();
std::thread pacer([&]() {
send_stream->OnPacketAdded(/*ssrc*/ 0xcafe,
/*seq_num*/ 0xf00d);
});
send_stream->Reconfigure(helper.config());
pacer.join();
}
TEST(AudioSendStreamTest, ReconfigureTransportCcResetsFirst) { TEST(AudioSendStreamTest, ReconfigureTransportCcResetsFirst) {
ScopedFieldTrials field_trials("WebRTC-Audio-SendSideBwe/Enabled/"); ScopedFieldTrials field_trials("WebRTC-Audio-SendSideBwe/Enabled/");
ConfigHelper helper(false, true); ConfigHelper helper(false, true);

View file

@ -163,15 +163,6 @@ class ChannelSend : public ChannelSendInterface,
// packet. // packet.
void ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame) override; void ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame) override;
// The existence of this function alongside OnUplinkPacketLossRate is
// a compromise. We want the encoder to be agnostic of the PLR source, but
// we also don't want it to receive conflicting information from TWCC and
// from RTCP-XR.
void OnTwccBasedUplinkPacketLossRate(float packet_loss_rate) override;
void OnRecoverableUplinkPacketLossRate(
float recoverable_packet_loss_rate) override;
int64_t GetRTT() const override; int64_t GetRTT() const override;
// E2EE Custom Audio Frame Encryption // E2EE Custom Audio Frame Encryption
@ -257,7 +248,6 @@ class ChannelSend : public ChannelSendInterface,
rtc::ThreadChecker construction_thread_; rtc::ThreadChecker construction_thread_;
const bool use_twcc_plr_for_ana_;
bool encoder_queue_is_active_ RTC_GUARDED_BY(encoder_queue_) = false; bool encoder_queue_is_active_ RTC_GUARDED_BY(encoder_queue_) = false;
@ -601,8 +591,6 @@ ChannelSend::ChannelSend(Clock* clock,
rtp_packet_pacer_proxy_(new RtpPacketSenderProxy()), rtp_packet_pacer_proxy_(new RtpPacketSenderProxy()),
retransmission_rate_limiter_( retransmission_rate_limiter_(
new RateLimiter(clock, kMaxRetransmissionWindowMs)), new RateLimiter(clock, kMaxRetransmissionWindowMs)),
use_twcc_plr_for_ana_(
webrtc::field_trial::FindFullName("UseTwccPlrForAna") == "Enabled"),
media_transport_config_(media_transport_config), media_transport_config_(media_transport_config),
frame_encryptor_(frame_encryptor), frame_encryptor_(frame_encryptor),
crypto_options_(crypto_options), crypto_options_(crypto_options),
@ -790,27 +778,7 @@ int ChannelSend::GetBitrate() const {
return configured_bitrate_bps_; return configured_bitrate_bps_;
} }
void ChannelSend::OnTwccBasedUplinkPacketLossRate(float packet_loss_rate) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!use_twcc_plr_for_ana_)
return;
CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedUplinkPacketLossFraction(packet_loss_rate);
});
}
void ChannelSend::OnRecoverableUplinkPacketLossRate(
float recoverable_packet_loss_rate) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedUplinkRecoverablePacketLossFraction(
recoverable_packet_loss_rate);
});
}
void ChannelSend::OnUplinkPacketLossRate(float packet_loss_rate) { void ChannelSend::OnUplinkPacketLossRate(float packet_loss_rate) {
if (use_twcc_plr_for_ana_)
return;
CallEncoder([&](AudioEncoder* encoder) { CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedUplinkPacketLossFraction(packet_loss_rate); encoder->OnReceivedUplinkPacketLossFraction(packet_loss_rate);
}); });

View file

@ -106,9 +106,6 @@ class ChannelSendInterface {
std::unique_ptr<AudioFrame> audio_frame) = 0; std::unique_ptr<AudioFrame> audio_frame) = 0;
virtual RtpRtcp* GetRtpRtcp() const = 0; virtual RtpRtcp* GetRtpRtcp() const = 0;
virtual void OnTwccBasedUplinkPacketLossRate(float packet_loss_rate) = 0;
virtual void OnRecoverableUplinkPacketLossRate(
float recoverable_packet_loss_rate) = 0;
// In RTP we currently rely on RTCP packets (|ReceivedRTCPPacket|) to inform // In RTP we currently rely on RTCP packets (|ReceivedRTCPPacket|) to inform
// about RTT. // about RTT.
// In media transport we rely on the TargetTransferRateObserver instead. // In media transport we rely on the TargetTransferRateObserver instead.

View file

@ -1,368 +0,0 @@
/*
* Copyright (c) 2017 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.
*/
#include "audio/transport_feedback_packet_loss_tracker.h"
#include <iterator>
#include <limits>
#include <utility>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/mod_ops.h"
namespace {
constexpr uint16_t kSeqNumHalf = 0x8000u;
void UpdateCounter(size_t* counter, bool increment) {
if (increment) {
RTC_DCHECK_LT(*counter, std::numeric_limits<std::size_t>::max());
++(*counter);
} else {
RTC_DCHECK_GT(*counter, 0);
--(*counter);
}
}
} // namespace
namespace webrtc {
TransportFeedbackPacketLossTracker::TransportFeedbackPacketLossTracker(
int64_t max_window_size_ms,
size_t plr_min_num_acked_packets,
size_t rplr_min_num_acked_pairs)
: max_window_size_ms_(max_window_size_ms),
ref_packet_status_(packet_status_window_.begin()),
plr_state_(plr_min_num_acked_packets),
rplr_state_(rplr_min_num_acked_pairs) {
RTC_DCHECK_GT(max_window_size_ms, 0);
RTC_DCHECK_GT(plr_min_num_acked_packets, 0);
RTC_DCHECK_GT(rplr_min_num_acked_pairs, 0);
Reset();
}
TransportFeedbackPacketLossTracker::~TransportFeedbackPacketLossTracker() =
default;
void TransportFeedbackPacketLossTracker::Reset() {
acked_packets_ = 0;
plr_state_.Reset();
rplr_state_.Reset();
packet_status_window_.clear();
ref_packet_status_ = packet_status_window_.begin();
}
uint16_t TransportFeedbackPacketLossTracker::ReferenceSequenceNumber() const {
RTC_DCHECK(!packet_status_window_.empty());
return ref_packet_status_->first;
}
uint16_t TransportFeedbackPacketLossTracker::NewestSequenceNumber() const {
RTC_DCHECK(!packet_status_window_.empty());
return PreviousPacketStatus(packet_status_window_.end())->first;
}
void TransportFeedbackPacketLossTracker::OnPacketAdded(uint16_t seq_num,
int64_t send_time_ms) {
// Sanity - time can't flow backwards.
RTC_DCHECK(
packet_status_window_.empty() ||
PreviousPacketStatus(packet_status_window_.end())->second.send_time_ms <=
send_time_ms);
if (packet_status_window_.find(seq_num) != packet_status_window_.end() ||
(!packet_status_window_.empty() &&
ForwardDiff(seq_num, NewestSequenceNumber()) <= kSeqNumHalf)) {
// The only way for these two to happen is when the stream lies dormant for
// long enough for the sequence numbers to wrap. Everything in the window in
// such a case would be too old to use.
Reset();
}
// Maintain a window where the newest sequence number is at most 0x7fff away
// from the oldest, so that would could still distinguish old/new.
while (!packet_status_window_.empty() &&
ForwardDiff(ref_packet_status_->first, seq_num) >= kSeqNumHalf) {
RemoveOldestPacketStatus();
}
SentPacket sent_packet(send_time_ms, PacketStatus::Unacked);
packet_status_window_.insert(packet_status_window_.end(),
std::make_pair(seq_num, sent_packet));
if (packet_status_window_.size() == 1) {
ref_packet_status_ = packet_status_window_.cbegin();
}
}
void TransportFeedbackPacketLossTracker::OnPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedback_vector) {
for (const PacketFeedback& packet : packet_feedback_vector) {
const auto& it = packet_status_window_.find(packet.sequence_number);
// Packets which aren't at least marked as unacked either do not belong to
// this media stream, or have been shifted out of window.
if (it == packet_status_window_.end())
continue;
const bool lost = packet.arrival_time_ms == PacketFeedback::kNotReceived;
const PacketStatus packet_status =
lost ? PacketStatus::Lost : PacketStatus::Received;
UpdatePacketStatus(it, packet_status);
}
}
absl::optional<float> TransportFeedbackPacketLossTracker::GetPacketLossRate()
const {
return plr_state_.GetMetric();
}
absl::optional<float>
TransportFeedbackPacketLossTracker::GetRecoverablePacketLossRate() const {
return rplr_state_.GetMetric();
}
void TransportFeedbackPacketLossTracker::UpdatePacketStatus(
SentPacketStatusMap::iterator it,
PacketStatus new_status) {
if (it->second.status != PacketStatus::Unacked) {
// Normally, packets are sent (inserted into window as "unacked"), then we
// receive one feedback for them.
// But it is possible that a packet would receive two feedbacks. Then:
if (it->second.status == PacketStatus::Lost &&
new_status == PacketStatus::Received) {
// If older status said that the packet was lost but newer one says it
// is received, we take the newer one.
UpdateMetrics(it, false);
it->second.status =
PacketStatus::Unacked; // For clarity; overwritten shortly.
} else {
// If the value is unchanged or if older status said that the packet was
// received but the newer one says it is lost, we ignore it.
// The standard allows for previously-reported packets to carry
// no report when the reports overlap, which also looks like the
// packet is being reported as lost.
return;
}
}
// Change from UNACKED to RECEIVED/LOST.
it->second.status = new_status;
UpdateMetrics(it, true);
// Remove packets from the beginning of the window until we only hold packets,
// be they acked or unacked, which are not more than |max_window_size_ms|
// older from the newest packet. (If the packet we're now inserting into the
// window isn't the newest, it would not trigger any removals; the newest
// already removed all relevant.)
while (ref_packet_status_ != packet_status_window_.end() &&
(it->second.send_time_ms - ref_packet_status_->second.send_time_ms) >
max_window_size_ms_) {
RemoveOldestPacketStatus();
}
}
void TransportFeedbackPacketLossTracker::RemoveOldestPacketStatus() {
UpdateMetrics(ref_packet_status_, false);
const auto it = ref_packet_status_;
ref_packet_status_ = NextPacketStatus(it);
packet_status_window_.erase(it);
}
void TransportFeedbackPacketLossTracker::UpdateMetrics(
ConstPacketStatusIterator it,
bool apply /* false = undo */) {
RTC_DCHECK(it != packet_status_window_.end());
// Metrics are dependent on feedbacks from the other side. We don't want
// to update the metrics each time a packet is sent, except for the case
// when it shifts old sent-but-unacked-packets out of window.
RTC_DCHECK(!apply || it->second.status != PacketStatus::Unacked);
if (it->second.status != PacketStatus::Unacked) {
UpdateCounter(&acked_packets_, apply);
}
UpdatePlr(it, apply);
UpdateRplr(it, apply);
}
void TransportFeedbackPacketLossTracker::UpdatePlr(
ConstPacketStatusIterator it,
bool apply /* false = undo */) {
switch (it->second.status) {
case PacketStatus::Unacked:
return;
case PacketStatus::Received:
UpdateCounter(&plr_state_.num_received_packets_, apply);
break;
case PacketStatus::Lost:
UpdateCounter(&plr_state_.num_lost_packets_, apply);
break;
default:
RTC_NOTREACHED();
}
}
void TransportFeedbackPacketLossTracker::UpdateRplr(
ConstPacketStatusIterator it,
bool apply /* false = undo */) {
if (it->second.status == PacketStatus::Unacked) {
// Unacked packets cannot compose a pair.
return;
}
// Previous packet and current packet might compose a pair.
if (it != ref_packet_status_) {
const auto& prev = PreviousPacketStatus(it);
if (prev->second.status != PacketStatus::Unacked) {
UpdateCounter(&rplr_state_.num_acked_pairs_, apply);
if (prev->second.status == PacketStatus::Lost &&
it->second.status == PacketStatus::Received) {
UpdateCounter(&rplr_state_.num_recoverable_losses_, apply);
}
}
}
// Current packet and next packet might compose a pair.
const auto& next = NextPacketStatus(it);
if (next != packet_status_window_.end() &&
next->second.status != PacketStatus::Unacked) {
UpdateCounter(&rplr_state_.num_acked_pairs_, apply);
if (it->second.status == PacketStatus::Lost &&
next->second.status == PacketStatus::Received) {
UpdateCounter(&rplr_state_.num_recoverable_losses_, apply);
}
}
}
TransportFeedbackPacketLossTracker::ConstPacketStatusIterator
TransportFeedbackPacketLossTracker::PreviousPacketStatus(
ConstPacketStatusIterator it) const {
RTC_DCHECK(it != ref_packet_status_);
if (it == packet_status_window_.end()) {
// This is to make PreviousPacketStatus(packet_status_window_.end()) point
// to the last element.
it = ref_packet_status_;
}
if (it == packet_status_window_.begin()) {
// Due to the circular nature of sequence numbers, we let the iterator
// go to the end.
it = packet_status_window_.end();
}
return --it;
}
TransportFeedbackPacketLossTracker::ConstPacketStatusIterator
TransportFeedbackPacketLossTracker::NextPacketStatus(
ConstPacketStatusIterator it) const {
RTC_DCHECK(it != packet_status_window_.end());
++it;
if (it == packet_status_window_.end()) {
// Due to the circular nature of sequence numbers, we let the iterator
// goes back to the beginning.
it = packet_status_window_.begin();
}
if (it == ref_packet_status_) {
// This is to make the NextPacketStatus of the last element to return the
// beyond-the-end iterator.
it = packet_status_window_.end();
}
return it;
}
// TODO(minyue): This method checks the states of this class do not misbehave.
// The method is used both in unit tests and a fuzzer test. The fuzzer test
// is present to help finding potential errors. Once the fuzzer test shows no
// error after long period, we can remove the fuzzer test, and move this method
// to unit test.
void TransportFeedbackPacketLossTracker::Validate() const { // Testing only!
RTC_CHECK_EQ(plr_state_.num_received_packets_ + plr_state_.num_lost_packets_,
acked_packets_);
RTC_CHECK_LE(acked_packets_, packet_status_window_.size());
RTC_CHECK_LE(rplr_state_.num_recoverable_losses_,
rplr_state_.num_acked_pairs_);
RTC_CHECK_LE(rplr_state_.num_acked_pairs_, acked_packets_ - 1);
size_t unacked_packets = 0;
size_t received_packets = 0;
size_t lost_packets = 0;
size_t acked_pairs = 0;
size_t recoverable_losses = 0;
if (!packet_status_window_.empty()) {
ConstPacketStatusIterator it = ref_packet_status_;
do {
switch (it->second.status) {
case PacketStatus::Unacked:
++unacked_packets;
break;
case PacketStatus::Received:
++received_packets;
break;
case PacketStatus::Lost:
++lost_packets;
break;
default:
RTC_NOTREACHED();
}
auto next = std::next(it);
if (next == packet_status_window_.end())
next = packet_status_window_.begin();
if (next != ref_packet_status_) { // If we have a next packet...
RTC_CHECK_GE(next->second.send_time_ms, it->second.send_time_ms);
if (it->second.status != PacketStatus::Unacked &&
next->second.status != PacketStatus::Unacked) {
++acked_pairs;
if (it->second.status == PacketStatus::Lost &&
next->second.status == PacketStatus::Received) {
++recoverable_losses;
}
}
}
RTC_CHECK_LT(ForwardDiff(ReferenceSequenceNumber(), it->first),
kSeqNumHalf);
it = next;
} while (it != ref_packet_status_);
}
RTC_CHECK_EQ(plr_state_.num_received_packets_, received_packets);
RTC_CHECK_EQ(plr_state_.num_lost_packets_, lost_packets);
RTC_CHECK_EQ(packet_status_window_.size(),
unacked_packets + received_packets + lost_packets);
RTC_CHECK_EQ(rplr_state_.num_acked_pairs_, acked_pairs);
RTC_CHECK_EQ(rplr_state_.num_recoverable_losses_, recoverable_losses);
}
absl::optional<float> TransportFeedbackPacketLossTracker::PlrState::GetMetric()
const {
const size_t total = num_lost_packets_ + num_received_packets_;
if (total < min_num_acked_packets_) {
return absl::nullopt;
} else {
return static_cast<float>(num_lost_packets_) / total;
}
}
absl::optional<float> TransportFeedbackPacketLossTracker::RplrState::GetMetric()
const {
if (num_acked_pairs_ < min_num_acked_pairs_) {
return absl::nullopt;
} else {
return static_cast<float>(num_recoverable_losses_) / num_acked_pairs_;
}
}
} // namespace webrtc

View file

@ -1,142 +0,0 @@
/*
* Copyright (c) 2017 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 AUDIO_TRANSPORT_FEEDBACK_PACKET_LOSS_TRACKER_H_
#define AUDIO_TRANSPORT_FEEDBACK_PACKET_LOSS_TRACKER_H_
#include <map>
#include <vector>
#include "absl/types/optional.h"
namespace webrtc {
namespace rtcp {
class TransportFeedback;
}
struct PacketFeedback;
class TransportFeedbackPacketLossTracker final {
public:
// * We count up to |max_window_size_ms| from the sent
// time of the latest acked packet for the calculation of the metrics.
// * PLR (packet-loss-rate) is reliably computable once the statuses of
// |plr_min_num_acked_packets| packets are known.
// * RPLR (recoverable-packet-loss-rate) is reliably computable once the
// statuses of |rplr_min_num_acked_pairs| pairs are known.
TransportFeedbackPacketLossTracker(int64_t max_window_size_ms,
size_t plr_min_num_acked_packets,
size_t rplr_min_num_acked_pairs);
~TransportFeedbackPacketLossTracker();
void OnPacketAdded(uint16_t seq_num, int64_t send_time_ms);
void OnPacketFeedbackVector(
const std::vector<PacketFeedback>& packet_feedbacks_vector);
// Returns the packet loss rate, if the window has enough packet statuses to
// reliably compute it. Otherwise, returns empty.
absl::optional<float> GetPacketLossRate() const;
// Returns the first-order-FEC recoverable packet loss rate, if the window has
// enough status pairs to reliably compute it. Otherwise, returns empty.
absl::optional<float> GetRecoverablePacketLossRate() const;
// Verifies that the internal states are correct. Only used for tests.
void Validate() const;
private:
// When a packet is sent, we memorize its association with the stream by
// marking it as (sent-but-so-far-) unacked. If we ever receive a feedback
// that reports it as received/lost, we update the state and
// metrics accordingly.
enum class PacketStatus { Unacked = 0, Received = 1, Lost = 2 };
struct SentPacket {
SentPacket(int64_t send_time_ms, PacketStatus status)
: send_time_ms(send_time_ms), status(status) {}
int64_t send_time_ms;
PacketStatus status;
};
typedef std::map<uint16_t, SentPacket> SentPacketStatusMap;
typedef SentPacketStatusMap::const_iterator ConstPacketStatusIterator;
void Reset();
// ReferenceSequenceNumber() provides a sequence number that defines the
// order of packet reception info stored in |packet_status_window_|. In
// particular, given any sequence number |x|,
// (2^16 + x - ref_seq_num_) % 2^16 defines its actual position in
// |packet_status_window_|.
uint16_t ReferenceSequenceNumber() const;
uint16_t NewestSequenceNumber() const;
void UpdatePacketStatus(SentPacketStatusMap::iterator it,
PacketStatus new_status);
void RemoveOldestPacketStatus();
void UpdateMetrics(ConstPacketStatusIterator it,
bool apply /* false = undo */);
void UpdatePlr(ConstPacketStatusIterator it, bool apply /* false = undo */);
void UpdateRplr(ConstPacketStatusIterator it, bool apply /* false = undo */);
ConstPacketStatusIterator PreviousPacketStatus(
ConstPacketStatusIterator it) const;
ConstPacketStatusIterator NextPacketStatus(
ConstPacketStatusIterator it) const;
const int64_t max_window_size_ms_;
size_t acked_packets_;
SentPacketStatusMap packet_status_window_;
// |ref_packet_status_| points to the oldest item in |packet_status_window_|.
ConstPacketStatusIterator ref_packet_status_;
// Packet-loss-rate calculation (lost / all-known-packets).
struct PlrState {
explicit PlrState(size_t min_num_acked_packets)
: min_num_acked_packets_(min_num_acked_packets) {
Reset();
}
void Reset() {
num_received_packets_ = 0;
num_lost_packets_ = 0;
}
absl::optional<float> GetMetric() const;
const size_t min_num_acked_packets_;
size_t num_received_packets_;
size_t num_lost_packets_;
} plr_state_;
// Recoverable packet loss calculation (first-order-FEC recoverable).
struct RplrState {
explicit RplrState(size_t min_num_acked_pairs)
: min_num_acked_pairs_(min_num_acked_pairs) {
Reset();
}
void Reset() {
num_acked_pairs_ = 0;
num_recoverable_losses_ = 0;
}
absl::optional<float> GetMetric() const;
// Recoverable packets are those which were lost, but immediately followed
// by a properly received packet. If that second packet carried FEC,
// the data from the former (lost) packet could be recovered.
// The RPLR is calculated as the fraction of such pairs (lost-received) out
// of all pairs of consecutive acked packets.
const size_t min_num_acked_pairs_;
size_t num_acked_pairs_;
size_t num_recoverable_losses_;
} rplr_state_;
};
} // namespace webrtc
#endif // AUDIO_TRANSPORT_FEEDBACK_PACKET_LOSS_TRACKER_H_

View file

@ -1,569 +0,0 @@
/*
* Copyright (c) 2017 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.
*/
#include "audio/transport_feedback_packet_loss_tracker.h"
#include <limits>
#include <memory>
#include <numeric>
#include <vector>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "rtc_base/checks.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
constexpr int64_t kDefaultSendIntervalMs = 10;
constexpr int64_t kDefaultMaxWindowSizeMs = 500 * kDefaultSendIntervalMs;
class TransportFeedbackPacketLossTrackerTest
: public ::testing::TestWithParam<uint16_t> {
public:
TransportFeedbackPacketLossTrackerTest() = default;
virtual ~TransportFeedbackPacketLossTrackerTest() = default;
protected:
void SendPackets(TransportFeedbackPacketLossTracker* tracker,
const std::vector<uint16_t>& sequence_numbers,
int64_t send_time_interval_ms,
bool validate_all = true) {
RTC_CHECK_GE(send_time_interval_ms, 0);
for (uint16_t sequence_number : sequence_numbers) {
tracker->OnPacketAdded(sequence_number, time_ms_);
if (validate_all) {
tracker->Validate();
}
time_ms_ += send_time_interval_ms;
}
// We've either validated after each packet, or, for making sure the UT
// doesn't run too long, we might validate only at the end of the range.
if (!validate_all) {
tracker->Validate();
}
}
void SendPackets(TransportFeedbackPacketLossTracker* tracker,
uint16_t first_seq_num,
size_t num_of_packets,
int64_t send_time_interval_ms,
bool validate_all = true) {
RTC_CHECK_GE(send_time_interval_ms, 0);
std::vector<uint16_t> sequence_numbers(num_of_packets);
std::iota(sequence_numbers.begin(), sequence_numbers.end(), first_seq_num);
SendPackets(tracker, sequence_numbers, send_time_interval_ms, validate_all);
}
void AdvanceClock(int64_t time_delta_ms) {
RTC_CHECK_GT(time_delta_ms, 0);
time_ms_ += time_delta_ms;
}
void AddTransportFeedbackAndValidate(
TransportFeedbackPacketLossTracker* tracker,
uint16_t base_sequence_num,
const std::vector<bool>& reception_status_vec) {
// Any positive integer signals reception. kNotReceived signals loss.
// Other values are just illegal.
constexpr int64_t kArrivalTimeMs = 1234;
std::vector<PacketFeedback> packet_feedback_vector;
uint16_t seq_num = base_sequence_num;
for (bool received : reception_status_vec) {
packet_feedback_vector.emplace_back(PacketFeedback(
received ? kArrivalTimeMs : PacketFeedback::kNotReceived, seq_num));
++seq_num;
}
tracker->OnPacketFeedbackVector(packet_feedback_vector);
tracker->Validate();
}
// Checks that validty is as expected. If valid, checks also that
// value is as expected.
void ValidatePacketLossStatistics(
const TransportFeedbackPacketLossTracker& tracker,
absl::optional<float> expected_plr,
absl::optional<float> expected_rplr) {
// TODO(eladalon): Comparing the absl::optional<float> directly would have
// given concise code, but less readable error messages. If we modify
// the way absl::optional is printed, we can get rid of this.
absl::optional<float> plr = tracker.GetPacketLossRate();
EXPECT_EQ(static_cast<bool>(expected_plr), static_cast<bool>(plr));
if (expected_plr && plr) {
EXPECT_EQ(*expected_plr, *plr);
}
absl::optional<float> rplr = tracker.GetRecoverablePacketLossRate();
EXPECT_EQ(static_cast<bool>(expected_rplr), static_cast<bool>(rplr));
if (expected_rplr && rplr) {
EXPECT_EQ(*expected_rplr, *rplr);
}
}
uint16_t base_{GetParam()};
private:
int64_t time_ms_{0};
};
} // namespace
// Sanity check on an empty window.
TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindow) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
// PLR and RPLR reported as unknown before reception of first feedback.
ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
}
// A feedback received for an empty window has no effect.
TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindowFeedback) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
// Feedback doesn't correspond to any packets - ignored.
AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
// After the packets are transmitted, acking them would have an effect.
SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
}
// Sanity check on partially filled window.
TEST_P(TransportFeedbackPacketLossTrackerTest, PartiallyFilledWindow) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
// PLR unknown before minimum window size reached.
// RPLR unknown before minimum pairs reached.
// Expected window contents: [] -> [1001].
SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_, {true, false, false, true});
ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
}
// Sanity check on minimum filled window - PLR known, RPLR unknown.
TEST_P(TransportFeedbackPacketLossTrackerTest, PlrMinimumFilledWindow) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
// PLR correctly calculated after minimum window size reached.
// RPLR not necessarily known at that time (not if min-pairs not reached).
// Expected window contents: [] -> [10011].
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, absl::nullopt);
}
// Sanity check on minimum filled window - PLR unknown, RPLR known.
TEST_P(TransportFeedbackPacketLossTrackerTest, RplrMinimumFilledWindow) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 6, 4);
// RPLR correctly calculated after minimum pairs reached.
// PLR not necessarily known at that time (not if min window not reached).
// Expected window contents: [] -> [10011].
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, absl::nullopt, 1.0f / 4.0f);
}
// If packets are sent close enough together that the clock reading for both
// is the same, that's handled properly.
TEST_P(TransportFeedbackPacketLossTrackerTest, SameSentTime) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
// Expected window contents: [] -> [101].
SendPackets(&tracker, base_, 3, 0); // Note: time interval = 0ms.
AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
}
// Additional reports update PLR and RPLR.
TEST_P(TransportFeedbackPacketLossTrackerTest, ExtendWindow) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
SendPackets(&tracker, base_, 25, kDefaultSendIntervalMs);
// Expected window contents: [] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, absl::nullopt);
// Expected window contents: [10011] -> [1001110101].
AddTransportFeedbackAndValidate(&tracker, base_ + 5,
{true, false, true, false, true});
ValidatePacketLossStatistics(tracker, 4.0f / 10.0f, 3.0f / 9.0f);
// Expected window contents: [1001110101] -> [1001110101-GAP-10001].
AddTransportFeedbackAndValidate(&tracker, base_ + 20,
{true, false, false, false, true});
ValidatePacketLossStatistics(tracker, 7.0f / 15.0f, 4.0f / 13.0f);
}
// Correct calculation with different packet lengths.
TEST_P(TransportFeedbackPacketLossTrackerTest, DifferentSentIntervals) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
int64_t frames[] = {20, 60, 120, 20, 60};
for (size_t i = 0; i < sizeof(frames) / sizeof(frames[0]); i++) {
SendPackets(&tracker, {static_cast<uint16_t>(base_ + i)}, frames[i]);
}
// Expected window contents: [] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
}
// The window retains information up to sent times that exceed the the max
// window size. The oldest packets get shifted out of window to make room
// for the newer ones.
TEST_P(TransportFeedbackPacketLossTrackerTest, MaxWindowSize) {
TransportFeedbackPacketLossTracker tracker(4 * kDefaultSendIntervalMs, 5, 1);
SendPackets(&tracker, base_, 6, kDefaultSendIntervalMs, true);
// Up to the maximum time-span retained (first + 4 * kDefaultSendIntervalMs).
// Expected window contents: [] -> [01001].
AddTransportFeedbackAndValidate(&tracker, base_,
{false, true, false, false, true});
ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
// After the maximum time-span, older entries are discarded to accommodate
// newer ones.
// Expected window contents: [01001] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_ + 5, {true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
}
// All packets received.
TEST_P(TransportFeedbackPacketLossTrackerTest, AllReceived) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
// Expected window contents: [] -> [11111].
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_,
{true, true, true, true, true});
ValidatePacketLossStatistics(tracker, 0.0f, 0.0f);
}
// All packets lost.
TEST_P(TransportFeedbackPacketLossTrackerTest, AllLost) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
// Note: The last packet in the feedback does not belong to the stream.
// It's only there because we're not allowed to end a feedback with a loss.
// Expected window contents: [] -> [00000].
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_,
{false, false, false, false, false, true});
ValidatePacketLossStatistics(tracker, 1.0f, 0.0f);
}
// Repeated reports are ignored.
TEST_P(TransportFeedbackPacketLossTrackerTest, ReportRepetition) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
// Expected window contents: [] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
// Repeat entire previous feedback
// Expected window contents: [10011] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
}
// Report overlap.
TEST_P(TransportFeedbackPacketLossTrackerTest, ReportOverlap) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 1);
SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
// Expected window contents: [] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
// Expected window contents: [10011] -> [1001101].
AddTransportFeedbackAndValidate(&tracker, base_ + 3,
{true, true, false, true});
ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 2.0f / 6.0f);
}
// Report conflict.
TEST_P(TransportFeedbackPacketLossTrackerTest, ReportConflict) {
TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
SendPackets(&tracker, base_, 15, 10);
// Expected window contents: [] -> [01001].
AddTransportFeedbackAndValidate(&tracker, base_,
{false, true, false, false, true});
ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
// Expected window contents: [01001] -> [11101].
// While false->true will be applied, true -> false will be ignored.
AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
}
// Skipped packets treated as unknown (not lost).
TEST_P(TransportFeedbackPacketLossTrackerTest, SkippedPackets) {
TransportFeedbackPacketLossTracker tracker(200 * kDefaultSendIntervalMs, 5,
1);
SendPackets(&tracker, base_, 200, kDefaultSendIntervalMs);
// Expected window contents: [] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
// Expected window contents: [10011] -> [10011-GAP-101].
AddTransportFeedbackAndValidate(&tracker, base_ + 100, {true, false, true});
ValidatePacketLossStatistics(tracker, 3.0f / 8.0f, 2.0f / 6.0f);
}
// Moving a window, if it excludes some old acked messages, can leave
// in-window unacked messages intact, and ready to be used later.
TEST_P(TransportFeedbackPacketLossTrackerTest, MovedWindowRetainsRelevantInfo) {
constexpr int64_t max_window_size_ms = 100;
TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 5, 1);
// Note: All messages in this test are sent 1ms apart from each other.
// Therefore, the delta in sequence numbers equals the timestamps delta.
SendPackets(&tracker, base_, 4 * max_window_size_ms, 1);
// Expected window contents: [] -> [10101].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, true, false, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
// Expected window contents: [10101] -> [100011].
const int64_t moved_oldest_acked = base_ + 2 * max_window_size_ms;
const std::vector<bool> feedback = {true, false, false, false, true, true};
AddTransportFeedbackAndValidate(&tracker, moved_oldest_acked, feedback);
ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
// Having acked |feedback.size()| starting with |moved_oldest_acked|, the
// newest of the acked ones is now:
const int64_t moved_newest_acked = moved_oldest_acked + feedback.size() - 1;
// Messages that *are* more than the span-limit away from the newest
// acked message *are* too old. Acking them would have no effect.
AddTransportFeedbackAndValidate(
&tracker, moved_newest_acked - max_window_size_ms - 1, {true});
ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
// Messages that are *not* more than the span-limit away from the newest
// acked message are *not* too old. Acking them would have an effect.
AddTransportFeedbackAndValidate(
&tracker, moved_newest_acked - max_window_size_ms, {true});
ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 1.0f / 5.0f);
}
// Inserting feedback into the middle of a window works correctly - can
// complete two pairs.
TEST_P(TransportFeedbackPacketLossTrackerTest, InsertionCompletesTwoPairs) {
TransportFeedbackPacketLossTracker tracker(150 * kDefaultSendIntervalMs, 5,
1);
SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
// Expected window contents: [] -> [10111].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, true, true, true});
ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
// Expected window contents: [10111] -> [10111-GAP-10101].
AddTransportFeedbackAndValidate(&tracker, base_ + 7,
{true, false, true, false, true});
ValidatePacketLossStatistics(tracker, 3.0f / 10.0f, 3.0f / 8.0f);
// Insert in between, closing the gap completely.
// Expected window contents: [10111-GAP-10101] -> [101110110101].
AddTransportFeedbackAndValidate(&tracker, base_ + 5, {false, true});
ValidatePacketLossStatistics(tracker, 4.0f / 12.0f, 4.0f / 11.0f);
}
// Sequence number gaps are not gaps in reception. However, gaps in reception
// are still possible, if a packet which WAS sent on the stream is not acked.
TEST_P(TransportFeedbackPacketLossTrackerTest, SanityGapsInSequenceNumbers) {
TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 5, 1);
SendPackets(
&tracker,
{static_cast<uint16_t>(base_), static_cast<uint16_t>(base_ + 2),
static_cast<uint16_t>(base_ + 4), static_cast<uint16_t>(base_ + 6),
static_cast<uint16_t>(base_ + 8)},
kDefaultSendIntervalMs);
// Gaps in sequence numbers not considered as gaps in window, because only
// those sequence numbers which were associated with the stream count.
// Expected window contents: [] -> [11011].
AddTransportFeedbackAndValidate(
// Note: Left packets belong to this stream, right ones ignored.
&tracker, base_,
{true, false, true, false, false, false, true, false, true, true});
ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
// Create gap by sending [base + 10] but not acking it.
// Note: Acks for [base + 11] and [base + 13] ignored (other stream).
// Expected window contents: [11011] -> [11011-GAP-01].
SendPackets(
&tracker,
{static_cast<uint16_t>(base_ + 10), static_cast<uint16_t>(base_ + 12),
static_cast<uint16_t>(base_ + 14)},
kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_ + 11,
{false, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 7.0f, 2.0f / 5.0f);
}
// The window cannot span more than 0x8000 in sequence numbers, regardless
// of time stamps and ack/unacked status.
TEST_P(TransportFeedbackPacketLossTrackerTest, MaxUnackedPackets) {
TransportFeedbackPacketLossTracker tracker(0x10000, 4, 1);
SendPackets(&tracker, base_, 0x2000, 1, false);
// Expected window contents: [] -> [10011].
AddTransportFeedbackAndValidate(&tracker, base_,
{true, false, false, true, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
// Sending more unacked packets, up to 0x7fff from the base, does not
// move the window or discard any information.
SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000 - 0x2000), 0x2000,
1, false);
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
// Sending more unacked packets, up to 0x7fff from the base, does not
// move the window or discard any information.
// Expected window contents: [10011] -> [0011].
SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000), 1, 1);
ValidatePacketLossStatistics(tracker, 2.0f / 4.0f, 1.0f / 3.0f);
}
// The window holds acked packets up until the difference in timestamps between
// the oldest and newest reaches the configured maximum. Once this maximum
// is exceeded, old packets are shifted out of window until the maximum is
// once again observed.
TEST_P(TransportFeedbackPacketLossTrackerTest, TimeDifferenceMaximumObserved) {
constexpr int64_t max_window_size_ms = 500;
TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 3, 1);
// Note: All messages in this test are sent 1ms apart from each other.
// Therefore, the delta in sequence numbers equals the timestamps delta.
// Baseline - window has acked messages.
// Expected window contents: [] -> [01101].
const std::vector<bool> feedback = {false, true, true, false, true};
SendPackets(&tracker, base_, feedback.size(), 1);
AddTransportFeedbackAndValidate(&tracker, base_, feedback);
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
// Test - window base not moved.
// Expected window contents: [01101] -> [011011].
AdvanceClock(max_window_size_ms - feedback.size());
SendPackets(&tracker, static_cast<uint16_t>(base_ + feedback.size()), 1, 1);
AddTransportFeedbackAndValidate(
&tracker, static_cast<uint16_t>(base_ + feedback.size()), {true});
ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
// Another packet, sent 1ms later, would already be too late. The window will
// be moved, but only after the ACK is received.
const uint16_t new_packet_seq_num =
static_cast<uint16_t>(base_ + feedback.size() + 1);
SendPackets(&tracker, {new_packet_seq_num}, 1);
ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
// Expected window contents: [011011] -> [110111].
AddTransportFeedbackAndValidate(&tracker, new_packet_seq_num, {true});
ValidatePacketLossStatistics(tracker, 1.0f / 6.0f, 1.0f / 5.0f);
}
TEST_P(TransportFeedbackPacketLossTrackerTest, RepeatedSeqNumResetsWindow) {
TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
// Baseline - window has acked messages.
// Expected window contents: [] -> [01101].
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_,
{false, true, true, false, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
// A reset occurs.
SendPackets(&tracker, {static_cast<uint16_t>(base_ + 2)},
kDefaultSendIntervalMs);
ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
}
// The window is reset by the sending of a packet which is 0x8000 or more
// away from the newest packet acked/unacked packet.
TEST_P(TransportFeedbackPacketLossTrackerTest,
SendAfterLongSuspensionResetsWindow) {
TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
// Baseline - window has acked messages.
// Expected window contents: [] -> [01101].
SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
AddTransportFeedbackAndValidate(&tracker, base_,
{false, true, true, false, true});
ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
// A reset occurs.
SendPackets(&tracker, {static_cast<uint16_t>(base_ + 5 + 0x8000)},
kDefaultSendIntervalMs);
ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigMaxWindowSize) {
EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(0, 20, 10), "");
}
TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigPlrMinAcked) {
EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 0, 10), "");
}
TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigRplrMinPairs) {
EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 20, 0), "");
}
TEST(TransportFeedbackPacketLossTrackerTest, TimeCantFlowBackwards) {
TransportFeedbackPacketLossTracker tracker(5000, 2, 1);
tracker.OnPacketAdded(100, 0);
tracker.OnPacketAdded(101, 2);
EXPECT_DEATH(tracker.OnPacketAdded(102, 1), "");
}
#endif
// All tests are run multiple times with various baseline sequence number,
// to weed out potential bugs with wrap-around handling.
constexpr uint16_t kBases[] = {0x0000, 0x3456, 0xc032, 0xfffe};
INSTANTIATE_TEST_SUITE_P(_,
TransportFeedbackPacketLossTrackerTest,
::testing::ValuesIn(kBases));
} // namespace webrtc

View file

@ -890,8 +890,6 @@ rtc_library("audio_network_adaptor") {
"audio_network_adaptor/event_log_writer.h", "audio_network_adaptor/event_log_writer.h",
"audio_network_adaptor/fec_controller_plr_based.cc", "audio_network_adaptor/fec_controller_plr_based.cc",
"audio_network_adaptor/fec_controller_plr_based.h", "audio_network_adaptor/fec_controller_plr_based.h",
"audio_network_adaptor/fec_controller_rplr_based.cc",
"audio_network_adaptor/fec_controller_rplr_based.h",
"audio_network_adaptor/frame_length_controller.cc", "audio_network_adaptor/frame_length_controller.cc",
"audio_network_adaptor/frame_length_controller.h", "audio_network_adaptor/frame_length_controller.h",
"audio_network_adaptor/include/audio_network_adaptor.h", "audio_network_adaptor/include/audio_network_adaptor.h",
@ -1920,7 +1918,6 @@ if (rtc_include_tests) {
"audio_network_adaptor/dtx_controller_unittest.cc", "audio_network_adaptor/dtx_controller_unittest.cc",
"audio_network_adaptor/event_log_writer_unittest.cc", "audio_network_adaptor/event_log_writer_unittest.cc",
"audio_network_adaptor/fec_controller_plr_based_unittest.cc", "audio_network_adaptor/fec_controller_plr_based_unittest.cc",
"audio_network_adaptor/fec_controller_rplr_based_unittest.cc",
"audio_network_adaptor/frame_length_controller_unittest.cc", "audio_network_adaptor/frame_length_controller_unittest.cc",
"audio_network_adaptor/util/threshold_curve_unittest.cc", "audio_network_adaptor/util/threshold_curve_unittest.cc",
"codecs/builtin_audio_decoder_factory_unittest.cc", "codecs/builtin_audio_decoder_factory_unittest.cc",

View file

@ -72,18 +72,6 @@ void AudioNetworkAdaptorImpl::SetUplinkPacketLossFraction(
UpdateNetworkMetrics(network_metrics); UpdateNetworkMetrics(network_metrics);
} }
void AudioNetworkAdaptorImpl::SetUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) {
last_metrics_.uplink_recoverable_packet_loss_fraction =
uplink_recoverable_packet_loss_fraction;
DumpNetworkMetrics();
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_recoverable_packet_loss_fraction =
uplink_recoverable_packet_loss_fraction;
UpdateNetworkMetrics(network_metrics);
}
void AudioNetworkAdaptorImpl::SetRtt(int rtt_ms) { void AudioNetworkAdaptorImpl::SetRtt(int rtt_ms) {
last_metrics_.rtt_ms = rtt_ms; last_metrics_.rtt_ms = rtt_ms;
DumpNetworkMetrics(); DumpNetworkMetrics();

View file

@ -48,9 +48,6 @@ class AudioNetworkAdaptorImpl final : public AudioNetworkAdaptor {
void SetUplinkPacketLossFraction(float uplink_packet_loss_fraction) override; void SetUplinkPacketLossFraction(float uplink_packet_loss_fraction) override;
void SetUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) override;
void SetRtt(int rtt_ms) override; void SetRtt(int rtt_ms) override;
void SetTargetAudioBitrate(int target_audio_bitrate_bps) override; void SetTargetAudioBitrate(int target_audio_bitrate_bps) override;

View file

@ -41,10 +41,7 @@ MATCHER_P(NetworkMetricsIs, metric, "") {
arg.target_audio_bitrate_bps == metric.target_audio_bitrate_bps && arg.target_audio_bitrate_bps == metric.target_audio_bitrate_bps &&
arg.rtt_ms == metric.rtt_ms && arg.rtt_ms == metric.rtt_ms &&
arg.overhead_bytes_per_packet == metric.overhead_bytes_per_packet && arg.overhead_bytes_per_packet == metric.overhead_bytes_per_packet &&
arg.uplink_packet_loss_fraction == arg.uplink_packet_loss_fraction == metric.uplink_packet_loss_fraction;
metric.uplink_packet_loss_fraction &&
arg.uplink_recoverable_packet_loss_fraction ==
metric.uplink_recoverable_packet_loss_fraction;
} }
MATCHER_P(IsRtcEventAnaConfigEqualTo, config, "") { MATCHER_P(IsRtcEventAnaConfigEqualTo, config, "") {
@ -139,17 +136,6 @@ TEST(AudioNetworkAdaptorImplTest,
states.audio_network_adaptor->SetUplinkPacketLossFraction(kPacketLoss); states.audio_network_adaptor->SetUplinkPacketLossFraction(kPacketLoss);
} }
TEST(AudioNetworkAdaptorImplTest,
UpdateNetworkMetricsIsCalledOnSetUplinkRecoverablePacketLossFraction) {
auto states = CreateAudioNetworkAdaptor();
constexpr float kRecoverablePacketLoss = 0.1f;
Controller::NetworkMetrics check;
check.uplink_recoverable_packet_loss_fraction = kRecoverablePacketLoss;
SetExpectCallToUpdateNetworkMetrics(states.mock_controllers, check);
states.audio_network_adaptor->SetUplinkRecoverablePacketLossFraction(
kRecoverablePacketLoss);
}
TEST(AudioNetworkAdaptorImplTest, UpdateNetworkMetricsIsCalledOnSetRtt) { TEST(AudioNetworkAdaptorImplTest, UpdateNetworkMetricsIsCalledOnSetRtt) {
auto states = CreateAudioNetworkAdaptor(); auto states = CreateAudioNetworkAdaptor();
constexpr int kRtt = 100; constexpr int kRtt = 100;
@ -216,7 +202,6 @@ TEST(AudioNetworkAdaptorImplTest,
constexpr int kBandwidth = 16000; constexpr int kBandwidth = 16000;
constexpr float kPacketLoss = 0.7f; constexpr float kPacketLoss = 0.7f;
const auto kRecoverablePacketLoss = 0.2f;
constexpr int kRtt = 100; constexpr int kRtt = 100;
constexpr int kTargetAudioBitrate = 15000; constexpr int kTargetAudioBitrate = 15000;
constexpr size_t kOverhead = 64; constexpr size_t kOverhead = 64;
@ -238,11 +223,6 @@ TEST(AudioNetworkAdaptorImplTest,
fake_clock.AdvanceTime(TimeDelta::ms(50)); fake_clock.AdvanceTime(TimeDelta::ms(50));
timestamp_check += 50; timestamp_check += 50;
check.uplink_recoverable_packet_loss_fraction = kRecoverablePacketLoss;
EXPECT_CALL(*states.mock_debug_dump_writer,
DumpNetworkMetrics(NetworkMetricsIs(check), timestamp_check));
states.audio_network_adaptor->SetUplinkRecoverablePacketLossFraction(
kRecoverablePacketLoss);
fake_clock.AdvanceTime(TimeDelta::ms(200)); fake_clock.AdvanceTime(TimeDelta::ms(200));
timestamp_check += 200; timestamp_check += 200;

View file

@ -23,7 +23,6 @@ class Controller {
~NetworkMetrics(); ~NetworkMetrics();
absl::optional<int> uplink_bandwidth_bps; absl::optional<int> uplink_bandwidth_bps;
absl::optional<float> uplink_packet_loss_fraction; absl::optional<float> uplink_packet_loss_fraction;
absl::optional<float> uplink_recoverable_packet_loss_fraction;
absl::optional<int> target_audio_bitrate_bps; absl::optional<int> target_audio_bitrate_bps;
absl::optional<int> rtt_ms; absl::optional<int> rtt_ms;
absl::optional<size_t> overhead_bytes_per_packet; absl::optional<size_t> overhead_bytes_per_packet;

View file

@ -19,10 +19,10 @@
#include "modules/audio_coding/audio_network_adaptor/debug_dump_writer.h" #include "modules/audio_coding/audio_network_adaptor/debug_dump_writer.h"
#include "modules/audio_coding/audio_network_adaptor/dtx_controller.h" #include "modules/audio_coding/audio_network_adaptor/dtx_controller.h"
#include "modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h" #include "modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h"
#include "modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h"
#include "modules/audio_coding/audio_network_adaptor/frame_length_controller.h" #include "modules/audio_coding/audio_network_adaptor/frame_length_controller.h"
#include "modules/audio_coding/audio_network_adaptor/util/threshold_curve.h" #include "modules/audio_coding/audio_network_adaptor/util/threshold_curve.h"
#include "rtc_base/ignore_wundef.h" #include "rtc_base/ignore_wundef.h"
#include "rtc_base/logging.h"
#include "rtc_base/time_utils.h" #include "rtc_base/time_utils.h"
#if WEBRTC_ENABLE_PROTOBUF #if WEBRTC_ENABLE_PROTOBUF
@ -74,43 +74,6 @@ std::unique_ptr<FecControllerPlrBased> CreateFecControllerPlrBased(
config.time_constant_ms()))); config.time_constant_ms())));
} }
std::unique_ptr<FecControllerRplrBased> CreateFecControllerRplrBased(
const audio_network_adaptor::config::FecControllerRplrBased& config,
bool initial_fec_enabled) {
RTC_CHECK(config.has_fec_enabling_threshold());
RTC_CHECK(config.has_fec_disabling_threshold());
auto& fec_enabling_threshold = config.fec_enabling_threshold();
RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_bps());
RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_recoverable_packet_loss());
RTC_CHECK(fec_enabling_threshold.has_high_bandwidth_bps());
RTC_CHECK(
fec_enabling_threshold.has_high_bandwidth_recoverable_packet_loss());
auto& fec_disabling_threshold = config.fec_disabling_threshold();
RTC_CHECK(fec_disabling_threshold.has_low_bandwidth_bps());
RTC_CHECK(
fec_disabling_threshold.has_low_bandwidth_recoverable_packet_loss());
RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_bps());
RTC_CHECK(
fec_disabling_threshold.has_high_bandwidth_recoverable_packet_loss());
return std::unique_ptr<FecControllerRplrBased>(
new FecControllerRplrBased(FecControllerRplrBased::Config(
initial_fec_enabled,
ThresholdCurve(
fec_enabling_threshold.low_bandwidth_bps(),
fec_enabling_threshold.low_bandwidth_recoverable_packet_loss(),
fec_enabling_threshold.high_bandwidth_bps(),
fec_enabling_threshold.high_bandwidth_recoverable_packet_loss()),
ThresholdCurve(
fec_disabling_threshold.low_bandwidth_bps(),
fec_disabling_threshold.low_bandwidth_recoverable_packet_loss(),
fec_disabling_threshold.high_bandwidth_bps(),
fec_disabling_threshold
.high_bandwidth_recoverable_packet_loss()))));
}
std::unique_ptr<FrameLengthController> CreateFrameLengthController( std::unique_ptr<FrameLengthController> CreateFrameLengthController(
const audio_network_adaptor::config::FrameLengthController& config, const audio_network_adaptor::config::FrameLengthController& config,
rtc::ArrayView<const int> encoder_frame_lengths_ms, rtc::ArrayView<const int> encoder_frame_lengths_ms,
@ -291,9 +254,9 @@ std::unique_ptr<ControllerManager> ControllerManagerImpl::Create(
controller_config.fec_controller(), initial_fec_enabled); controller_config.fec_controller(), initial_fec_enabled);
break; break;
case audio_network_adaptor::config::Controller::kFecControllerRplrBased: case audio_network_adaptor::config::Controller::kFecControllerRplrBased:
controller = CreateFecControllerRplrBased( // FecControllerRplrBased has been removed and can't be used anymore.
controller_config.fec_controller_rplr_based(), initial_fec_enabled); RTC_NOTREACHED();
break; continue;
case audio_network_adaptor::config::Controller::kFrameLengthController: case audio_network_adaptor::config::Controller::kFrameLengthController:
controller = CreateFrameLengthController( controller = CreateFrameLengthController(
controller_config.frame_length_controller(), controller_config.frame_length_controller(),

View file

@ -105,11 +105,6 @@ void DebugDumpWriterImpl::DumpNetworkMetrics(
if (metrics.rtt_ms) if (metrics.rtt_ms)
dump_metrics->set_rtt_ms(*metrics.rtt_ms); dump_metrics->set_rtt_ms(*metrics.rtt_ms);
if (metrics.uplink_recoverable_packet_loss_fraction) {
dump_metrics->set_uplink_recoverable_packet_loss_fraction(
*metrics.uplink_recoverable_packet_loss_fraction);
}
DumpEventToFile(event, &dump_file_); DumpEventToFile(event, &dump_file_);
#endif // WEBRTC_ENABLE_PROTOBUF #endif // WEBRTC_ENABLE_PROTOBUF
} }

View file

@ -1,75 +0,0 @@
/*
* Copyright (c) 2017 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.
*/
#include "modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h"
#include "rtc_base/checks.h"
namespace webrtc {
FecControllerRplrBased::Config::Config(
bool initial_fec_enabled,
const ThresholdCurve& fec_enabling_threshold,
const ThresholdCurve& fec_disabling_threshold)
: initial_fec_enabled(initial_fec_enabled),
fec_enabling_threshold(fec_enabling_threshold),
fec_disabling_threshold(fec_disabling_threshold) {}
FecControllerRplrBased::FecControllerRplrBased(const Config& config)
: config_(config), fec_enabled_(config.initial_fec_enabled) {
RTC_DCHECK(config_.fec_disabling_threshold <= config_.fec_enabling_threshold);
}
FecControllerRplrBased::~FecControllerRplrBased() = default;
void FecControllerRplrBased::UpdateNetworkMetrics(
const NetworkMetrics& network_metrics) {
if (network_metrics.uplink_bandwidth_bps)
uplink_bandwidth_bps_ = network_metrics.uplink_bandwidth_bps;
if (network_metrics.uplink_recoverable_packet_loss_fraction) {
uplink_recoverable_packet_loss_ =
network_metrics.uplink_recoverable_packet_loss_fraction;
}
}
void FecControllerRplrBased::MakeDecision(AudioEncoderRuntimeConfig* config) {
RTC_DCHECK(!config->enable_fec);
RTC_DCHECK(!config->uplink_packet_loss_fraction);
fec_enabled_ = fec_enabled_ ? !FecDisablingDecision() : FecEnablingDecision();
config->enable_fec = fec_enabled_;
config->uplink_packet_loss_fraction =
uplink_recoverable_packet_loss_ ? *uplink_recoverable_packet_loss_ : 0.0;
}
bool FecControllerRplrBased::FecEnablingDecision() const {
if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) {
return false;
} else {
// Enable when above the curve or exactly on it.
return !config_.fec_enabling_threshold.IsBelowCurve(
{static_cast<float>(*uplink_bandwidth_bps_),
*uplink_recoverable_packet_loss_});
}
}
bool FecControllerRplrBased::FecDisablingDecision() const {
if (!uplink_bandwidth_bps_ || !uplink_recoverable_packet_loss_) {
return false;
} else {
// Disable when below the curve.
return config_.fec_disabling_threshold.IsBelowCurve(
{static_cast<float>(*uplink_bandwidth_bps_),
*uplink_recoverable_packet_loss_});
}
}
} // namespace webrtc

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2017 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_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_
#define MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_
#include "absl/types/optional.h"
#include "modules/audio_coding/audio_network_adaptor/controller.h"
#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h"
#include "modules/audio_coding/audio_network_adaptor/util/threshold_curve.h"
#include "rtc_base/constructor_magic.h"
namespace webrtc {
class FecControllerRplrBased final : public Controller {
public:
struct Config {
// |fec_enabling_threshold| defines a curve, above which FEC should be
// enabled. |fec_disabling_threshold| defines a curve, under which FEC
// should be disabled. See below
//
// recoverable
// packet-loss ^ | |
// | | | FEC
// | \ \ ON
// | FEC \ \_______ fec_enabling_threshold
// | OFF \_________ fec_disabling_threshold
// |-----------------> bandwidth
Config(bool initial_fec_enabled,
const ThresholdCurve& fec_enabling_threshold,
const ThresholdCurve& fec_disabling_threshold);
bool initial_fec_enabled;
ThresholdCurve fec_enabling_threshold;
ThresholdCurve fec_disabling_threshold;
};
explicit FecControllerRplrBased(const Config& config);
~FecControllerRplrBased() override;
void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override;
void MakeDecision(AudioEncoderRuntimeConfig* config) override;
private:
bool FecEnablingDecision() const;
bool FecDisablingDecision() const;
const Config config_;
bool fec_enabled_;
absl::optional<int> uplink_bandwidth_bps_;
absl::optional<float> uplink_recoverable_packet_loss_;
RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerRplrBased);
};
} // namespace webrtc
#endif // MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FEC_CONTROLLER_RPLR_BASED_H_

View file

@ -1,522 +0,0 @@
/*
* Copyright (c) 2017 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.
*/
#include "modules/audio_coding/audio_network_adaptor/fec_controller_rplr_based.h"
#include <random>
#include <utility>
#include "test/gtest.h"
namespace webrtc {
namespace {
// The test uses the following settings:
//
// recoverable ^
// packet-loss | | |
// | A| C| FEC
// | \ \ ON
// | FEC \ D\_______
// | OFF B\_________
// |-----------------> bandwidth
//
// A : (kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw)
// B : (kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw)
// C : (kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw)
// D : (kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw)
constexpr int kDisablingBandwidthLow = 15000;
constexpr float kDisablingRecoverablePacketLossAtLowBw = 0.08f;
constexpr int kDisablingBandwidthHigh = 64000;
constexpr float kDisablingRecoverablePacketLossAtHighBw = 0.01f;
constexpr int kEnablingBandwidthLow = 17000;
constexpr float kEnablingRecoverablePacketLossAtLowBw = 0.1f;
constexpr int kEnablingBandwidthHigh = 64000;
constexpr float kEnablingRecoverablePacketLossAtHighBw = 0.05f;
constexpr float kEpsilon = 1e-5f;
absl::optional<float> GetRandomProbabilityOrUnknown() {
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_real_distribution<> distribution(0, 1);
return (distribution(generator) < 0.2)
? absl::nullopt
: absl::optional<float>(distribution(generator));
}
std::unique_ptr<FecControllerRplrBased> CreateFecControllerRplrBased(
bool initial_fec_enabled) {
return std::unique_ptr<FecControllerRplrBased>(
new FecControllerRplrBased(FecControllerRplrBased::Config(
initial_fec_enabled,
ThresholdCurve(
kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw),
ThresholdCurve(kDisablingBandwidthLow,
kDisablingRecoverablePacketLossAtLowBw,
kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw))));
}
void UpdateNetworkMetrics(
FecControllerRplrBased* controller,
const absl::optional<int>& uplink_bandwidth_bps,
const absl::optional<float>& uplink_packet_loss,
const absl::optional<float>& uplink_recoveralbe_packet_loss) {
// UpdateNetworkMetrics can accept multiple network metric updates at once.
// However, currently, the most used case is to update one metric at a time.
// To reflect this fact, we separate the calls.
if (uplink_bandwidth_bps) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = uplink_bandwidth_bps;
controller->UpdateNetworkMetrics(network_metrics);
}
if (uplink_packet_loss) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_packet_loss_fraction = uplink_packet_loss;
controller->UpdateNetworkMetrics(network_metrics);
}
if (uplink_recoveralbe_packet_loss) {
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_recoverable_packet_loss_fraction =
uplink_recoveralbe_packet_loss;
controller->UpdateNetworkMetrics(network_metrics);
}
}
void UpdateNetworkMetrics(
FecControllerRplrBased* controller,
const absl::optional<int>& uplink_bandwidth_bps,
const absl::optional<float>& uplink_recoveralbe_packet_loss) {
// FecControllerRplrBased doesn't currently use the PLR (general packet-loss
// rate) at all. (This might be changed in the future.) The unit-tests will
// use a random value (including unknown), to show this does not interfere.
UpdateNetworkMetrics(controller, uplink_bandwidth_bps,
GetRandomProbabilityOrUnknown(),
uplink_recoveralbe_packet_loss);
}
// Checks that the FEC decision and |uplink_packet_loss_fraction| given by
// |states->controller->MakeDecision| matches |expected_enable_fec| and
// |expected_uplink_packet_loss_fraction|, respectively.
void CheckDecision(FecControllerRplrBased* controller,
bool expected_enable_fec,
float expected_uplink_packet_loss_fraction) {
AudioEncoderRuntimeConfig config;
controller->MakeDecision(&config);
// Less compact than comparing optionals, but yields more readable errors.
EXPECT_TRUE(config.enable_fec);
if (config.enable_fec) {
EXPECT_EQ(expected_enable_fec, *config.enable_fec);
}
EXPECT_TRUE(config.uplink_packet_loss_fraction);
if (config.uplink_packet_loss_fraction) {
EXPECT_EQ(expected_uplink_packet_loss_fraction,
*config.uplink_packet_loss_fraction);
}
}
} // namespace
TEST(FecControllerRplrBasedTest, OutputInitValueBeforeAnyInputsAreReceived) {
for (bool initial_fec_enabled : {false, true}) {
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
CheckDecision(controller.get(), initial_fec_enabled, 0);
}
}
TEST(FecControllerRplrBasedTest, OutputInitValueWhenUplinkBandwidthUnknown) {
// Regardless of the initial FEC state and the recoverable-packet-loss
// rate, the initial FEC state is maintained as long as the BWE is unknown.
for (bool initial_fec_enabled : {false, true}) {
for (float recoverable_packet_loss :
{kDisablingRecoverablePacketLossAtHighBw - kEpsilon,
kDisablingRecoverablePacketLossAtHighBw,
kDisablingRecoverablePacketLossAtHighBw + kEpsilon,
kEnablingRecoverablePacketLossAtHighBw - kEpsilon,
kEnablingRecoverablePacketLossAtHighBw,
kEnablingRecoverablePacketLossAtHighBw + kEpsilon}) {
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
UpdateNetworkMetrics(controller.get(), absl::nullopt,
recoverable_packet_loss);
CheckDecision(controller.get(), initial_fec_enabled,
recoverable_packet_loss);
}
}
}
TEST(FecControllerRplrBasedTest,
OutputInitValueWhenUplinkRecoverablePacketLossFractionUnknown) {
// Regardless of the initial FEC state and the BWE, the initial FEC state
// is maintained as long as the recoverable-packet-loss rate is unknown.
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {kDisablingBandwidthLow - 1, kDisablingBandwidthLow,
kDisablingBandwidthLow + 1, kEnablingBandwidthLow - 1,
kEnablingBandwidthLow, kEnablingBandwidthLow + 1}) {
auto controller = CreateFecControllerRplrBased(initial_fec_enabled);
UpdateNetworkMetrics(controller.get(), bandwidth, absl::nullopt);
CheckDecision(controller.get(), initial_fec_enabled, 0.0);
}
}
}
TEST(FecControllerRplrBasedTest, EnableFecForHighBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthHigh,
kEnablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw);
}
TEST(FecControllerRplrBasedTest, UpdateMultipleNetworkMetricsAtOnce) {
// This test is similar to EnableFecForHighBandwidth. But instead of
// using ::UpdateNetworkMetrics(...), which calls
// FecControllerRplrBasedTest::UpdateNetworkMetrics(...) multiple times, we
// we call it only once. This is to verify that
// FecControllerRplrBasedTest::UpdateNetworkMetrics(...) can handle multiple
// network updates at once. This is, however, not a common use case in current
// audio_network_adaptor_impl.cc.
auto controller = CreateFecControllerRplrBased(false);
Controller::NetworkMetrics network_metrics;
network_metrics.uplink_bandwidth_bps = kEnablingBandwidthHigh;
network_metrics.uplink_packet_loss_fraction = GetRandomProbabilityOrUnknown();
network_metrics.uplink_recoverable_packet_loss_fraction =
kEnablingRecoverablePacketLossAtHighBw;
controller->UpdateNetworkMetrics(network_metrics);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForHighBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
kEnablingRecoverablePacketLossAtHighBw * 0.99f;
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthHigh,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, EnableFecForMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
(kEnablingRecoverablePacketLossAtLowBw +
kEnablingRecoverablePacketLossAtHighBw) /
2.0;
UpdateNetworkMetrics(controller.get(),
(kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), true, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
kEnablingRecoverablePacketLossAtLowBw * 0.49f +
kEnablingRecoverablePacketLossAtHighBw * 0.51f;
UpdateNetworkMetrics(controller.get(),
(kEnablingBandwidthHigh + kEnablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, EnableFecForLowBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow,
kEnablingRecoverablePacketLossAtLowBw);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtLowBw);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForLowBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
constexpr float kRecoverablePacketLoss =
kEnablingRecoverablePacketLossAtLowBw * 0.99f;
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOffForVeryLowBandwidth) {
auto controller = CreateFecControllerRplrBased(false);
// Below |kEnablingBandwidthLow|, no recoverable packet loss fraction can
// cause FEC to turn on.
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow - 1, 1.0);
CheckDecision(controller.get(), false, 1.0);
}
TEST(FecControllerRplrBasedTest, DisableFecForHighBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
kDisablingRecoverablePacketLossAtHighBw - kEpsilon;
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOnForHighBandwidth) {
// Note: Disabling happens when the value is strictly below the threshold.
auto controller = CreateFecControllerRplrBased(true);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true,
kDisablingRecoverablePacketLossAtHighBw);
}
TEST(FecControllerRplrBasedTest, DisableFecOnMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
((kDisablingRecoverablePacketLossAtLowBw +
kDisablingRecoverablePacketLossAtHighBw) /
2.0f) -
kEpsilon;
UpdateNetworkMetrics(controller.get(),
(kDisablingBandwidthHigh + kDisablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, MaintainFecOnForMediumBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
kDisablingRecoverablePacketLossAtLowBw * 0.51f +
kDisablingRecoverablePacketLossAtHighBw * 0.49f - kEpsilon;
UpdateNetworkMetrics(controller.get(),
(kEnablingBandwidthHigh + kDisablingBandwidthLow) / 2,
kRecoverablePacketLoss);
CheckDecision(controller.get(), true, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, DisableFecForLowBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
constexpr float kRecoverablePacketLoss =
kDisablingRecoverablePacketLossAtLowBw - kEpsilon;
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthLow,
kRecoverablePacketLoss);
CheckDecision(controller.get(), false, kRecoverablePacketLoss);
}
TEST(FecControllerRplrBasedTest, DisableFecForVeryLowBandwidth) {
auto controller = CreateFecControllerRplrBased(true);
// Below |kEnablingBandwidthLow|, any recoverable packet loss fraction can
// cause FEC to turn off.
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthLow - 1, 1.0);
CheckDecision(controller.get(), false, 1.0);
}
TEST(FecControllerRplrBasedTest, CheckBehaviorOnChangingNetworkMetrics) {
// In this test, we let the network metrics to traverse from 1 to 5.
//
// recoverable ^
// packet-loss | 1 | |
// | | 2|
// | \ \ 3
// | \4 \_______
// | \_________
// |---------5-------> bandwidth
auto controller = CreateFecControllerRplrBased(true);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthLow - 1, 1.0);
CheckDecision(controller.get(), false, 1.0);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthLow,
kEnablingRecoverablePacketLossAtLowBw * 0.99f);
CheckDecision(controller.get(), false,
kEnablingRecoverablePacketLossAtLowBw * 0.99f);
UpdateNetworkMetrics(controller.get(), kEnablingBandwidthHigh,
kEnablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true, kEnablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw);
CheckDecision(controller.get(), true,
kDisablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(controller.get(), kDisablingBandwidthHigh + 1, 0.0);
CheckDecision(controller.get(), false, 0.0);
}
TEST(FecControllerRplrBasedTest, CheckBehaviorOnSpecialCurves) {
// We test a special configuration, where the points to define the FEC
// enabling/disabling curves are placed like the following, otherwise the test
// is the same as CheckBehaviorOnChangingNetworkMetrics.
//
// recoverable ^
// packet-loss | | |
// | | C|
// | | |
// | | D|_______
// | A|___B______
// |-----------------> bandwidth
constexpr int kEnablingBandwidthHigh = kEnablingBandwidthLow;
constexpr float kDisablingRecoverablePacketLossAtLowBw =
kDisablingRecoverablePacketLossAtHighBw;
FecControllerRplrBased controller(FecControllerRplrBased::Config(
true,
ThresholdCurve(
kEnablingBandwidthLow, kEnablingRecoverablePacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw),
ThresholdCurve(
kDisablingBandwidthLow, kDisablingRecoverablePacketLossAtLowBw,
kDisablingBandwidthHigh, kDisablingRecoverablePacketLossAtHighBw)));
UpdateNetworkMetrics(&controller, kDisablingBandwidthLow - 1, 1.0);
CheckDecision(&controller, false, 1.0);
UpdateNetworkMetrics(&controller, kEnablingBandwidthLow,
kEnablingRecoverablePacketLossAtHighBw * 0.99f);
CheckDecision(&controller, false,
kEnablingRecoverablePacketLossAtHighBw * 0.99f);
UpdateNetworkMetrics(&controller, kEnablingBandwidthHigh,
kEnablingRecoverablePacketLossAtHighBw);
CheckDecision(&controller, true, kEnablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(&controller, kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw);
CheckDecision(&controller, true, kDisablingRecoverablePacketLossAtHighBw);
UpdateNetworkMetrics(&controller, kDisablingBandwidthHigh + 1, 0.0);
CheckDecision(&controller, false, 0.0);
}
TEST(FecControllerRplrBasedTest, SingleThresholdCurveForEnablingAndDisabling) {
// Note: To avoid numerical errors, keep kRecoverablePacketLossAtLowBw and
// kRecoverablePacketLossAthighBw as (negative) integer powers of 2.
// This is mostly relevant for the O3 case.
constexpr int kBandwidthLow = 10000;
constexpr float kRecoverablePacketLossAtLowBw = 0.25f;
constexpr int kBandwidthHigh = 20000;
constexpr float kRecoverablePacketLossAtHighBw = 0.125f;
auto curve = ThresholdCurve(kBandwidthLow, kRecoverablePacketLossAtLowBw,
kBandwidthHigh, kRecoverablePacketLossAtHighBw);
// B* stands for "below-curve", O* for "on-curve", and A* for "above-curve".
//
// //
// recoverable ^ //
// packet-loss | | //
// | B1 O1 //
// | | //
// | O2 //
// | \ A1 //
// | \ //
// | O3 A2 //
// | B2 \ //
// | \ //
// | O4--O5---- //
// | //
// | B3 //
// |-----------------> bandwidth //
struct NetworkState {
int bandwidth;
float recoverable_packet_loss;
};
std::vector<NetworkState> below{
{kBandwidthLow - 1, kRecoverablePacketLossAtLowBw + 0.1f}, // B1
{(kBandwidthLow + kBandwidthHigh) / 2,
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) / 2 -
kEpsilon}, // B2
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw - kEpsilon} // B3
};
std::vector<NetworkState> on{
{kBandwidthLow, kRecoverablePacketLossAtLowBw + 0.1f}, // O1
{kBandwidthLow, kRecoverablePacketLossAtLowBw}, // O2
{(kBandwidthLow + kBandwidthHigh) / 2,
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) /
2}, // O3
{kBandwidthHigh, kRecoverablePacketLossAtHighBw}, // O4
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw}, // O5
};
std::vector<NetworkState> above{
{(kBandwidthLow + kBandwidthHigh) / 2,
(kRecoverablePacketLossAtLowBw + kRecoverablePacketLossAtHighBw) / 2 +
kEpsilon}, // A1
{kBandwidthHigh + 1, kRecoverablePacketLossAtHighBw + kEpsilon}, // A2
};
// Test that FEC is turned off whenever we're below the curve, independent
// of the starting FEC state.
for (NetworkState net_state : below) {
for (bool initial_fec_enabled : {false, true}) {
FecControllerRplrBased controller(
FecControllerRplrBased::Config(initial_fec_enabled, curve, curve));
UpdateNetworkMetrics(&controller, net_state.bandwidth,
net_state.recoverable_packet_loss);
CheckDecision(&controller, false, net_state.recoverable_packet_loss);
}
}
// Test that FEC is turned on whenever we're on the curve or above it,
// independent of the starting FEC state.
for (const std::vector<NetworkState>& states_list : {on, above}) {
for (NetworkState net_state : states_list) {
for (bool initial_fec_enabled : {false, true}) {
FecControllerRplrBased controller(
FecControllerRplrBased::Config(initial_fec_enabled, curve, curve));
UpdateNetworkMetrics(&controller, net_state.bandwidth,
net_state.recoverable_packet_loss);
CheckDecision(&controller, true, net_state.recoverable_packet_loss);
}
}
}
}
TEST(FecControllerRplrBasedTest, FecAlwaysOff) {
ThresholdCurve always_off_curve(0, 1.0f + kEpsilon, 0, 1.0f + kEpsilon);
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {0, 10000}) {
for (float recoverable_packet_loss : {0.0f, 0.5f, 1.0f}) {
FecControllerRplrBased controller(FecControllerRplrBased::Config(
initial_fec_enabled, always_off_curve, always_off_curve));
UpdateNetworkMetrics(&controller, bandwidth, recoverable_packet_loss);
CheckDecision(&controller, false, recoverable_packet_loss);
}
}
}
}
TEST(FecControllerRplrBasedTest, FecAlwaysOn) {
ThresholdCurve always_on_curve(0, 0.0f, 0, 0.0f);
for (bool initial_fec_enabled : {false, true}) {
for (int bandwidth : {0, 10000}) {
for (float recoverable_packet_loss : {0.0f, 0.5f, 1.0f}) {
FecControllerRplrBased controller(FecControllerRplrBased::Config(
initial_fec_enabled, always_on_curve, always_on_curve));
UpdateNetworkMetrics(&controller, bandwidth, recoverable_packet_loss);
CheckDecision(&controller, true, recoverable_packet_loss);
}
}
}
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
TEST(FecControllerRplrBasedDeathTest, InvalidConfig) {
EXPECT_DEATH(
FecControllerRplrBased controller(FecControllerRplrBased::Config(
true,
ThresholdCurve(
kDisablingBandwidthLow - 1, kEnablingRecoverablePacketLossAtLowBw,
kEnablingBandwidthHigh, kEnablingRecoverablePacketLossAtHighBw),
ThresholdCurve(kDisablingBandwidthLow,
kDisablingRecoverablePacketLossAtLowBw,
kDisablingBandwidthHigh,
kDisablingRecoverablePacketLossAtHighBw))),
"Check failed");
}
#endif
} // namespace webrtc

View file

@ -29,9 +29,6 @@ class AudioNetworkAdaptor {
virtual void SetUplinkPacketLossFraction( virtual void SetUplinkPacketLossFraction(
float uplink_packet_loss_fraction) = 0; float uplink_packet_loss_fraction) = 0;
virtual void SetUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) = 0;
virtual void SetRtt(int rtt_ms) = 0; virtual void SetRtt(int rtt_ms) = 0;
virtual void SetTargetAudioBitrate(int target_audio_bitrate_bps) = 0; virtual void SetTargetAudioBitrate(int target_audio_bitrate_bps) = 0;

View file

@ -26,9 +26,6 @@ class MockAudioNetworkAdaptor : public AudioNetworkAdaptor {
MOCK_METHOD1(SetUplinkPacketLossFraction, MOCK_METHOD1(SetUplinkPacketLossFraction,
void(float uplink_packet_loss_fraction)); void(float uplink_packet_loss_fraction));
MOCK_METHOD1(SetUplinkRecoverablePacketLossFraction,
void(float uplink_recoverable_packet_loss_fraction));
MOCK_METHOD1(SetRtt, void(int rtt_ms)); MOCK_METHOD1(SetRtt, void(int rtt_ms));
MOCK_METHOD1(SetTargetAudioBitrate, void(int target_audio_bitrate_bps)); MOCK_METHOD1(SetTargetAudioBitrate, void(int target_audio_bitrate_bps));

View file

@ -52,8 +52,6 @@ class AudioEncoderCng final : public AudioEncoder {
override; override;
void OnReceivedUplinkPacketLossFraction( void OnReceivedUplinkPacketLossFraction(
float uplink_packet_loss_fraction) override; float uplink_packet_loss_fraction) override;
void OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) override;
void OnReceivedUplinkBandwidth( void OnReceivedUplinkBandwidth(
int target_audio_bitrate_bps, int target_audio_bitrate_bps,
absl::optional<int64_t> bwe_period_ms) override; absl::optional<int64_t> bwe_period_ms) override;
@ -220,12 +218,6 @@ void AudioEncoderCng::OnReceivedUplinkPacketLossFraction(
uplink_packet_loss_fraction); uplink_packet_loss_fraction);
} }
void AudioEncoderCng::OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) {
speech_encoder_->OnReceivedUplinkRecoverablePacketLossFraction(
uplink_recoverable_packet_loss_fraction);
}
void AudioEncoderCng::OnReceivedUplinkBandwidth( void AudioEncoderCng::OnReceivedUplinkBandwidth(
int target_audio_bitrate_bps, int target_audio_bitrate_bps,
absl::optional<int64_t> bwe_period_ms) { absl::optional<int64_t> bwe_period_ms) {

View file

@ -593,15 +593,6 @@ void AudioEncoderOpusImpl::OnReceivedUplinkPacketLossFraction(
ApplyAudioNetworkAdaptor(); ApplyAudioNetworkAdaptor();
} }
void AudioEncoderOpusImpl::OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) {
if (!audio_network_adaptor_)
return;
audio_network_adaptor_->SetUplinkRecoverablePacketLossFraction(
uplink_recoverable_packet_loss_fraction);
ApplyAudioNetworkAdaptor();
}
void AudioEncoderOpusImpl::OnReceivedUplinkBandwidth( void AudioEncoderOpusImpl::OnReceivedUplinkBandwidth(
int target_audio_bitrate_bps, int target_audio_bitrate_bps,
absl::optional<int64_t> bwe_period_ms, absl::optional<int64_t> bwe_period_ms,

View file

@ -104,8 +104,6 @@ class AudioEncoderOpusImpl final : public AudioEncoder {
void DisableAudioNetworkAdaptor() override; void DisableAudioNetworkAdaptor() override;
void OnReceivedUplinkPacketLossFraction( void OnReceivedUplinkPacketLossFraction(
float uplink_packet_loss_fraction) override; float uplink_packet_loss_fraction) override;
void OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) override;
void OnReceivedUplinkBandwidth( void OnReceivedUplinkBandwidth(
int target_audio_bitrate_bps, int target_audio_bitrate_bps,
absl::optional<int64_t> bwe_period_ms) override; absl::optional<int64_t> bwe_period_ms) override;

View file

@ -125,12 +125,6 @@ void AudioEncoderCopyRed::OnReceivedUplinkPacketLossFraction(
uplink_packet_loss_fraction); uplink_packet_loss_fraction);
} }
void AudioEncoderCopyRed::OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) {
speech_encoder_->OnReceivedUplinkRecoverablePacketLossFraction(
uplink_recoverable_packet_loss_fraction);
}
void AudioEncoderCopyRed::OnReceivedUplinkBandwidth( void AudioEncoderCopyRed::OnReceivedUplinkBandwidth(
int target_audio_bitrate_bps, int target_audio_bitrate_bps,
absl::optional<int64_t> bwe_period_ms) { absl::optional<int64_t> bwe_period_ms) {

View file

@ -57,8 +57,6 @@ class AudioEncoderCopyRed final : public AudioEncoder {
override; override;
void OnReceivedUplinkPacketLossFraction( void OnReceivedUplinkPacketLossFraction(
float uplink_packet_loss_fraction) override; float uplink_packet_loss_fraction) override;
void OnReceivedUplinkRecoverablePacketLossFraction(
float uplink_recoverable_packet_loss_fraction) override;
void OnReceivedUplinkBandwidth( void OnReceivedUplinkBandwidth(
int target_audio_bitrate_bps, int target_audio_bitrate_bps,
absl::optional<int64_t> bwe_period_ms) override; absl::optional<int64_t> bwe_period_ms) override;

View file

@ -457,17 +457,6 @@ webrtc_fuzzer_test("pseudotcp_parser_fuzzer") {
] ]
} }
webrtc_fuzzer_test("transport_feedback_packet_loss_tracker_fuzzer") {
sources = [
"transport_feedback_packet_loss_tracker_fuzzer.cc",
]
deps = [
"../../api:array_view",
"../../audio",
"../../modules/rtp_rtcp:rtp_rtcp_format",
"../../rtc_base:rtc_base_approved",
]
}
rtc_library("audio_processing_fuzzer_helper") { rtc_library("audio_processing_fuzzer_helper") {
sources = [ sources = [
"audio_processing_fuzzer_helper.cc", "audio_processing_fuzzer_helper.cc",

View file

@ -1,279 +0,0 @@
/*
* Copyright (c) 2017 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.
*/
#include <algorithm>
#include "api/array_view.h"
#include "audio/transport_feedback_packet_loss_tracker.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
namespace webrtc {
namespace {
template <typename T>
T FuzzInput(const uint8_t** data, size_t* size) {
RTC_CHECK_GE(*size, sizeof(T));
T rc = ByteReader<T>::ReadBigEndian(*data);
*data += sizeof(T);
*size -= sizeof(T);
return rc;
}
size_t FuzzInRange(const uint8_t** data,
size_t* size,
size_t lower,
size_t upper) {
// Achieve a close-to-uniform distribution.
RTC_CHECK_LE(lower, upper);
RTC_CHECK_LT(upper - lower, 1 << (8 * sizeof(uint16_t)));
const size_t range = upper - lower;
const uint16_t fuzzed = FuzzInput<uint16_t>(data, size);
const size_t offset = (static_cast<float>(fuzzed) / 0x10000) * (range + 1);
RTC_CHECK_LE(offset, range); // (fuzzed <= 0xffff) -> (offset < range + 1)
return lower + offset;
}
class TransportFeedbackGenerator {
public:
explicit TransportFeedbackGenerator(const uint8_t** data, size_t* size)
: data_(data), size_(size) {}
bool GetNextTransportFeedbackVector(
std::vector<PacketFeedback>* feedback_vector) {
RTC_CHECK(feedback_vector->empty());
uint16_t remaining_packets = 0;
if (!ReadData<uint16_t>(&remaining_packets)) {
return false;
}
if (remaining_packets == 0) {
return true;
}
uint16_t seq_num;
if (!ReadData<uint16_t>(&seq_num)) { // Fuzz base sequence number.
return false;
}
while (remaining_packets > 0) {
uint8_t status_byte = 0;
if (!ReadData<uint8_t>(&status_byte)) {
return false;
}
// Each status byte contains 8 statuses.
for (size_t i = 0; i < 8 && remaining_packets > 0; ++i) {
// Any positive integer signals reception. kNotReceived signals loss.
// Other values are just illegal.
constexpr int64_t kArrivalTimeMs = 1234;
const bool received = (status_byte & (0x01 << i));
feedback_vector->emplace_back(PacketFeedback(
received ? kArrivalTimeMs : PacketFeedback::kNotReceived,
seq_num++));
--remaining_packets;
}
}
return true;
}
private:
template <typename T>
bool ReadData(T* value) {
if (*size_ < sizeof(T)) {
return false;
} else {
*value = FuzzInput<T>(data_, size_);
return true;
}
}
const uint8_t** data_;
size_t* size_;
};
bool Setup(const uint8_t** data,
size_t* size,
std::unique_ptr<TransportFeedbackPacketLossTracker>* tracker) {
if (*size < 3 * sizeof(uint16_t)) {
return false;
}
constexpr size_t kSeqNumHalf = 0x8000u;
const int64_t max_window_size_ms = FuzzInRange(data, size, 1, 1 << 16);
const size_t plr_min_num_packets = FuzzInRange(data, size, 1, kSeqNumHalf);
const size_t rplr_min_num_pairs = FuzzInRange(data, size, 1, kSeqNumHalf - 1);
tracker->reset(new TransportFeedbackPacketLossTracker(
max_window_size_ms, plr_min_num_packets, rplr_min_num_pairs));
return true;
}
bool FuzzSequenceNumberDelta(const uint8_t** data,
size_t* size,
uint16_t* delta) {
// Fuzz with a higher likelihood for immediately consecutive pairs
// than you would by just fuzzing 1-256.
// Note: Values higher than 256 still possible, but that would be in a new
// packet-sending block.
// * Fuzzed value in [0 : 127] (50% chance) -> delta is 1.
// * Fuzzed value in [128 : 255] (50% chance) -> delta in range [2 : 129].
if (*size < sizeof(uint8_t)) {
return false;
}
uint8_t fuzzed = FuzzInput<uint8_t>(data, size);
*delta = (fuzzed < 128) ? 1 : (fuzzed - 128 + 2);
return true;
}
bool FuzzClockAdvancement(const uint8_t** data,
size_t* size,
int64_t* time_ms) {
// Fuzzing 64-bit worth of delta would be extreme overkill, as 32-bit is
// already ~49 days long. We'll fuzz deltas up to a smaller value, and this
// way also guarantee that wrap-around is impossible, as in real life.
// Higher likelihood for more likely cases:
// 5% chance of delta = 0.
// 20% chance of delta in range [1 : 10] (uniformly distributed)
// 55% chance of delta in range [11 : 500] (uniformly distributed)
// 20% chance of delta in range [501 : 10000] (uniformly distributed)
struct ProbabilityDistribution {
float probability;
size_t lower;
size_t higher;
};
constexpr ProbabilityDistribution clock_probability_distribution[] = {
{0.05, 0, 0}, {0.20, 1, 10}, {0.55, 11, 500}, {0.20, 501, 10000}};
if (*size < sizeof(uint8_t)) {
return false;
}
const float fuzzed = FuzzInput<uint8_t>(data, size) / 256.0f;
float cumulative_probability = 0;
for (const auto& dist : clock_probability_distribution) {
cumulative_probability += dist.probability;
if (fuzzed < cumulative_probability) {
if (dist.lower == dist.higher) {
*time_ms += dist.lower;
return true;
} else if (*size < sizeof(uint16_t)) {
return false;
} else {
*time_ms += FuzzInRange(data, size, dist.lower, dist.higher);
return true;
}
}
}
RTC_NOTREACHED();
return false;
}
bool FuzzPacketSendBlock(
std::unique_ptr<TransportFeedbackPacketLossTracker>& tracker,
const uint8_t** data,
size_t* size,
int64_t* time_ms) {
// We want to test with block lengths between 1 and 2^16, inclusive.
if (*size < sizeof(uint8_t)) {
return false;
}
size_t packet_block_len = 1 + FuzzInput<uint8_t>(data, size);
// First sent sequence number uniformly selected.
if (*size < sizeof(uint16_t)) {
return false;
}
uint16_t seq_num = FuzzInput<uint16_t>(data, size);
tracker->OnPacketAdded(seq_num, *time_ms);
tracker->Validate();
bool may_continue = FuzzClockAdvancement(data, size, time_ms);
if (!may_continue) {
return false;
}
for (size_t i = 1; i < packet_block_len; i++) {
uint16_t delta;
may_continue = FuzzSequenceNumberDelta(data, size, &delta);
if (!may_continue)
return false;
may_continue = FuzzClockAdvancement(data, size, time_ms);
if (!may_continue)
return false;
seq_num += delta;
tracker->OnPacketAdded(seq_num, *time_ms);
tracker->Validate();
}
return true;
}
bool FuzzTransportFeedbackBlock(
std::unique_ptr<TransportFeedbackPacketLossTracker>& tracker,
const uint8_t** data,
size_t* size) {
// Fuzz the number of back-to-back feedbacks. At least one, or this would
// be meaningless - we'd go straight back to fuzzing another packet
// transmission block.
if (*size < sizeof(uint8_t)) {
return false;
}
size_t feedbacks_num = 1 + (FuzzInput<uint8_t>(data, size) & 0x3f);
TransportFeedbackGenerator feedback_generator(data, size);
for (size_t i = 0; i < feedbacks_num; i++) {
std::vector<PacketFeedback> feedback_vector;
bool may_continue =
feedback_generator.GetNextTransportFeedbackVector(&feedback_vector);
if (!may_continue) {
return false;
}
tracker->OnPacketFeedbackVector(feedback_vector);
tracker->Validate();
}
return true;
}
} // namespace
void FuzzOneInput(const uint8_t* data, size_t size) {
std::unique_ptr<TransportFeedbackPacketLossTracker> tracker;
bool may_continue;
may_continue = Setup(&data, &size, &tracker);
// We never expect this to wrap around, so it makes sense to just start with
// a sane value, and keep on incrementing by a fuzzed delta.
if (size < sizeof(uint32_t)) {
return;
}
int64_t time_ms = FuzzInput<uint32_t>(&data, &size);
while (may_continue) {
may_continue = FuzzPacketSendBlock(tracker, &data, &size, &time_ms);
if (!may_continue) {
return;
}
may_continue = FuzzTransportFeedbackBlock(tracker, &data, &size);
}
}
} // namespace webrtc