blob: 9e025cb553a4a7e2234ce424eb8b7b8597fb32a5 [file] [log] [blame]
/*
* 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 "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_transport_->SetClockOffset(clock_offset_ms_);
jumps_.pop();
}
return SEND_PACKET;
}
test::PacketTransport* CreateSendTransport(
test::SingleThreadedTaskQueueForTesting* task_queue,
Call* sender_call) override {
return send_transport_ = new test::PacketTransport(
task_queue, sender_call, this, test::PacketTransport::kSender,
test::CallTest::payload_type_map_, FakeNetworkPipe::Config());
}
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<int64_t> send_times_us_ RTC_GUARDED_BY(send_times_crit_);
bool jump_in_reported_times_ = false;
test::PacketTransport* send_transport_;
int64_t clock_offset_ms_ = 0;
std::queue<TimeJump> 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