Implement packet discard rate in NetEq.
BUG=webrtc:7903
Change-Id: I819c9362671ca0b02c602d53e4dc39afdd8ec465
Reviewed-on: https://chromium-review.googlesource.com/555311
Commit-Queue: Minyue Li <minyue@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#18899}
diff --git a/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h b/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h
index 5a2f7e1..7967ab9 100644
--- a/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h
+++ b/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h
@@ -48,12 +48,13 @@
const Packet*());
MOCK_METHOD0(GetNextPacket,
rtc::Optional<Packet>());
- MOCK_METHOD0(DiscardNextPacket,
- int());
- MOCK_METHOD2(DiscardOldPackets,
- int(uint32_t timestamp_limit, uint32_t horizon_samples));
- MOCK_METHOD1(DiscardAllOldPackets,
- int(uint32_t timestamp_limit));
+ MOCK_METHOD1(DiscardNextPacket, int(StatisticsCalculator* stats));
+ MOCK_METHOD3(DiscardOldPackets,
+ void(uint32_t timestamp_limit,
+ uint32_t horizon_samples,
+ StatisticsCalculator* stats));
+ MOCK_METHOD2(DiscardAllOldPackets,
+ void(uint32_t timestamp_limit, StatisticsCalculator* stats));
MOCK_CONST_METHOD0(NumPacketsInBuffer,
size_t());
MOCK_METHOD1(IncrementWaitingTimes,
diff --git a/webrtc/modules/audio_coding/neteq/mock/mock_statistics_calculator.h b/webrtc/modules/audio_coding/neteq/mock/mock_statistics_calculator.h
new file mode 100644
index 0000000..da4e3ed
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/mock/mock_statistics_calculator.h
@@ -0,0 +1,27 @@
+/*
+ * 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 WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_STATISTICS_CALCULATOR_H_
+#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_STATISTICS_CALCULATOR_H_
+
+#include "webrtc/modules/audio_coding/neteq/statistics_calculator.h"
+
+#include "webrtc/test/gmock.h"
+
+namespace webrtc {
+
+class MockStatisticsCalculator : public StatisticsCalculator {
+ public:
+ // For current unittest, we mock only one method.
+ MOCK_METHOD1(PacketsDiscarded, void(size_t num_packets));
+};
+
+} // namespace webrtc
+#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_STATISTICS_CALCULATOR_H_
diff --git a/webrtc/modules/audio_coding/neteq/neteq_impl.cc b/webrtc/modules/audio_coding/neteq/neteq_impl.cc
index fb3cc11..bf4e4eb 100644
--- a/webrtc/modules/audio_coding/neteq/neteq_impl.cc
+++ b/webrtc/modules/audio_coding/neteq/neteq_impl.cc
@@ -217,7 +217,7 @@
const std::vector<int> changed_payload_types =
decoder_database_->SetCodecs(codecs);
for (const int pt : changed_payload_types) {
- packet_buffer_->DiscardPacketsWithPayloadType(pt);
+ packet_buffer_->DiscardPacketsWithPayloadType(pt, &stats_);
}
}
@@ -268,7 +268,7 @@
rtc::CritScope lock(&crit_sect_);
int ret = decoder_database_->Remove(rtp_payload_type);
if (ret == DecoderDatabase::kOK || ret == DecoderDatabase::kDecoderNotFound) {
- packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type);
+ packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type, &stats_);
return kOK;
}
return kFail;
@@ -1063,7 +1063,8 @@
uint32_t end_timestamp = sync_buffer_->end_timestamp();
if (!new_codec_) {
const uint32_t five_seconds_samples = 5 * fs_hz_;
- packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples);
+ packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples,
+ &stats_);
}
const Packet* packet = packet_buffer_->PeekNextPacket();
@@ -1084,12 +1085,12 @@
(end_timestamp >= packet->timestamp ||
end_timestamp + generated_noise_samples > packet->timestamp)) {
// Don't use this packet, discard it.
- if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) {
+ if (packet_buffer_->DiscardNextPacket(&stats_) != PacketBuffer::kOK) {
assert(false); // Must be ok by design.
}
// Check buffer again.
if (!new_codec_) {
- packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_);
+ packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_, &stats_);
}
packet = packet_buffer_->PeekNextPacket();
}
@@ -2003,7 +2004,7 @@
// we could end up in the situation where we never decode anything, since
// all incoming packets are considered too old but the buffer will also
// never be flooded and flushed.
- packet_buffer_->DiscardAllOldPackets(timestamp_);
+ packet_buffer_->DiscardAllOldPackets(timestamp_, &stats_);
}
return rtc::dchecked_cast<int>(extracted_samples);
diff --git a/webrtc/modules/audio_coding/neteq/neteq_unittest.cc b/webrtc/modules/audio_coding/neteq/neteq_unittest.cc
index eed7740..5049a6b 100644
--- a/webrtc/modules/audio_coding/neteq/neteq_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/neteq_unittest.cc
@@ -482,10 +482,10 @@
"6237dd113ad80d7764fe4c90b55b2ec035eae64e");
const std::string network_stats_checksum =
- PlatformChecksum("0869a450a819b14bf2a91eb6f3629a3421d17606",
- "0869a450a819b14bf2a91eb6f3629a3421d17606",
- "0869a450a819b14bf2a91eb6f3629a3421d17606",
- "0869a450a819b14bf2a91eb6f3629a3421d17606");
+ PlatformChecksum("7a29daca4dfa4fb6eaec06d7e63c1729da7708f6",
+ "7a29daca4dfa4fb6eaec06d7e63c1729da7708f6",
+ "7a29daca4dfa4fb6eaec06d7e63c1729da7708f6",
+ "7a29daca4dfa4fb6eaec06d7e63c1729da7708f6");
const std::string rtcp_stats_checksum = PlatformChecksum(
"e37c797e3de6a64dda88c9ade7a013d022a2e1e0",
diff --git a/webrtc/modules/audio_coding/neteq/packet_buffer.cc b/webrtc/modules/audio_coding/neteq/packet_buffer.cc
index 78a71c7..cea6b3d 100644
--- a/webrtc/modules/audio_coding/neteq/packet_buffer.cc
+++ b/webrtc/modules/audio_coding/neteq/packet_buffer.cc
@@ -19,6 +19,7 @@
#include "webrtc/api/audio_codecs/audio_decoder.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/audio_coding/neteq/decoder_database.h"
+#include "webrtc/modules/audio_coding/neteq/statistics_calculator.h"
#include "webrtc/modules/audio_coding/neteq/tick_timer.h"
namespace webrtc {
@@ -206,41 +207,51 @@
return packet;
}
-int PacketBuffer::DiscardNextPacket() {
+int PacketBuffer::DiscardNextPacket(StatisticsCalculator* stats) {
if (Empty()) {
return kBufferEmpty;
}
// Assert that the packet sanity checks in InsertPacket method works.
RTC_DCHECK(!buffer_.front().empty());
buffer_.pop_front();
+ stats->PacketsDiscarded(1);
return kOK;
}
-int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit,
- uint32_t horizon_samples) {
+void PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit,
+ uint32_t horizon_samples,
+ StatisticsCalculator* stats) {
+ // TODO(minyue): the following implementation is wrong. It won't discard
+ // old packets if the buffer_.front() is newer than timestamp_limit -
+ // horizon_samples. https://bugs.chromium.org/p/webrtc/issues/detail?id=7937
while (!Empty() && timestamp_limit != buffer_.front().timestamp &&
IsObsoleteTimestamp(buffer_.front().timestamp, timestamp_limit,
horizon_samples)) {
- if (DiscardNextPacket() != kOK) {
+ if (DiscardNextPacket(stats) != kOK) {
assert(false); // Must be ok by design.
}
}
- return 0;
}
-int PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit) {
- return DiscardOldPackets(timestamp_limit, 0);
+void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit,
+ StatisticsCalculator* stats) {
+ DiscardOldPackets(timestamp_limit, 0, stats);
}
-void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type) {
+void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type,
+ StatisticsCalculator* stats) {
+ int packets_discarded = 0;
for (auto it = buffer_.begin(); it != buffer_.end(); /* */) {
const Packet& packet = *it;
if (packet.payload_type == payload_type) {
it = buffer_.erase(it);
+ ++packets_discarded;
} else {
++it;
}
}
+ if (packets_discarded > 0)
+ stats->PacketsDiscarded(packets_discarded);
}
size_t PacketBuffer::NumPacketsInBuffer() const {
diff --git a/webrtc/modules/audio_coding/neteq/packet_buffer.h b/webrtc/modules/audio_coding/neteq/packet_buffer.h
index a26d6c5..f937322 100644
--- a/webrtc/modules/audio_coding/neteq/packet_buffer.h
+++ b/webrtc/modules/audio_coding/neteq/packet_buffer.h
@@ -20,6 +20,7 @@
namespace webrtc {
class DecoderDatabase;
+class StatisticsCalculator;
class TickTimer;
// This is the actual buffer holding the packets before decoding.
@@ -92,22 +93,24 @@
// Discards the first packet in the buffer. The packet is deleted.
// Returns PacketBuffer::kBufferEmpty if the buffer is empty,
// PacketBuffer::kOK otherwise.
- virtual int DiscardNextPacket();
+ virtual int DiscardNextPacket(StatisticsCalculator* stats);
// Discards all packets that are (strictly) older than timestamp_limit,
// but newer than timestamp_limit - horizon_samples. Setting horizon_samples
// to zero implies that the horizon is set to half the timestamp range. That
// is, if a packet is more than 2^31 timestamps into the future compared with
// timestamp_limit (including wrap-around), it is considered old.
- // Returns number of packets discarded.
- virtual int DiscardOldPackets(uint32_t timestamp_limit,
- uint32_t horizon_samples);
+ virtual void DiscardOldPackets(uint32_t timestamp_limit,
+ uint32_t horizon_samples,
+ StatisticsCalculator* stats);
// Discards all packets that are (strictly) older than timestamp_limit.
- virtual int DiscardAllOldPackets(uint32_t timestamp_limit);
+ virtual void DiscardAllOldPackets(uint32_t timestamp_limit,
+ StatisticsCalculator* stats);
// Removes all packets with a specific payload type from the buffer.
- virtual void DiscardPacketsWithPayloadType(uint8_t payload_type);
+ virtual void DiscardPacketsWithPayloadType(uint8_t payload_type,
+ StatisticsCalculator* stats);
// Returns the number of packets in the buffer, including duplicates and
// redundant packets.
diff --git a/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc b/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc
index e8795a9..82f7246 100644
--- a/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc
@@ -10,15 +10,17 @@
// Unit tests for PacketBuffer class.
+#include "webrtc/modules/audio_coding/neteq/packet_buffer.h"
#include "webrtc/api/audio_codecs/builtin_audio_decoder_factory.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h"
+#include "webrtc/modules/audio_coding/neteq/mock/mock_statistics_calculator.h"
#include "webrtc/modules/audio_coding/neteq/packet.h"
-#include "webrtc/modules/audio_coding/neteq/packet_buffer.h"
#include "webrtc/modules/audio_coding/neteq/tick_timer.h"
#include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h"
using ::testing::Return;
+using ::testing::StrictMock;
using ::testing::_;
namespace webrtc {
@@ -299,22 +301,42 @@
PacketList list;
const int payload_len = 10;
+ constexpr int kTotalPackets = 10;
// Insert 10 small packets.
- for (int i = 0; i < 10; ++i) {
+ for (int i = 0; i < kTotalPackets; ++i) {
buffer.InsertPacket(gen.NextPacket(payload_len));
}
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ uint32_t current_ts = start_ts;
+
// Discard them one by one and make sure that the right packets are at the
// front of the buffer.
- uint32_t current_ts = start_ts;
- for (int i = 0; i < 10; ++i) {
+ constexpr int kDiscardPackets = 5;
+ for (int i = 0; i < kDiscardPackets; ++i) {
uint32_t ts;
EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&ts));
EXPECT_EQ(current_ts, ts);
- EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket());
+ EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(1);
+ EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket(&mock_stats));
current_ts += ts_increment;
}
+
+ constexpr int kRemainingPackets = kTotalPackets - kDiscardPackets;
+ // This will not discard any packets because the oldest packet is newer than
+ // the indicated horizon_samples.
+ buffer.DiscardOldPackets(start_ts + kTotalPackets * ts_increment,
+ kRemainingPackets * ts_increment, &mock_stats);
+ uint32_t ts;
+ EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&ts));
+ EXPECT_EQ(current_ts, ts);
+
+ // Discard all remaining packets.
+ EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(kRemainingPackets);
+ buffer.DiscardAllOldPackets(start_ts + kTotalPackets * ts_increment,
+ &mock_stats);
+
EXPECT_TRUE(buffer.Empty());
}
@@ -452,8 +474,12 @@
buffer->NextHigherTimestamp(0, &temp_ts));
EXPECT_EQ(NULL, buffer->PeekNextPacket());
EXPECT_FALSE(buffer->GetNextPacket());
- EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket());
- EXPECT_EQ(0, buffer->DiscardAllOldPackets(0)); // 0 packets discarded.
+
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ // Discarding packets will not invoke mock_stats.PacketDiscarded() because the
+ // packet buffer is empty.
+ EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket(&mock_stats));
+ buffer->DiscardAllOldPackets(0, &mock_stats);
// Insert one packet to make the buffer non-empty.
EXPECT_EQ(PacketBuffer::kOK,
diff --git a/webrtc/modules/audio_coding/neteq/statistics_calculator.h b/webrtc/modules/audio_coding/neteq/statistics_calculator.h
index fb869ab..320a74a 100644
--- a/webrtc/modules/audio_coding/neteq/statistics_calculator.h
+++ b/webrtc/modules/audio_coding/neteq/statistics_calculator.h
@@ -64,7 +64,7 @@
void AddZeros(size_t num_samples);
// Reports that |num_packets| packets were discarded.
- void PacketsDiscarded(size_t num_packets);
+ virtual void PacketsDiscarded(size_t num_packets);
// Reports that |num_samples| were lost.
void LostSamples(size_t num_samples);