blob: b7bfdf2ff3a6bd75ebae9040b04ac132f2e92396 [file]
/*
* Copyright (c) 2026 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 "video/timing/simulator/rtt_simulator.h"
#include <cstdint>
#include <memory>
#include "api/sequence_checker.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/logged_rtp_rtcp.h"
#include "modules/rtp_rtcp/source/ntp_time_util.h"
#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
#include "system_wrappers/include/ntp_time.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "video/timing/simulator/test/simulated_time_test_fixture.h"
namespace webrtc::video_timing_simulator {
namespace {
constexpr uint32_t kSenderSsrc = 123456;
constexpr uint32_t kReceiverSsrc = 987654;
class MockSimulatedRttCallback : public SimulatedRttCallback {
public:
MOCK_METHOD(void, OnMaxRttUpdate, (TimeDelta max_rtt), (override));
};
class RttSimulatorTest : public SimulatedTimeTestFixture {
protected:
RttSimulatorTest() {
SendTask([this]() {
RTC_DCHECK_RUN_ON(queue_ptr_);
rtt_simulator_ =
std::make_unique<RttSimulator>(env_, queue_ptr_, &callback_);
});
}
~RttSimulatorTest() override {
SendTask([this]() {
RTC_DCHECK_RUN_ON(queue_ptr_);
rtt_simulator_.reset();
});
}
MockSimulatedRttCallback callback_;
std::unique_ptr<RttSimulator> rtt_simulator_;
};
LoggedRtcpPacketSenderReport CreateLoggedRtcpPacketSenderReport(
NtpTime ntp,
Timestamp log_time) {
rtcp::SenderReport sr;
sr.SetSenderSsrc(kSenderSsrc);
sr.SetNtp(ntp);
return LoggedRtcpPacketSenderReport(log_time, sr);
}
LoggedRtcpPacketSenderReport CreateLoggedRtcpPacketSenderReportWithReportBlock(
NtpTime ntp,
uint32_t last_sr,
uint32_t delay_since_last_sr,
Timestamp log_time) {
rtcp::SenderReport sr;
sr.SetSenderSsrc(kReceiverSsrc);
sr.SetNtp(ntp);
rtcp::ReportBlock block;
block.SetMediaSsrc(kSenderSsrc);
block.SetLastSr(last_sr);
block.SetDelayLastSr(delay_since_last_sr);
sr.AddReportBlock(block);
return LoggedRtcpPacketSenderReport(log_time, sr);
}
LoggedRtcpPacketReceiverReport CreateLoggedRtcpPacketReceiverReport(
uint32_t last_sr,
uint32_t delay_since_last_sr,
Timestamp log_time) {
rtcp::ReceiverReport rr;
rr.SetSenderSsrc(kReceiverSsrc);
rtcp::ReportBlock block;
block.SetMediaSsrc(kSenderSsrc);
block.SetLastSr(last_sr);
block.SetDelayLastSr(delay_since_last_sr);
rr.AddReportBlock(block);
return LoggedRtcpPacketReceiverReport(log_time, rr);
}
LoggedRtcpPacketExtendedReports CreateLoggedRtcpPacketExtendedReportsWithRrtr(
NtpTime ntp,
Timestamp log_time) {
rtcp::ExtendedReports xr;
xr.SetSenderSsrc(kReceiverSsrc);
rtcp::Rrtr rrtr;
rrtr.SetNtp(ntp);
xr.SetRrtr(rrtr);
LoggedRtcpPacketExtendedReports logged_xr;
logged_xr.timestamp = log_time;
logged_xr.xr = xr;
return logged_xr;
}
LoggedRtcpPacketExtendedReports CreateLoggedRtcpPacketExtendedReportsWithDlrr(
uint32_t last_rr,
uint32_t delay_since_last_rr,
Timestamp log_time) {
rtcp::ExtendedReports xr;
xr.SetSenderSsrc(kSenderSsrc);
rtcp::ReceiveTimeInfo dlrr_block;
dlrr_block.ssrc = kReceiverSsrc;
dlrr_block.last_rr = last_rr;
dlrr_block.delay_since_last_rr = delay_since_last_rr;
xr.AddDlrrItem(dlrr_block);
LoggedRtcpPacketExtendedReports logged_xr;
logged_xr.timestamp = log_time;
logged_xr.xr = xr;
return logged_xr;
}
TEST_F(RttSimulatorTest, SenderCalculatesRttFromIncomingRr) {
// Outgoing SR.
Timestamp sent_timestamp = time_controller_.GetClock()->CurrentTime();
NtpTime ntp = env_.clock().ConvertTimestampToNtpTime(sent_timestamp);
auto sr = CreateLoggedRtcpPacketSenderReport(ntp, sent_timestamp);
SendTask([this, sr]() { rtt_simulator_->OnOutgoingSenderReport(sr); });
// Incoming RR: 50ms delay, arrives after 150ms => RTT is 100ms.
time_controller_.AdvanceTime(TimeDelta::Millis(150));
Timestamp recv_timestamp = time_controller_.GetClock()->CurrentTime();
uint32_t last_sr = CompactNtp(ntp);
uint32_t delay_since_last_sr = SaturatedToCompactNtp(TimeDelta::Millis(50));
auto rr = CreateLoggedRtcpPacketReceiverReport(last_sr, delay_since_last_sr,
recv_timestamp);
EXPECT_CALL(callback_, OnMaxRttUpdate(TimeDelta::Millis(100)));
SendTask([this, rr]() { rtt_simulator_->OnIncomingReceiverReport(rr); });
}
TEST_F(RttSimulatorTest, SenderCalculatesRttFromIncomingSr) {
// Outgoing SR.
Timestamp sent_timestamp = time_controller_.GetClock()->CurrentTime();
NtpTime ntp = env_.clock().ConvertTimestampToNtpTime(sent_timestamp);
auto sr = CreateLoggedRtcpPacketSenderReport(ntp, sent_timestamp);
SendTask([this, sr]() { rtt_simulator_->OnOutgoingSenderReport(sr); });
// Incoming SR: 50ms delay, arrives after 150ms => RTT is 100ms.
time_controller_.AdvanceTime(TimeDelta::Millis(150));
Timestamp recv_timestamp = time_controller_.GetClock()->CurrentTime();
uint32_t last_sr = CompactNtp(ntp);
uint32_t delay_since_last_sr = SaturatedToCompactNtp(TimeDelta::Millis(50));
NtpTime ntp_incoming;
auto sr_incoming = CreateLoggedRtcpPacketSenderReportWithReportBlock(
ntp_incoming, last_sr, delay_since_last_sr, recv_timestamp);
EXPECT_CALL(callback_, OnMaxRttUpdate(TimeDelta::Millis(100)));
SendTask([this, sr_incoming]() {
rtt_simulator_->OnIncomingSenderReport(sr_incoming);
});
}
TEST_F(RttSimulatorTest, ReceiverCalculatesRttFromIncomingXr) {
// Outgoing XR with RRTR.
Timestamp sent_timestamp = time_controller_.GetClock()->CurrentTime();
NtpTime ntp = env_.clock().ConvertTimestampToNtpTime(sent_timestamp);
auto xr_rrtr =
CreateLoggedRtcpPacketExtendedReportsWithRrtr(ntp, sent_timestamp);
SendTask([this, xr_rrtr]() {
rtt_simulator_->OnOutgoingExtendedReports(xr_rrtr);
});
// Incoming XR with DLRR: 50ms delay, arrives after 150ms => RTT is 100ms.
time_controller_.AdvanceTime(TimeDelta::Millis(150));
Timestamp recv_timestamp = time_controller_.GetClock()->CurrentTime();
uint32_t last_rr = CompactNtp(ntp);
uint32_t delay_since_last_rr = SaturatedToCompactNtp(TimeDelta::Millis(50));
auto xr_dlrr = CreateLoggedRtcpPacketExtendedReportsWithDlrr(
last_rr, delay_since_last_rr, recv_timestamp);
EXPECT_CALL(callback_, OnMaxRttUpdate(TimeDelta::Millis(100)));
SendTask([this, xr_dlrr]() {
rtt_simulator_->OnIncomingExtendedReports(xr_dlrr);
});
}
} // namespace
} // namespace webrtc::video_timing_simulator