Skip to content

Commit

Permalink
Prevent updating state in the delay manager if the packet was reordered.
Browse files Browse the repository at this point in the history
Currently, if the last packet was reordered (e.g. due to retransmission) then the next packet's inter-arrival time will be estimated incorrectly due to the jump in sequence numbers. This change prevents that by not resetting the stopwatch on reordered packets.

This will also better estimate inter-arrival times when we have multiple reordered packets in a burst. Currently we would only measure the iat of the first reordered packet correctly and not the ones coming after it.

There is a slight risk introducing this: If we would receive an out of order packet far into the future (in sequence numbers) and then continue getting packets in the normal order, then we would not update the current sequence number for these and incorrectly estimate their inter-arrival times since they would all be considered reordered.

Change-Id: Ic938a37cbddf1cb9c30b610218f56794568d3d01
Bug: webrtc:10178
Reviewed-on: https://webrtc-review.googlesource.com/c/119949
Reviewed-by: Minyue Li <[email protected]>
Reviewed-by: Henrik Lundin <[email protected]>
Commit-Queue: Jakob Ivarsson‎ <[email protected]>
Cr-Commit-Position: refs/heads/master@{#26572}
  • Loading branch information
Jakob Ivarsson authored and Commit Bot committed Feb 6, 2019
1 parent 9025bd5 commit e98954c
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 7 deletions.
4 changes: 2 additions & 2 deletions modules/audio_coding/neteq/decision_logic_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ TEST(DecisionLogic, CreateAndDestroy) {
TickTimer tick_timer;
PacketBuffer packet_buffer(10, &tick_timer);
DelayPeakDetector delay_peak_detector(&tick_timer, false);
DelayManager delay_manager(240, 0, &delay_peak_detector, &tick_timer);
DelayManager delay_manager(240, 0, false, &delay_peak_detector, &tick_timer);
BufferLevelFilter buffer_level_filter;
DecisionLogic* logic = DecisionLogic::Create(
fs_hz, output_size_samples, false, &decoder_database, packet_buffer,
Expand All @@ -48,7 +48,7 @@ TEST(DecisionLogic, PostponeDecodingAfterExpansionSettings) {
TickTimer tick_timer;
PacketBuffer packet_buffer(10, &tick_timer);
DelayPeakDetector delay_peak_detector(&tick_timer, false);
DelayManager delay_manager(240, 0, &delay_peak_detector, &tick_timer);
DelayManager delay_manager(240, 0, false, &delay_peak_detector, &tick_timer);
BufferLevelFilter buffer_level_filter;
{
test::ScopedFieldTrials field_trial(
Expand Down
14 changes: 12 additions & 2 deletions modules/audio_coding/neteq/delay_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ constexpr int kCumulativeSumDrift = 2; // Drift term for cumulative sum
// Steady-state forgetting factor for |iat_vector_|, 0.9993 in Q15.
constexpr int kIatFactor_ = 32745;
constexpr int kMaxIat = 64; // Max inter-arrival time to register.
constexpr int kMaxReorderedPackets =
10; // Max number of consecutive reordered packets.

absl::optional<int> GetForcedLimitProbability() {
constexpr char kForceTargetDelayPercentileFieldTrial[] =
Expand Down Expand Up @@ -63,6 +65,7 @@ namespace webrtc {

DelayManager::DelayManager(size_t max_packets_in_buffer,
int base_min_target_delay_ms,
bool enable_rtx_handling,
DelayPeakDetector* peak_detector,
const TickTimer* tick_timer)
: first_packet_received_(false),
Expand All @@ -85,7 +88,8 @@ DelayManager::DelayManager(size_t max_packets_in_buffer,
last_pack_cng_or_dtmf_(1),
frame_length_change_experiment_(
field_trial::IsEnabled("WebRTC-Audio-NetEqFramelengthExperiment")),
forced_limit_probability_(GetForcedLimitProbability()) {
forced_limit_probability_(GetForcedLimitProbability()),
enable_rtx_handling_(enable_rtx_handling) {
assert(peak_detector); // Should never be NULL.
RTC_DCHECK_GE(base_min_target_delay_ms_, 0);
RTC_DCHECK_LE(minimum_delay_ms_, maximum_delay_ms_);
Expand Down Expand Up @@ -146,6 +150,7 @@ int DelayManager::Update(uint16_t sequence_number,
rtc::saturated_cast<int>(1000 * packet_len_samp / sample_rate_hz);
}

bool reordered = false;
if (packet_len_ms > 0) {
// Cannot update statistics unless |packet_len_ms| is valid.
// Calculate inter-arrival time (IAT) in integer "packet times"
Expand All @@ -158,7 +163,6 @@ int DelayManager::Update(uint16_t sequence_number,
}

// Check for discontinuous packet sequence and re-ordering.
bool reordered = false;
if (IsNewerSequenceNumber(sequence_number, last_seq_no_ + 1)) {
// Compensate for gap in the sequence numbers. Reduce IAT with the
// expected extra time due to lost packets, but ensure that the IAT is
Expand All @@ -183,6 +187,12 @@ int DelayManager::Update(uint16_t sequence_number,
LimitTargetLevel();
} // End if (packet_len_ms > 0).

if (enable_rtx_handling_ && reordered &&
num_reordered_packets_ < kMaxReorderedPackets) {
++num_reordered_packets_;
return 0;
}
num_reordered_packets_ = 0;
// Prepare for next packet arrival.
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
last_seq_no_ = sequence_number;
Expand Down
3 changes: 3 additions & 0 deletions modules/audio_coding/neteq/delay_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class DelayManager {
// PeakDetector object to the DelayManager.
DelayManager(size_t max_packets_in_buffer,
int base_min_target_delay_ms,
bool enable_rtx_handling,
DelayPeakDetector* peak_detector,
const TickTimer* tick_timer);

Expand Down Expand Up @@ -177,6 +178,8 @@ class DelayManager {
int last_pack_cng_or_dtmf_;
const bool frame_length_change_experiment_;
const absl::optional<int> forced_limit_probability_;
const bool enable_rtx_handling_;
int num_reordered_packets_ = 0; // Number of consecutive reordered packets.

RTC_DISALLOW_COPY_AND_ASSIGN(DelayManager);
};
Expand Down
30 changes: 28 additions & 2 deletions modules/audio_coding/neteq/delay_manager_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class DelayManagerTest : public ::testing::Test {
MockDelayPeakDetector detector_;
uint16_t seq_no_;
uint32_t ts_;
bool enable_rtx_handling_ = false;
};

DelayManagerTest::DelayManagerTest()
Expand All @@ -60,8 +61,8 @@ void DelayManagerTest::SetUp() {

void DelayManagerTest::RecreateDelayManager() {
EXPECT_CALL(detector_, Reset()).Times(1);
dm_.reset(new DelayManager(kMaxNumberOfPackets, kMinDelayMs, &detector_,
&tick_timer_));
dm_.reset(new DelayManager(kMaxNumberOfPackets, kMinDelayMs,
enable_rtx_handling_, &detector_, &tick_timer_));
}

void DelayManagerTest::SetPacketAudioLength(int lengt_ms) {
Expand Down Expand Up @@ -320,6 +321,31 @@ TEST_F(DelayManagerTest, UpdateReorderedPacket) {
EXPECT_EQ(0, dm_->Update(seq_no_ - 1, ts_ - kFrameSizeMs, kFs));
}

TEST_F(DelayManagerTest, EnableRtxHandling) {
enable_rtx_handling_ = true;
RecreateDelayManager();

// Insert first packet.
SetPacketAudioLength(kFrameSizeMs);
InsertNextPacket();

// Insert reordered packet.
// TODO(jakobi): Test estimated inter-arrival time by mocking the histogram
// instead of checking the call to the peak detector.
EXPECT_CALL(detector_, Update(3, true, _));
EXPECT_EQ(0, dm_->Update(seq_no_ - 3, ts_ - 3 * kFrameSizeMs, kFs));

// Insert another reordered packet.
EXPECT_CALL(detector_, Update(2, true, _));
EXPECT_EQ(0, dm_->Update(seq_no_ - 2, ts_ - 2 * kFrameSizeMs, kFs));

// Insert the next packet in order and verify that the inter-arrival time is
// estimated correctly.
IncreaseTime(kFrameSizeMs);
EXPECT_CALL(detector_, Update(1, false, _));
InsertNextPacket();
}

// Tests that skipped sequence numbers (simulating empty packets) are handled
// correctly.
TEST_F(DelayManagerTest, EmptyPacketsReported) {
Expand Down
2 changes: 2 additions & 0 deletions modules/audio_coding/neteq/mock/mock_delay_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ class MockDelayManager : public DelayManager {
public:
MockDelayManager(size_t max_packets_in_buffer,
int base_min_target_delay_ms,
bool enable_rtx_handling,
DelayPeakDetector* peak_detector,
const TickTimer* tick_timer)
: DelayManager(max_packets_in_buffer,
base_min_target_delay_ms,
enable_rtx_handling,
peak_detector,
tick_timer) {}
virtual ~MockDelayManager() { Die(); }
Expand Down
1 change: 1 addition & 0 deletions modules/audio_coding/neteq/neteq_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ NetEqImpl::Dependencies::Dependencies(
new DelayPeakDetector(tick_timer.get(), config.enable_rtx_handling)),
delay_manager(new DelayManager(config.max_packets_in_buffer,
config.min_delay_ms,
config.enable_rtx_handling,
delay_peak_detector.get(),
tick_timer.get())),
dtmf_buffer(new DtmfBuffer(config.sample_rate_hz)),
Expand Down
2 changes: 1 addition & 1 deletion modules/audio_coding/neteq/neteq_impl_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class NetEqImplTest : public ::testing::Test {
if (use_mock_delay_manager_) {
std::unique_ptr<MockDelayManager> mock(new MockDelayManager(
config_.max_packets_in_buffer, config_.min_delay_ms,
delay_peak_detector_, tick_timer_));
config_.enable_rtx_handling, delay_peak_detector_, tick_timer_));
mock_delay_manager_ = mock.get();
EXPECT_CALL(*mock_delay_manager_, set_streaming_mode(false)).Times(1);
deps.delay_manager = std::move(mock);
Expand Down
1 change: 1 addition & 0 deletions modules/audio_coding/neteq/tools/neteq_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ NetEqTest::SimulationStepResult NetEqTest::RunToNextGetAudio() {
: -1;
*text_log_ << "Packet - wallclock: " << std::setw(5) << time_now_ms
<< ", delta wc: " << std::setw(4) << delta_wallclock
<< ", seq_no: " << packet_data->header.sequenceNumber
<< ", timestamp: " << std::setw(10)
<< packet_data->header.timestamp
<< ", delta ts: " << std::setw(4) << delta_timestamp
Expand Down

0 comments on commit e98954c

Please sign in to comment.