diff --git a/call/BUILD.gn b/call/BUILD.gn index 611d9e8adb..b06dbbe696 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -217,6 +217,7 @@ rtc_static_library("call") { "../rtc_base:rtc_task_queue", "../rtc_base:safe_minmax", "../rtc_base:sequenced_task_checker", + "../rtc_base/experiments:field_trial_parser", "../rtc_base/synchronization:rw_lock_wrapper", "../system_wrappers", "../system_wrappers:field_trial", diff --git a/call/call.cc b/call/call.cc index 635874b2d6..fa20679221 100644 --- a/call/call.cc +++ b/call/call.cc @@ -56,6 +56,7 @@ #include "rtc_base/synchronization/rw_lock_wrapper.h" #include "rtc_base/task_queue.h" #include "rtc_base/thread_annotations.h" +#include "rtc_base/timeutils.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/cpu_info.h" @@ -1215,8 +1216,10 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type, if (packet_time_us != -1) { if (receive_time_calculator_) { + int64_t system_time_us = + rtc::SystemTimeNanos() / rtc::kNumNanosecsPerMicrosec; packet_time_us = receive_time_calculator_->ReconcileReceiveTimes( - packet_time_us, clock_->TimeInMicroseconds()); + packet_time_us, system_time_us, clock_->TimeInMicroseconds()); } parsed_packet.set_arrival_time_ms((packet_time_us + 500) / 1000); } else { diff --git a/call/receive_time_calculator.cc b/call/receive_time_calculator.cc index 16c6a43cc3..b7056732e8 100644 --- a/call/receive_time_calculator.cc +++ b/call/receive_time_calculator.cc @@ -9,59 +9,112 @@ */ #include "call/receive_time_calculator.h" + +#include + #include "absl/memory/memory.h" +#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" #include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { -using ::webrtc::field_trial::FindFullName; using ::webrtc::field_trial::IsEnabled; -const char kBweReceiveTimeCorrection[] = "WebRTC-BweReceiveTimeCorrection"; +const char kBweReceiveTimeCorrection[] = "WebRTC-Bwe-ReceiveTimeFix"; } // namespace -ReceiveTimeCalculator::ReceiveTimeCalculator(int64_t min_delta_ms, - int64_t max_delta_diff_ms) - : min_delta_us_(min_delta_ms * 1000), - max_delta_diff_us_(max_delta_diff_ms * 1000) {} +ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig() + : max_packet_time_repair("maxrep", TimeDelta::ms(2000)), + stall_threshold("stall", TimeDelta::ms(5)), + tolerance("tol", TimeDelta::ms(1)), + max_stall("maxstall", TimeDelta::seconds(5)) { + std::string trial_string = + field_trial::FindFullName(kBweReceiveTimeCorrection); + ParseFieldTrial( + {&max_packet_time_repair, &stall_threshold, &tolerance, &max_stall}, + trial_string); +} +ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig( + const ReceiveTimeCalculatorConfig&) = default; +ReceiveTimeCalculatorConfig::~ReceiveTimeCalculatorConfig() = default; + +ReceiveTimeCalculator::ReceiveTimeCalculator() + : config_(ReceiveTimeCalculatorConfig()) {} std::unique_ptr ReceiveTimeCalculator::CreateFromFieldTrial() { if (!IsEnabled(kBweReceiveTimeCorrection)) return nullptr; - int min, max; - if (sscanf(FindFullName(kBweReceiveTimeCorrection).c_str(), "Enabled,%d,%d", - &min, &max) != 2) { - RTC_LOG(LS_WARNING) << "Invalid number of parameters provided."; - return nullptr; - } - return absl::make_unique(min, max); + return absl::make_unique(); } -int64_t ReceiveTimeCalculator::ReconcileReceiveTimes(int64_t packet_time_us_, - int64_t safe_time_us_) { - if (!receive_time_offset_us_) { - receive_time_offset_us_ = safe_time_us_ - packet_time_us_; - } else { - int64_t safe_delta_us = safe_time_us_ - last_safe_time_us_; - int64_t packet_delta_us_ = packet_time_us_ - last_packet_time_us_; - int64_t delta_diff = packet_delta_us_ - safe_delta_us; - // Packet time should not decrease significantly, a large decrease indicates - // a reset of the packet time clock and we should reset the offest - // parameter. The safe reference time can increase in large jumps if the - // thread measuring it is backgrounded for longer periods. But if the packet - // time increases significantly more than the safe time, it indicates a - // clock reset and we should reset the offset. +int64_t ReceiveTimeCalculator::ReconcileReceiveTimes(int64_t packet_time_us, + int64_t system_time_us, + int64_t safe_time_us) { + int64_t stall_time_us = system_time_us - packet_time_us; + if (total_system_time_passed_us_ < config_.stall_threshold->us()) { + stall_time_us = rtc::SafeMin(stall_time_us, config_.max_stall->us()); + } + int64_t corrected_time_us = safe_time_us - stall_time_us; - if (packet_delta_us_ < min_delta_us_ || delta_diff > max_delta_diff_us_) { - RTC_LOG(LS_WARNING) << "Received a clock jump of " << delta_diff - << " resetting offset"; - receive_time_offset_us_ = safe_time_us_ - packet_time_us_; + if (last_packet_time_us_ == -1 && stall_time_us < 0) { + static_clock_offset_us_ = stall_time_us; + corrected_time_us += static_clock_offset_us_; + } else if (last_packet_time_us_ > 0) { + // All repairs depend on variables being intialized + int64_t packet_time_delta_us = packet_time_us - last_packet_time_us_; + int64_t system_time_delta_us = system_time_us - last_system_time_us_; + int64_t safe_time_delta_us = safe_time_us - last_safe_time_us_; + + // Repair backwards clock resets during initial stall. In this case, the + // reset is observed only in packet time but never in system time. + if (system_time_delta_us < 0) + total_system_time_passed_us_ += config_.stall_threshold->us(); + else + total_system_time_passed_us_ += system_time_delta_us; + if (packet_time_delta_us < 0 && + total_system_time_passed_us_ < config_.stall_threshold->us()) { + static_clock_offset_us_ -= packet_time_delta_us; + } + corrected_time_us += static_clock_offset_us_; + + // Detect resets inbetween clock readings in socket and app. + bool forward_clock_reset = + corrected_time_us + config_.tolerance->us() < last_corrected_time_us_; + bool obvious_backward_clock_reset = system_time_us < packet_time_us; + + // Harder case with backward clock reset during stall, the reset being + // smaller than the stall. Compensate throughout the stall. + bool small_backward_clock_reset = + !obvious_backward_clock_reset && + safe_time_delta_us > system_time_delta_us + config_.tolerance->us(); + bool stall_start = + packet_time_delta_us >= 0 && + system_time_delta_us > packet_time_delta_us + config_.tolerance->us(); + bool stall_is_over = safe_time_delta_us > config_.stall_threshold->us(); + bool packet_time_caught_up = + packet_time_delta_us < 0 && system_time_delta_us >= 0; + if (stall_start && small_backward_clock_reset) + small_reset_during_stall_ = true; + else if (stall_is_over || packet_time_caught_up) + small_reset_during_stall_ = false; + + // If resets are detected, advance time by (capped) packet time increase. + if (forward_clock_reset || obvious_backward_clock_reset || + small_reset_during_stall_) { + corrected_time_us = last_corrected_time_us_ + + rtc::SafeClamp(packet_time_delta_us, 0, + config_.max_packet_time_repair->us()); } } - last_packet_time_us_ = packet_time_us_; - last_safe_time_us_ = safe_time_us_; - return packet_time_us_ + *receive_time_offset_us_; + + last_corrected_time_us_ = corrected_time_us; + last_packet_time_us_ = packet_time_us; + last_system_time_us_ = system_time_us; + last_safe_time_us_ = safe_time_us; + return corrected_time_us; } + } // namespace webrtc diff --git a/call/receive_time_calculator.h b/call/receive_time_calculator.h index a8217cd101..269d4ef910 100644 --- a/call/receive_time_calculator.h +++ b/call/receive_time_calculator.h @@ -10,13 +10,24 @@ #ifndef CALL_RECEIVE_TIME_CALCULATOR_H_ #define CALL_RECEIVE_TIME_CALCULATOR_H_ -#include #include -#include "absl/types/optional.h" +#include "rtc_base/experiments/field_trial_units.h" namespace webrtc { +struct ReceiveTimeCalculatorConfig { + ReceiveTimeCalculatorConfig(); + ReceiveTimeCalculatorConfig(const ReceiveTimeCalculatorConfig&); + ReceiveTimeCalculatorConfig& operator=(const ReceiveTimeCalculatorConfig&) = + default; + ~ReceiveTimeCalculatorConfig(); + FieldTrialParameter max_packet_time_repair; + FieldTrialParameter stall_threshold; + FieldTrialParameter tolerance; + FieldTrialParameter max_stall; +}; + // The receive time calculator serves the purpose of combining packet time // stamps with a safely incremental clock. This assumes that the packet time // stamps are based on lower layer timestamps that have more accurate time @@ -28,20 +39,20 @@ namespace webrtc { class ReceiveTimeCalculator { public: static std::unique_ptr CreateFromFieldTrial(); - // The min delta is used to correct for jumps backwards in time, to allow some - // packet reordering a small negative value is appropriate to use. The max - // delta difference is used as margin when detecting when packet time - // increases more than the safe clock. This should be larger than the largest - // expected sysmtem induced delay in the safe clock timestamp. - ReceiveTimeCalculator(int64_t min_delta_ms, int64_t max_delta_diff_ms); - int64_t ReconcileReceiveTimes(int64_t packet_time_us_, int64_t safe_time_us_); + ReceiveTimeCalculator(); + int64_t ReconcileReceiveTimes(int64_t packet_time_us_, + int64_t system_time_us_, + int64_t safe_time_us_); private: - const int64_t min_delta_us_; - const int64_t max_delta_diff_us_; - absl::optional receive_time_offset_us_; - int64_t last_packet_time_us_ = 0; - int64_t last_safe_time_us_ = 0; + int64_t last_corrected_time_us_ = -1; + int64_t last_packet_time_us_ = -1; + int64_t last_system_time_us_ = -1; + int64_t last_safe_time_us_ = -1; + int64_t total_system_time_passed_us_ = 0; + int64_t static_clock_offset_us_ = 0; + int64_t small_reset_during_stall_ = false; + ReceiveTimeCalculatorConfig config_; }; } // namespace webrtc #endif // CALL_RECEIVE_TIME_CALCULATOR_H_ diff --git a/call/receive_time_calculator_unittest.cc b/call/receive_time_calculator_unittest.cc index 92d5a279ea..38ef54ece7 100644 --- a/call/receive_time_calculator_unittest.cc +++ b/call/receive_time_calculator_unittest.cc @@ -10,65 +10,237 @@ #include "call/receive_time_calculator.h" +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/types/optional.h" +#include "rtc_base/random.h" +#include "rtc_base/timeutils.h" #include "test/gtest.h" namespace webrtc { namespace test { namespace { -int64_t ReconcileMs(ReceiveTimeCalculator* calc, - int64_t packet_time_ms, - int64_t safe_time_ms) { - return calc->ReconcileReceiveTimes(packet_time_ms * 1000, - safe_time_ms * 1000) / - 1000; +class EmulatedClock { + public: + explicit EmulatedClock(int seed, float drift = 0.0f) + : random_(seed), clock_us_(random_.Rand()), drift_(drift) {} + virtual ~EmulatedClock() = default; + int64_t GetClockUs() const { return clock_us_; } + + protected: + int64_t UpdateClock(int64_t time_us) { + if (!last_query_us_) + last_query_us_ = time_us; + int64_t skip_us = time_us - *last_query_us_; + accumulated_drift_us_ += skip_us * drift_; + int64_t drift_correction_us = static_cast(accumulated_drift_us_); + accumulated_drift_us_ -= drift_correction_us; + clock_us_ += skip_us + drift_correction_us; + last_query_us_ = time_us; + return skip_us; + } + Random random_; + + private: + int64_t clock_us_; + absl::optional last_query_us_; + float drift_; + float accumulated_drift_us_ = 0; +}; + +class EmulatedMonotoneousClock : public EmulatedClock { + public: + explicit EmulatedMonotoneousClock(int seed) : EmulatedClock(seed) {} + ~EmulatedMonotoneousClock() = default; + + int64_t Query(int64_t time_us) { + int64_t skip_us = UpdateClock(time_us); + + // In a stall + if (stall_recovery_time_us_ > 0) { + if (GetClockUs() > stall_recovery_time_us_) { + stall_recovery_time_us_ = 0; + return GetClockUs(); + } else { + return stall_recovery_time_us_; + } + } + + // Check if we enter a stall + for (int k = 0; k < skip_us; ++k) { + if (random_.Rand() < kChanceOfStallPerUs) { + int64_t stall_duration_us = + static_cast(random_.Rand() * kMaxStallDurationUs); + stall_recovery_time_us_ = GetClockUs() + stall_duration_us; + return stall_recovery_time_us_; + } + } + return GetClockUs(); + } + + void ForceStallUs() { + int64_t stall_duration_us = + static_cast(random_.Rand() * kMaxStallDurationUs); + stall_recovery_time_us_ = GetClockUs() + stall_duration_us; + } + + bool Stalled() const { return stall_recovery_time_us_ > 0; } + + int64_t GetRemainingStall(int64_t time_us) const { + return stall_recovery_time_us_ > 0 ? stall_recovery_time_us_ - GetClockUs() + : 0; + } + + const int64_t kMaxStallDurationUs = rtc::kNumMicrosecsPerSec; + + private: + const float kChanceOfStallPerUs = 5e-6f; + int64_t stall_recovery_time_us_ = 0; +}; + +class EmulatedNonMonotoneousClock : public EmulatedClock { + public: + EmulatedNonMonotoneousClock(int seed, int64_t duration_us, float drift = 0) + : EmulatedClock(seed, drift) { + Pregenerate(duration_us); + } + ~EmulatedNonMonotoneousClock() = default; + + void Pregenerate(int64_t duration_us) { + int64_t time_since_reset_us = kMinTimeBetweenResetsUs; + int64_t clock_offset_us = 0; + for (int64_t time_us = 0; time_us < duration_us; time_us += kResolutionUs) { + int64_t skip_us = UpdateClock(time_us); + time_since_reset_us += skip_us; + int64_t reset_us = 0; + if (time_since_reset_us >= kMinTimeBetweenResetsUs) { + for (int k = 0; k < skip_us; ++k) { + if (random_.Rand() < kChanceOfResetPerUs) { + reset_us = static_cast(2 * random_.Rand() * + kMaxAbsResetUs) - + kMaxAbsResetUs; + clock_offset_us += reset_us; + time_since_reset_us = 0; + break; + } + } + } + pregenerated_clock_.emplace_back(GetClockUs() + clock_offset_us); + resets_us_.emplace_back(reset_us); + } + } + + int64_t Query(int64_t time_us) { + size_t ixStart = + (last_reset_query_time_us_ + (kResolutionUs >> 1)) / kResolutionUs + 1; + size_t ixEnd = (time_us + (kResolutionUs >> 1)) / kResolutionUs; + if (ixEnd >= pregenerated_clock_.size()) + return -1; + last_reset_size_us_ = 0; + for (size_t ix = ixStart; ix <= ixEnd; ++ix) { + if (resets_us_[ix] != 0) { + last_reset_size_us_ = resets_us_[ix]; + } + } + last_reset_query_time_us_ = time_us; + return pregenerated_clock_[ixEnd]; + } + + bool WasReset() const { return last_reset_size_us_ != 0; } + bool WasNegativeReset() const { return last_reset_size_us_ < 0; } + int64_t GetLastResetUs() const { return last_reset_size_us_; } + + private: + const float kChanceOfResetPerUs = 1e-6f; + const int64_t kMaxAbsResetUs = rtc::kNumMicrosecsPerSec; + const int64_t kMinTimeBetweenResetsUs = 3 * rtc::kNumMicrosecsPerSec; + const int64_t kResolutionUs = rtc::kNumMicrosecsPerMillisec; + int64_t last_reset_query_time_us_ = 0; + int64_t last_reset_size_us_ = 0; + std::vector pregenerated_clock_; + std::vector resets_us_; +}; + +TEST(ClockRepair, NoClockDrift) { + const int kSeeds = 10; + const int kFirstSeed = 1; + const int64_t kRuntimeUs = 10 * rtc::kNumMicrosecsPerSec; + const float kDrift = 0.0f; + const int64_t kMaxPacketInterarrivalUs = 50 * rtc::kNumMicrosecsPerMillisec; + for (int seed = kFirstSeed; seed < kSeeds + kFirstSeed; ++seed) { + EmulatedMonotoneousClock monotone_clock(seed); + EmulatedNonMonotoneousClock non_monotone_clock( + seed + 1, kRuntimeUs + rtc::kNumMicrosecsPerSec, kDrift); + ReceiveTimeCalculator reception_time_tracker; + int64_t corrected_clock_0 = 0; + int64_t reset_during_stall_tol_us = 0; + bool initial_clock_stall = true; + int64_t accumulated_upper_bound_tolerance_us = 0; + int64_t accumulated_lower_bound_tolerance_us = 0; + Random random(1); + monotone_clock.ForceStallUs(); + int64_t last_time_us = 0; + bool add_tolerance_on_next_packet = false; + int64_t monotone_noise_us = 1000; + + for (int64_t time_us = 0; time_us < kRuntimeUs; + time_us += static_cast(random.Rand() * + kMaxPacketInterarrivalUs)) { + int64_t socket_time_us = non_monotone_clock.Query(time_us); + int64_t monotone_us = monotone_clock.Query(time_us) + + 2 * random.Rand() * monotone_noise_us - + monotone_noise_us; + int64_t system_time_us = non_monotone_clock.Query( + time_us + monotone_clock.GetRemainingStall(time_us)); + + int64_t corrected_clock_us = reception_time_tracker.ReconcileReceiveTimes( + socket_time_us, system_time_us, monotone_us); + if (time_us == 0) + corrected_clock_0 = corrected_clock_us; + + if (add_tolerance_on_next_packet) + accumulated_lower_bound_tolerance_us -= (time_us - last_time_us); + + // Perfect repair cannot be achiveved if non-monotone clock resets during + // a monotone clock stall. + add_tolerance_on_next_packet = false; + if (monotone_clock.Stalled() && non_monotone_clock.WasReset()) { + reset_during_stall_tol_us = + std::max(reset_during_stall_tol_us, time_us - last_time_us); + if (non_monotone_clock.WasNegativeReset()) { + add_tolerance_on_next_packet = true; + } + if (initial_clock_stall && !non_monotone_clock.WasNegativeReset()) { + // Positive resets during an initial clock stall cannot be repaired + // and error will propagate through rest of trace. + accumulated_upper_bound_tolerance_us += + std::abs(non_monotone_clock.GetLastResetUs()); + } + } else { + reset_during_stall_tol_us = 0; + initial_clock_stall = false; + } + int64_t err = corrected_clock_us - corrected_clock_0 - time_us; + + // Resets during stalls may lead to small errors temporarily. + int64_t lower_tol_us = accumulated_lower_bound_tolerance_us - + reset_during_stall_tol_us - monotone_noise_us - + 2 * rtc::kNumMicrosecsPerMillisec; + EXPECT_GE(err, lower_tol_us); + int64_t upper_tol_us = accumulated_upper_bound_tolerance_us + + monotone_noise_us + + 2 * rtc::kNumMicrosecsPerMillisec; + EXPECT_LE(err, upper_tol_us); + + last_time_us = time_us; + } + } } } // namespace - -TEST(ReceiveTimeCalculatorTest, UsesSmallerIncrements) { - int64_t kMinDeltaMs = -20; - int64_t kMaxDeltaDiffMs = 100; - ReceiveTimeCalculator calc(kMinDeltaMs, kMaxDeltaDiffMs); - // Initialize offset. - ReconcileMs(&calc, 10000, 20000); - - EXPECT_EQ(ReconcileMs(&calc, 10010, 20100), 20010); - EXPECT_EQ(ReconcileMs(&calc, 10020, 20100), 20020); - EXPECT_EQ(ReconcileMs(&calc, 10030, 20100), 20030); - - EXPECT_EQ(ReconcileMs(&calc, 10110, 20200), 20110); - EXPECT_EQ(ReconcileMs(&calc, 10120, 20200), 20120); - EXPECT_EQ(ReconcileMs(&calc, 10130, 20200), 20130); - - // Small jumps backwards are let trough, they might be due to reordering. - EXPECT_EQ(ReconcileMs(&calc, 10120, 20200), 20120); - // The safe clock might be smaller than the packet clock. - EXPECT_EQ(ReconcileMs(&calc, 10210, 20200), 20210); - EXPECT_EQ(ReconcileMs(&calc, 10240, 20200), 20240); -} - -TEST(ReceiveTimeCalculatorTest, CorrectsJumps) { - int64_t kMinDeltaMs = -20; - int64_t kMaxDeltaDiffMs = 100; - ReceiveTimeCalculator calc(kMinDeltaMs, kMaxDeltaDiffMs); - // Initialize offset. - ReconcileMs(&calc, 10000, 20000); - - EXPECT_EQ(ReconcileMs(&calc, 10010, 20100), 20010); - EXPECT_EQ(ReconcileMs(&calc, 10020, 20100), 20020); - EXPECT_EQ(ReconcileMs(&calc, 10030, 20100), 20030); - - // Jump forward in time. - EXPECT_EQ(ReconcileMs(&calc, 10240, 20200), 20200); - EXPECT_EQ(ReconcileMs(&calc, 10250, 20200), 20210); - EXPECT_EQ(ReconcileMs(&calc, 10260, 20200), 20220); - - // Jump backward in time. - EXPECT_EQ(ReconcileMs(&calc, 10230, 20300), 20300); - EXPECT_EQ(ReconcileMs(&calc, 10240, 20300), 20310); - EXPECT_EQ(ReconcileMs(&calc, 10250, 20300), 20320); -} - } // namespace test - } // namespace webrtc diff --git a/video/BUILD.gn b/video/BUILD.gn index bd49b3de70..5ac2383cc3 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -399,7 +399,6 @@ if (rtc_include_tests) { "end_to_end_tests/multi_stream_tests.cc", "end_to_end_tests/network_state_tests.cc", "end_to_end_tests/probing_tests.cc", - "end_to_end_tests/receive_time_tests.cc", "end_to_end_tests/retransmission_tests.cc", "end_to_end_tests/rtp_rtcp_tests.cc", "end_to_end_tests/ssrc_tests.cc", diff --git a/video/end_to_end_tests/receive_time_tests.cc b/video/end_to_end_tests/receive_time_tests.cc deleted file mode 100644 index 10602b0bab..0000000000 --- a/video/end_to_end_tests/receive_time_tests.cc +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2018 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 "api/test/simulated_network.h" -#include "call/fake_network_pipe.h" -#include "call/simulated_network.h" -#include "rtc_base/criticalsection.h" -#include "rtc_base/timeutils.h" -#include "test/call_test.h" -#include "test/field_trial.h" -#include "test/rtcp_packet_parser.h" - -namespace webrtc { -namespace { - -// This tester simulates a series of clock reset events where different offsets -// are added to the receive time. It detects jumps in the resulting reported -// receive times of more than 200 ms. -class ReportedReceiveTimeTester : public test::EndToEndTest { - public: - struct TimeJump { - int64_t at_send_time_ms; - int64_t add_offset_ms; - static constexpr int64_t kStop = 0; - }; - - ReportedReceiveTimeTester() - : EndToEndTest(test::CallTest::kDefaultTimeoutMs) { - // These should be let trough without correction and filtered if correction - // is enabled. - jumps_.push({500, 2000}); - jumps_.push({1000, -400}); - jumps_.push({1500, 2000000}); - jumps_.push({1700, TimeJump::kStop}); - } - bool JumpInReportedTimes() { return jump_in_reported_times_; } - - protected: - Action OnReceiveRtcp(const uint8_t* data, size_t length) override { - test::RtcpPacketParser parser; - EXPECT_TRUE(parser.Parse(data, length)); - const auto& fb = parser.transport_feedback(); - if (fb->num_packets() > 0) { - int64_t arrival_time_us = fb->GetBaseTimeUs(); - for (const auto& pkt : fb->GetReceivedPackets()) { - arrival_time_us += pkt.delta_us(); - if (last_arrival_time_us_ != 0) { - int64_t delta_us = arrival_time_us - last_arrival_time_us_; - rtc::CritScope crit(&send_times_crit_); - if (send_times_us_.size() >= 2) { - int64_t ground_truth_delta_us = - send_times_us_[1] - send_times_us_[0]; - send_times_us_.pop_front(); - int64_t delta_diff_ms = (delta_us - ground_truth_delta_us) / 1000; - if (std::abs(delta_diff_ms) > 200) { - jump_in_reported_times_ = true; - observation_complete_.Set(); - } - } - } - last_arrival_time_us_ = arrival_time_us; - } - } - return SEND_PACKET; - } - Action OnSendRtp(const uint8_t* data, size_t length) override { - { - rtc::CritScope crit(&send_times_crit_); - send_times_us_.push_back(rtc::TimeMicros()); - } - int64_t now_ms = rtc::TimeMillis(); - if (!first_send_time_ms_) - first_send_time_ms_ = now_ms; - int64_t send_time_ms = now_ms - first_send_time_ms_; - if (send_time_ms >= jumps_.front().at_send_time_ms) { - if (jumps_.front().add_offset_ms == TimeJump::kStop) { - observation_complete_.Set(); - jumps_.pop(); - return SEND_PACKET; - } - clock_offset_ms_ += jumps_.front().add_offset_ms; - send_pipe_->SetClockOffset(clock_offset_ms_); - jumps_.pop(); - } - return SEND_PACKET; - } - test::PacketTransport* CreateSendTransport( - test::SingleThreadedTaskQueueForTesting* task_queue, - Call* sender_call) override { - auto pipe = absl::make_unique( - Clock::GetRealTimeClock(), - absl::make_unique(BuiltInNetworkBehaviorConfig())); - send_pipe_ = pipe.get(); - return send_transport_ = new test::PacketTransport( - task_queue, sender_call, this, test::PacketTransport::kSender, - test::CallTest::payload_type_map_, std::move(pipe)); - } - void PerformTest() override { - observation_complete_.Wait(test::CallTest::kDefaultTimeoutMs); - } - size_t GetNumVideoStreams() const override { return 1; } - size_t GetNumAudioStreams() const override { return 0; } - - private: - int64_t last_arrival_time_us_ = 0; - int64_t first_send_time_ms_ = 0; - rtc::CriticalSection send_times_crit_; - std::deque send_times_us_ RTC_GUARDED_BY(send_times_crit_); - bool jump_in_reported_times_ = false; - FakeNetworkPipe* send_pipe_; - test::PacketTransport* send_transport_; - int64_t clock_offset_ms_ = 0; - std::queue jumps_; -}; -} // namespace - -class ReceiveTimeEndToEndTest : public test::CallTest { - public: - ReceiveTimeEndToEndTest() {} - - virtual ~ReceiveTimeEndToEndTest() {} -}; - -TEST_F(ReceiveTimeEndToEndTest, ReceiveTimeJumpsWithoutFieldTrial) { - // Without the field trial, the jumps in clock offset should be let trough and - // be detected. - ReportedReceiveTimeTester test; - RunBaseTest(&test); - EXPECT_TRUE(test.JumpInReportedTimes()); -} - -TEST_F(ReceiveTimeEndToEndTest, ReceiveTimeSteadyWithFieldTrial) { - // Since all the added jumps by the tester are outside the interval of -100 ms - // to 1000 ms, they should all be filtered by the field trial below, and no - // jumps should be detected. - test::ScopedFieldTrials field_trial( - "WebRTC-BweReceiveTimeCorrection/Enabled,-100,1000/"); - ReportedReceiveTimeTester test; - RunBaseTest(&test); - EXPECT_FALSE(test.JumpInReportedTimes()); -} -} // namespace webrtc