diff --git a/call/BUILD.gn b/call/BUILD.gn index eeb82e85f6..f43acd3e31 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -147,6 +147,8 @@ rtc_static_library("call") { "degraded_call.h", "flexfec_receive_stream_impl.cc", "flexfec_receive_stream_impl.h", + "receive_time_calculator.cc", + "receive_time_calculator.h", ] if (!build_with_chromium && is_clang) { @@ -249,6 +251,7 @@ if (rtc_include_tests) { "bitrate_estimator_tests.cc", "call_unittest.cc", "flexfec_receive_stream_unittest.cc", + "receive_time_calculator_unittest.cc", "rtcp_demuxer_unittest.cc", "rtp_bitrate_configurator_unittest.cc", "rtp_demuxer_unittest.cc", diff --git a/call/call.cc b/call/call.cc index b7f54e554f..3cd9864d1a 100644 --- a/call/call.cc +++ b/call/call.cc @@ -24,6 +24,7 @@ #include "call/bitrate_allocator.h" #include "call/call.h" #include "call/flexfec_receive_stream_impl.h" +#include "call/receive_time_calculator.h" #include "call/rtp_stream_receiver_controller.h" #include "call/rtp_transport_controller_send.h" #include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" @@ -362,6 +363,9 @@ class Call : public webrtc::Call, RateLimiter retransmission_rate_limiter_; std::unique_ptr transport_send_; ReceiveSideCongestionController receive_side_cc_; + + const std::unique_ptr receive_time_calculator_; + const std::unique_ptr video_send_delay_stats_; const int64_t start_ms_; // TODO(perkj): |worker_queue_| is supposed to replace @@ -436,6 +440,7 @@ Call::Call(const Call::Config& config, pacer_bitrate_kbps_counter_(clock_, nullptr, true), retransmission_rate_limiter_(clock_, kRetransmitWindowSizeMs), receive_side_cc_(clock_, transport_send->packet_router()), + receive_time_calculator_(ReceiveTimeCalculator::CreateFromFieldTrial()), video_send_delay_stats_(new SendDelayStats(clock_)), start_ms_(clock_->TimeInMilliseconds()), worker_queue_("call_worker_queue") { @@ -1227,7 +1232,12 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type, return DELIVERY_PACKET_ERROR; if (packet_time.timestamp != -1) { - parsed_packet.set_arrival_time_ms((packet_time.timestamp + 500) / 1000); + int64_t timestamp_us = packet_time.timestamp; + if (receive_time_calculator_) { + timestamp_us = receive_time_calculator_->ReconcileReceiveTimes( + packet_time.timestamp, clock_->TimeInMicroseconds()); + } + parsed_packet.set_arrival_time_ms((timestamp_us + 500) / 1000); } else { parsed_packet.set_arrival_time_ms(clock_->TimeInMilliseconds()); } diff --git a/call/receive_time_calculator.cc b/call/receive_time_calculator.cc new file mode 100644 index 0000000000..ecc0272f99 --- /dev/null +++ b/call/receive_time_calculator.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 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 "call/receive_time_calculator.h" +#include "rtc_base/logging.h" +#include "rtc_base/ptr_util.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"; +} // 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) {} + +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 rtc::MakeUnique(min, max); +} + +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. + + 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_; + } + } + last_packet_time_us_ = packet_time_us_; + last_safe_time_us_ = safe_time_us_; + return packet_time_us_ + *receive_time_offset_us_; +} +} // namespace webrtc diff --git a/call/receive_time_calculator.h b/call/receive_time_calculator.h new file mode 100644 index 0000000000..b2dc00375d --- /dev/null +++ b/call/receive_time_calculator.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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. + */ +#ifndef CALL_RECEIVE_TIME_CALCULATOR_H_ +#define CALL_RECEIVE_TIME_CALCULATOR_H_ + +#include +#include + +#include "api/optional.h" + +namespace webrtc { + +// 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 +// increments since they are based on the exact receive time. They might +// however, have large jumps due to clock resets in the system. To compensate +// this they are combined with a safe clock source that is guaranteed to be +// consistent, but it will not be able to measure the exact time when a packet +// is received. +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_); + + private: + const int64_t min_delta_us_; + const int64_t max_delta_diff_us_; + rtc::Optional receive_time_offset_us_; + int64_t last_packet_time_us_ = 0; + int64_t last_safe_time_us_ = 0; +}; +} // namespace webrtc +#endif // CALL_RECEIVE_TIME_CALCULATOR_H_ diff --git a/call/receive_time_calculator_unittest.cc b/call/receive_time_calculator_unittest.cc new file mode 100644 index 0000000000..92d5a279ea --- /dev/null +++ b/call/receive_time_calculator_unittest.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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 "call/receive_time_calculator.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; +} +} // 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