From 39b934ba2ea23bac7d888f7da9bbb03c68fb87c1 Mon Sep 17 00:00:00 2001 From: Jakob Ivarsson Date: Thu, 10 Jan 2019 10:28:23 +0100 Subject: [PATCH] Add NetEq config flag that enables RTX handling. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When enabled, the delay manager is updated with reordered packets. It also makes the peak detector ignore the reordered packets. Change-Id: I2bdc99764cc76b15e613ed3dc75f83aaf66eee4e Bug: webrtc:10178 Reviewed-on: https://webrtc-review.googlesource.com/c/116481 Commit-Queue: Jakob Ivarsson‎ Reviewed-by: Minyue Li Cr-Commit-Position: refs/heads/master@{#26187} --- .../neteq/decision_logic_unittest.cc | 4 +- modules/audio_coding/neteq/delay_manager.cc | 9 ++-- modules/audio_coding/neteq/delay_manager.h | 2 +- .../neteq/delay_manager_unittest.cc | 32 +++++++++----- .../audio_coding/neteq/delay_peak_detector.cc | 11 ++++- .../audio_coding/neteq/delay_peak_detector.h | 12 +++--- .../neteq/delay_peak_detector_unittest.cc | 42 +++++++++++++------ modules/audio_coding/neteq/include/neteq.h | 1 + .../neteq/mock/mock_delay_manager.h | 2 +- .../neteq/mock/mock_delay_peak_detector.h | 8 ++-- modules/audio_coding/neteq/neteq.cc | 7 ++-- modules/audio_coding/neteq/neteq_impl.cc | 12 ++++-- modules/audio_coding/neteq/neteq_impl.h | 1 + .../audio_coding/neteq/neteq_impl_unittest.cc | 39 ++++++++++++++++- 14 files changed, 135 insertions(+), 47 deletions(-) diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc index 183b9c79c9..e54c213e58 100644 --- a/modules/audio_coding/neteq/decision_logic_unittest.cc +++ b/modules/audio_coding/neteq/decision_logic_unittest.cc @@ -30,7 +30,7 @@ TEST(DecisionLogic, CreateAndDestroy) { new rtc::RefCountedObject, absl::nullopt); TickTimer tick_timer; PacketBuffer packet_buffer(10, &tick_timer); - DelayPeakDetector delay_peak_detector(&tick_timer); + DelayPeakDetector delay_peak_detector(&tick_timer, false); DelayManager delay_manager(240, 0, &delay_peak_detector, &tick_timer); BufferLevelFilter buffer_level_filter; DecisionLogic* logic = DecisionLogic::Create( @@ -47,7 +47,7 @@ TEST(DecisionLogic, PostponeDecodingAfterExpansionSettings) { new rtc::RefCountedObject, absl::nullopt); TickTimer tick_timer; PacketBuffer packet_buffer(10, &tick_timer); - DelayPeakDetector delay_peak_detector(&tick_timer); + DelayPeakDetector delay_peak_detector(&tick_timer, false); DelayManager delay_manager(240, 0, &delay_peak_detector, &tick_timer); BufferLevelFilter buffer_level_filter; { diff --git a/modules/audio_coding/neteq/delay_manager.cc b/modules/audio_coding/neteq/delay_manager.cc index 67e6a13c1d..cbf3da1a68 100644 --- a/modules/audio_coding/neteq/delay_manager.cc +++ b/modules/audio_coding/neteq/delay_manager.cc @@ -158,6 +158,7 @@ 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 @@ -166,6 +167,7 @@ int DelayManager::Update(uint16_t sequence_number, iat_packets = std::max(iat_packets, 0); } else if (!IsNewerSequenceNumber(sequence_number, last_seq_no_)) { iat_packets += static_cast(last_seq_no_ + 1 - sequence_number); + reordered = true; } // Saturate IAT at maximum value. @@ -173,7 +175,7 @@ int DelayManager::Update(uint16_t sequence_number, iat_packets = std::min(iat_packets, max_iat); UpdateHistogram(iat_packets); // Calculate new |target_level_| based on updated statistics. - target_level_ = CalculateTargetLevel(iat_packets); + target_level_ = CalculateTargetLevel(iat_packets, reordered); if (streaming_mode_) { target_level_ = std::max(target_level_, max_iat_cumulative_sum_); } @@ -294,7 +296,7 @@ void DelayManager::LimitTargetLevel() { target_level_ = std::max(target_level_, 1 << 8); } -int DelayManager::CalculateTargetLevel(int iat_packets) { +int DelayManager::CalculateTargetLevel(int iat_packets, bool reordered) { int limit_probability = forced_limit_probability_.value_or(kLimitProbability); if (streaming_mode_) { limit_probability = kLimitProbabilityStreaming; @@ -325,7 +327,8 @@ int DelayManager::CalculateTargetLevel(int iat_packets) { base_target_level_ = static_cast(index); // Update detector for delay peaks. - bool delay_peak_found = peak_detector_.Update(iat_packets, target_level); + bool delay_peak_found = + peak_detector_.Update(iat_packets, reordered, target_level); if (delay_peak_found) { target_level = std::max(target_level, peak_detector_.MaxPeakHeight()); } diff --git a/modules/audio_coding/neteq/delay_manager.h b/modules/audio_coding/neteq/delay_manager.h index 2c8081b075..ce05bcc6f2 100644 --- a/modules/audio_coding/neteq/delay_manager.h +++ b/modules/audio_coding/neteq/delay_manager.h @@ -57,7 +57,7 @@ class DelayManager { // Sets target_level_ (in Q8) and returns the same value. Also calculates // and updates base_target_level_, which is the target buffer level before // taking delay peaks into account. - virtual int CalculateTargetLevel(int iat_packets); + virtual int CalculateTargetLevel(int iat_packets, bool reordered); // Notifies the DelayManager of how much audio data is carried in each packet. // The method updates the DelayPeakDetector too, and resets the inter-arrival diff --git a/modules/audio_coding/neteq/delay_manager_unittest.cc b/modules/audio_coding/neteq/delay_manager_unittest.cc index 6281a15679..e1cca89ffc 100644 --- a/modules/audio_coding/neteq/delay_manager_unittest.cc +++ b/modules/audio_coding/neteq/delay_manager_unittest.cc @@ -49,7 +49,10 @@ class DelayManagerTest : public ::testing::Test { }; DelayManagerTest::DelayManagerTest() - : dm_(nullptr), detector_(&tick_timer_), seq_no_(0x1234), ts_(0x12345678) {} + : dm_(nullptr), + detector_(&tick_timer_, false), + seq_no_(0x1234), + ts_(0x12345678) {} void DelayManagerTest::SetUp() { RecreateDelayManager(); @@ -126,7 +129,7 @@ TEST_F(DelayManagerTest, UpdateNormal) { // Expect detector update method to be called once with inter-arrival time // equal to 1 packet, and (base) target level equal to 1 as well. // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(1, 1)).WillOnce(Return(false)); + EXPECT_CALL(detector_, Update(1, false, 1)).WillOnce(Return(false)); InsertNextPacket(); EXPECT_EQ(1 << 8, dm_->TargetLevel()); // In Q8. EXPECT_EQ(1, dm_->base_target_level()); @@ -149,7 +152,7 @@ TEST_F(DelayManagerTest, UpdateLongInterArrivalTime) { // Expect detector update method to be called once with inter-arrival time // equal to 1 packet, and (base) target level equal to 1 as well. // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(2, 2)).WillOnce(Return(false)); + EXPECT_CALL(detector_, Update(2, false, 2)).WillOnce(Return(false)); InsertNextPacket(); EXPECT_EQ(2 << 8, dm_->TargetLevel()); // In Q8. EXPECT_EQ(2, dm_->base_target_level()); @@ -172,7 +175,7 @@ TEST_F(DelayManagerTest, UpdatePeakFound) { // Expect detector update method to be called once with inter-arrival time // equal to 1 packet, and (base) target level equal to 1 as well. // Return true to indicate that peaks are found. Let the peak height be 5. - EXPECT_CALL(detector_, Update(1, 1)).WillOnce(Return(true)); + EXPECT_CALL(detector_, Update(1, false, 1)).WillOnce(Return(true)); EXPECT_CALL(detector_, MaxPeakHeight()).WillOnce(Return(5)); InsertNextPacket(); EXPECT_EQ(5 << 8, dm_->TargetLevel()); @@ -194,7 +197,7 @@ TEST_F(DelayManagerTest, TargetDelay) { // Expect detector update method to be called once with inter-arrival time // equal to 1 packet, and (base) target level equal to 1 as well. // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(1, 1)).WillOnce(Return(false)); + EXPECT_CALL(detector_, Update(1, false, 1)).WillOnce(Return(false)); InsertNextPacket(); const int kExpectedTarget = 1; EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); // In Q8. @@ -216,7 +219,7 @@ TEST_F(DelayManagerTest, MaxDelay) { // Second packet arrival. // Expect detector update method to be called once with inter-arrival time // equal to |kExpectedTarget| packet. Return true to indicate peaks found. - EXPECT_CALL(detector_, Update(kExpectedTarget, _)) + EXPECT_CALL(detector_, Update(kExpectedTarget, false, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(detector_, MaxPeakHeight()) .WillRepeatedly(Return(kExpectedTarget)); @@ -246,7 +249,7 @@ TEST_F(DelayManagerTest, MinDelay) { // Second packet arrival. // Expect detector update method to be called once with inter-arrival time // equal to |kExpectedTarget| packet. Return true to indicate peaks found. - EXPECT_CALL(detector_, Update(kExpectedTarget, _)) + EXPECT_CALL(detector_, Update(kExpectedTarget, false, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(detector_, MaxPeakHeight()) .WillRepeatedly(Return(kExpectedTarget)); @@ -264,6 +267,15 @@ TEST_F(DelayManagerTest, MinDelay) { EXPECT_EQ(kMinDelayPackets << 8, dm_->TargetLevel()); } +TEST_F(DelayManagerTest, UpdateReorderedPacket) { + SetPacketAudioLength(kFrameSizeMs); + InsertNextPacket(); + + // Insert packet that was sent before the previous packet. + EXPECT_CALL(detector_, Update(_, true, _)); + EXPECT_EQ(0, dm_->Update(seq_no_ - 1, ts_ - kFrameSizeMs, kFs)); +} + // Tests that skipped sequence numbers (simulating empty packets) are handled // correctly. TEST_F(DelayManagerTest, EmptyPacketsReported) { @@ -285,7 +297,7 @@ TEST_F(DelayManagerTest, EmptyPacketsReported) { // Expect detector update method to be called once with inter-arrival time // equal to 1 packet, and (base) target level equal to 1 as well. // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(1, 1)).WillOnce(Return(false)); + EXPECT_CALL(detector_, Update(1, false, 1)).WillOnce(Return(false)); InsertNextPacket(); EXPECT_EQ(1 << 8, dm_->TargetLevel()); // In Q8. @@ -309,7 +321,7 @@ TEST_F(DelayManagerTest, EmptyPacketsNotReported) { // Expect detector update method to be called once with inter-arrival time // equal to 1 packet, and (base) target level equal to 1 as well. // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(10, 10)).WillOnce(Return(false)); + EXPECT_CALL(detector_, Update(10, false, 10)).WillOnce(Return(false)); InsertNextPacket(); // Note 10 times higher target value. @@ -348,7 +360,7 @@ TEST_F(DelayManagerTest, TargetDelayGreaterThanOne) { // Second packet arrival. // Expect detector update method to be called once with inter-arrival time // equal to 1 packet. - EXPECT_CALL(detector_, Update(1, 1)).WillOnce(Return(false)); + EXPECT_CALL(detector_, Update(1, false, 1)).WillOnce(Return(false)); InsertNextPacket(); constexpr int kExpectedTarget = 1; EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); // In Q8. diff --git a/modules/audio_coding/neteq/delay_peak_detector.cc b/modules/audio_coding/neteq/delay_peak_detector.cc index 893ce3e8fd..5669d72960 100644 --- a/modules/audio_coding/neteq/delay_peak_detector.cc +++ b/modules/audio_coding/neteq/delay_peak_detector.cc @@ -26,10 +26,12 @@ namespace webrtc { DelayPeakDetector::~DelayPeakDetector() = default; -DelayPeakDetector::DelayPeakDetector(const TickTimer* tick_timer) +DelayPeakDetector::DelayPeakDetector(const TickTimer* tick_timer, + bool ignore_reordered_packets) : peak_found_(false), peak_detection_threshold_(0), tick_timer_(tick_timer), + ignore_reordered_packets_(ignore_reordered_packets), frame_length_change_experiment_( field_trial::IsEnabled("WebRTC-Audio-NetEqFramelengthExperiment")) { RTC_DCHECK(!peak_period_stopwatch_); @@ -79,7 +81,12 @@ uint64_t DelayPeakDetector::MaxPeakPeriod() const { return max_period_element->period_ms; } -bool DelayPeakDetector::Update(int inter_arrival_time, int target_level) { +bool DelayPeakDetector::Update(int inter_arrival_time, + bool reordered, + int target_level) { + if (ignore_reordered_packets_ && reordered) { + return CheckPeakConditions(); + } if (inter_arrival_time > target_level + peak_detection_threshold_ || inter_arrival_time > 2 * target_level) { // A delay peak is observed. diff --git a/modules/audio_coding/neteq/delay_peak_detector.h b/modules/audio_coding/neteq/delay_peak_detector.h index 272d50ed50..5c741178d2 100644 --- a/modules/audio_coding/neteq/delay_peak_detector.h +++ b/modules/audio_coding/neteq/delay_peak_detector.h @@ -23,7 +23,7 @@ namespace webrtc { class DelayPeakDetector { public: - DelayPeakDetector(const TickTimer* tick_timer); + DelayPeakDetector(const TickTimer* tick_timer, bool ignore_reordered_packets); virtual ~DelayPeakDetector(); virtual void Reset(); @@ -43,10 +43,11 @@ class DelayPeakDetector { // larger than 0), or 0 if no delay peaks have been observed recently. virtual uint64_t MaxPeakPeriod() const; - // Updates the DelayPeakDetector with a new inter-arrival time (in packets) - // and the current target buffer level (needed to decide if a peak is observed - // or not). Returns true if peak-mode is active, false if not. - virtual bool Update(int inter_arrival_time, int target_level); + // Updates the DelayPeakDetector with a new inter-arrival time (in packets), + // the current target buffer level (needed to decide if a peak is observed or + // not) and if the new inter-arrival time includes a compensation for + // reordering. Returns true if peak-mode is active, false if not. + virtual bool Update(int inter_arrival_time, bool reordered, int target_level); private: static const size_t kMaxNumPeaks = 8; @@ -66,6 +67,7 @@ class DelayPeakDetector { int peak_detection_threshold_; const TickTimer* tick_timer_; std::unique_ptr peak_period_stopwatch_; + const bool ignore_reordered_packets_; const bool frame_length_change_experiment_; RTC_DISALLOW_COPY_AND_ASSIGN(DelayPeakDetector); diff --git a/modules/audio_coding/neteq/delay_peak_detector_unittest.cc b/modules/audio_coding/neteq/delay_peak_detector_unittest.cc index fd4dded608..6590dc281e 100644 --- a/modules/audio_coding/neteq/delay_peak_detector_unittest.cc +++ b/modules/audio_coding/neteq/delay_peak_detector_unittest.cc @@ -18,14 +18,14 @@ namespace webrtc { TEST(DelayPeakDetector, CreateAndDestroy) { TickTimer tick_timer; - DelayPeakDetector* detector = new DelayPeakDetector(&tick_timer); + DelayPeakDetector* detector = new DelayPeakDetector(&tick_timer, false); EXPECT_FALSE(detector->peak_found()); delete detector; } TEST(DelayPeakDetector, EmptyHistory) { TickTimer tick_timer; - DelayPeakDetector detector(&tick_timer); + DelayPeakDetector detector(&tick_timer, false); EXPECT_EQ(-1, detector.MaxPeakHeight()); EXPECT_EQ(0u, detector.MaxPeakPeriod()); } @@ -35,7 +35,7 @@ TEST(DelayPeakDetector, EmptyHistory) { // start. This should then continue until it is disengaged due to lack of peaks. TEST(DelayPeakDetector, TriggerPeakMode) { TickTimer tick_timer; - DelayPeakDetector detector(&tick_timer); + DelayPeakDetector detector(&tick_timer, false); const int kPacketSizeMs = 30; detector.SetPacketAudioLength(kPacketSizeMs); @@ -69,9 +69,9 @@ TEST(DelayPeakDetector, TriggerPeakMode) { (arrival_times_ms[next] - arrival_times_ms[next - 1]) / kPacketSizeMs; const int kTargetBufferLevel = 1; // Define peaks to be iat > 2. if (time < peak_mode_start_ms || time > peak_mode_end_ms) { - EXPECT_FALSE(detector.Update(iat_packets, kTargetBufferLevel)); + EXPECT_FALSE(detector.Update(iat_packets, false, kTargetBufferLevel)); } else { - EXPECT_TRUE(detector.Update(iat_packets, kTargetBufferLevel)); + EXPECT_TRUE(detector.Update(iat_packets, false, kTargetBufferLevel)); EXPECT_EQ(kWorstPeakPeriod, detector.MaxPeakPeriod()); EXPECT_EQ(kPeakDelayMs / kPacketSizeMs + 1, detector.MaxPeakHeight()); } @@ -87,7 +87,7 @@ TEST(DelayPeakDetector, TriggerPeakMode) { // The delay pattern has peaks with delay = 3, thus should not trigger. TEST(DelayPeakDetector, DoNotTriggerPeakMode) { TickTimer tick_timer; - DelayPeakDetector detector(&tick_timer); + DelayPeakDetector detector(&tick_timer, false); const int kPacketSizeMs = 30; detector.SetPacketAudioLength(kPacketSizeMs); @@ -115,7 +115,7 @@ TEST(DelayPeakDetector, DoNotTriggerPeakMode) { int iat_packets = (arrival_times_ms[next] - arrival_times_ms[next - 1]) / kPacketSizeMs; const int kTargetBufferLevel = 2; // Define peaks to be iat > 4. - EXPECT_FALSE(detector.Update(iat_packets, kTargetBufferLevel)); + EXPECT_FALSE(detector.Update(iat_packets, false, kTargetBufferLevel)); ++next; } tick_timer.Increment(); @@ -129,15 +129,33 @@ TEST(DelayPeakDetector, DoNotTriggerPeakMode) { // problems. TEST(DelayPeakDetector, ZeroDistancePeaks) { TickTimer tick_timer; - DelayPeakDetector detector(&tick_timer); + DelayPeakDetector detector(&tick_timer, false); const int kPacketSizeMs = 30; detector.SetPacketAudioLength(kPacketSizeMs); const int kTargetBufferLevel = 2; // Define peaks to be iat > 4. - const int kInterArrivalTime = 3 * kTargetBufferLevel; // Will trigger a peak. - EXPECT_FALSE(detector.Update(kInterArrivalTime, kTargetBufferLevel)); - EXPECT_FALSE(detector.Update(kInterArrivalTime, kTargetBufferLevel)); - EXPECT_FALSE(detector.Update(kInterArrivalTime, kTargetBufferLevel)); + const int kInterArrivalTime = + 3 * kTargetBufferLevel; // Above peak threshold. + EXPECT_FALSE(detector.Update(kInterArrivalTime, false, kTargetBufferLevel)); + tick_timer.Increment(); + EXPECT_FALSE(detector.Update(kInterArrivalTime, false, kTargetBufferLevel)); + // The following would fail if there were non-zero time between the updates. + EXPECT_FALSE(detector.Update(kInterArrivalTime, false, kTargetBufferLevel)); +} + +TEST(DelayPeakDetector, IgnoreReorderedPacket) { + TickTimer tick_timer; + DelayPeakDetector detector(&tick_timer, true); + + const int kTargetBufferLevel = 2; // Define peaks to be iat > 4. + const int kInterArrivalTime = + 3 * kTargetBufferLevel; // Above peak threshold. + EXPECT_FALSE(detector.Update(kInterArrivalTime, false, kTargetBufferLevel)); + tick_timer.Increment(); + EXPECT_FALSE(detector.Update(kInterArrivalTime, false, kTargetBufferLevel)); + tick_timer.Increment(); + // The following would fail if the packet was not reordered. + EXPECT_FALSE(detector.Update(kInterArrivalTime, true, kTargetBufferLevel)); } } // namespace webrtc diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h index bc4490d56b..70bedfbd0c 100644 --- a/modules/audio_coding/neteq/include/neteq.h +++ b/modules/audio_coding/neteq/include/neteq.h @@ -117,6 +117,7 @@ class NetEq { int min_delay_ms = 0; bool enable_fast_accelerate = false; bool enable_muted_state = false; + bool enable_rtx_handling = false; absl::optional codec_pair_id; bool for_test_no_time_stretching = false; // Use only for testing. }; diff --git a/modules/audio_coding/neteq/mock/mock_delay_manager.h b/modules/audio_coding/neteq/mock/mock_delay_manager.h index 206cea7f10..1671741bb2 100644 --- a/modules/audio_coding/neteq/mock/mock_delay_manager.h +++ b/modules/audio_coding/neteq/mock/mock_delay_manager.h @@ -34,7 +34,7 @@ class MockDelayManager : public DelayManager { int(uint16_t sequence_number, uint32_t timestamp, int sample_rate_hz)); - MOCK_METHOD1(CalculateTargetLevel, int(int iat_packets)); + MOCK_METHOD2(CalculateTargetLevel, int(int iat_packets, bool reordered)); MOCK_METHOD1(SetPacketAudioLength, int(int length_ms)); MOCK_METHOD0(Reset, void()); MOCK_CONST_METHOD0(PeakFound, bool()); diff --git a/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h b/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h index f6cdea04f1..f7f0465713 100644 --- a/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h +++ b/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h @@ -19,8 +19,9 @@ namespace webrtc { class MockDelayPeakDetector : public DelayPeakDetector { public: - MockDelayPeakDetector(const TickTimer* tick_timer) - : DelayPeakDetector(tick_timer) {} + MockDelayPeakDetector(const TickTimer* tick_timer, + bool ignore_reordered_packets) + : DelayPeakDetector(tick_timer, ignore_reordered_packets) {} virtual ~MockDelayPeakDetector() { Die(); } MOCK_METHOD0(Die, void()); MOCK_METHOD0(Reset, void()); @@ -28,7 +29,8 @@ class MockDelayPeakDetector : public DelayPeakDetector { MOCK_METHOD0(peak_found, bool()); MOCK_CONST_METHOD0(MaxPeakHeight, int()); MOCK_CONST_METHOD0(MaxPeakPeriod, uint64_t()); - MOCK_METHOD2(Update, bool(int inter_arrival_time, int target_level)); + MOCK_METHOD3(Update, + bool(int inter_arrival_time, bool reordered, int target_level)); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/neteq.cc b/modules/audio_coding/neteq/neteq.cc index 0e6147e232..a84c94280a 100644 --- a/modules/audio_coding/neteq/neteq.cc +++ b/modules/audio_coding/neteq/neteq.cc @@ -28,9 +28,10 @@ std::string NetEq::Config::ToString() const { ss << "sample_rate_hz=" << sample_rate_hz << ", enable_post_decode_vad=" << (enable_post_decode_vad ? "true" : "false") << ", max_packets_in_buffer=" << max_packets_in_buffer - << ", enable_fast_accelerate=" - << (enable_fast_accelerate ? " true" : "false") - << ", enable_muted_state=" << (enable_muted_state ? " true" : "false"); + << ", min_delay_ms=" << min_delay_ms << ", enable_fast_accelerate=" + << (enable_fast_accelerate ? "true" : "false") + << ", enable_muted_state=" << (enable_muted_state ? "true" : "false") + << ", enable_rtx_handling=" << (enable_rtx_handling ? "true" : "false"); return ss.str(); } diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index e1b3bc1270..00c9172a71 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -61,7 +61,8 @@ NetEqImpl::Dependencies::Dependencies( buffer_level_filter(new BufferLevelFilter), decoder_database( new DecoderDatabase(decoder_factory, config.codec_pair_id)), - delay_peak_detector(new DelayPeakDetector(tick_timer.get())), + delay_peak_detector( + new DelayPeakDetector(tick_timer.get(), config.enable_rtx_handling)), delay_manager(new DelayManager(config.max_packets_in_buffer, config.min_delay_ms, delay_peak_detector.get(), @@ -112,7 +113,8 @@ NetEqImpl::NetEqImpl(const NetEq::Config& config, speech_expand_uma_logger_("WebRTC.Audio.SpeechExpandRatePercent", 10, // Report once every 10 s. tick_timer_.get()), - no_time_stretching_(config.for_test_no_time_stretching) { + no_time_stretching_(config.for_test_no_time_stretching), + enable_rtx_handling_(config.enable_rtx_handling) { RTC_LOG(LS_INFO) << "NetEq config: " << config.ToString(); int fs = config.sample_rate_hz; if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) { @@ -737,9 +739,11 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header, } // Update statistics. - if ((int32_t)(main_timestamp - timestamp_) >= 0 && !new_codec_) { + if ((enable_rtx_handling_ || (int32_t)(main_timestamp - timestamp_) >= 0) && + !new_codec_) { // Only update statistics if incoming packet is not older than last played - // out packet, and if new codec flag is not set. + // out packet or RTX handling is enabled, and if new codec flag is not + // set. delay_manager_->Update(main_sequence_number, main_timestamp, fs_hz_); } } else if (delay_manager_->last_pack_cng_or_dtmf() == -1) { diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h index 1c730d7c8a..e695c6ffbd 100644 --- a/modules/audio_coding/neteq/neteq_impl.h +++ b/modules/audio_coding/neteq/neteq_impl.h @@ -408,6 +408,7 @@ class NetEqImpl : public webrtc::NetEq { ExpandUmaLogger speech_expand_uma_logger_ RTC_GUARDED_BY(crit_sect_); bool no_time_stretching_ RTC_GUARDED_BY(crit_sect_); // Only used for test. rtc::BufferT concealment_audio_ RTC_GUARDED_BY(crit_sect_); + const bool enable_rtx_handling_ RTC_GUARDED_BY(crit_sect_); private: RTC_DISALLOW_COPY_AND_ASSIGN(NetEqImpl); diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc index 15ec1f4191..43b134b9af 100644 --- a/modules/audio_coding/neteq/neteq_impl_unittest.cc +++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc @@ -87,7 +87,7 @@ class NetEqImplTest : public ::testing::Test { if (use_mock_delay_peak_detector_) { std::unique_ptr mock( - new MockDelayPeakDetector(tick_timer_)); + new MockDelayPeakDetector(tick_timer_, config_.enable_rtx_handling)); mock_delay_peak_detector_ = mock.get(); EXPECT_CALL(*mock_delay_peak_detector_, Reset()).Times(1); deps.delay_peak_detector = std::move(mock); @@ -1301,6 +1301,43 @@ TEST_F(NetEqImplTest, InsertEmptyPacket) { neteq_->InsertEmptyPacket(rtp_header); } +TEST_F(NetEqImplTest, EnableRtxHandling) { + UseNoMocks(); + use_mock_delay_manager_ = true; + config_.enable_rtx_handling = true; + CreateInstance(); + EXPECT_CALL(*mock_delay_manager_, BufferLimits(_, _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<0>(0), SetArgPointee<1>(0))); + + const int kPayloadLengthSamples = 80; + const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples; // PCM 16-bit. + const uint8_t kPayloadType = 17; // Just an arbitrary number. + const uint32_t kReceiveTime = 17; + uint8_t payload[kPayloadLengthBytes] = {0}; + RTPHeader rtp_header; + rtp_header.payloadType = kPayloadType; + rtp_header.sequenceNumber = 0x1234; + rtp_header.timestamp = 0x12345678; + rtp_header.ssrc = 0x87654321; + + EXPECT_EQ(NetEq::kOK, neteq_->RegisterPayloadType( + NetEqDecoder::kDecoderPCM16B, "", kPayloadType)); + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket(rtp_header, payload, kReceiveTime)); + AudioFrame output; + bool muted; + EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); + + // Insert second packet that was sent before the first packet. + rtp_header.sequenceNumber -= 1; + rtp_header.timestamp -= kPayloadLengthSamples; + EXPECT_CALL(*mock_delay_manager_, + Update(rtp_header.sequenceNumber, rtp_header.timestamp, _)); + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket(rtp_header, payload, kReceiveTime)); +} + class Decoder120ms : public AudioDecoder { public: Decoder120ms(int sample_rate_hz, SpeechType speech_type)