You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
7.9 KiB
194 lines
7.9 KiB
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <cmath>
|
|
|
|
#include "absl/types/span.h"
|
|
#include "cast/streaming/sender_report_builder.h"
|
|
#include "cast/streaming/sender_report_parser.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace openscreen {
|
|
namespace cast {
|
|
namespace {
|
|
|
|
using openscreen::operator<<;
|
|
|
|
constexpr Ssrc kSenderSsrc{1};
|
|
constexpr Ssrc kReceiverSsrc{2};
|
|
|
|
class SenderReportTest : public testing::Test {
|
|
public:
|
|
SenderReportBuilder* builder() { return &builder_; }
|
|
SenderReportParser* parser() { return &parser_; }
|
|
const NtpTimeConverter& ntp_converter() const {
|
|
return session_.ntp_converter();
|
|
}
|
|
|
|
private:
|
|
RtcpSession session_{kSenderSsrc, kReceiverSsrc, Clock::now()};
|
|
SenderReportBuilder builder_{&session_};
|
|
SenderReportParser parser_{&session_};
|
|
};
|
|
|
|
// Tests that the compound RTCP packets containing a Sender Report alongside
|
|
// zero or more other messages can be parsed successfully.
|
|
TEST_F(SenderReportTest, Parsing) {
|
|
// clang-format off
|
|
const uint8_t kSenderReportPacket[] = {
|
|
0b10000001, // Version=2, Padding=no, ItemCount=1 byte.
|
|
200, // RTCP Packet type byte.
|
|
0x00, 0x0c, // Length of remainder of packet, in 32-bit words.
|
|
0x00, 0x00, 0x00, 0x01, // SSRC of sender.
|
|
0xe0, 0x73, 0x2e, 0x54, // NTP Timestamp (late evening on 2019-04-30).
|
|
0x80, 0x00, 0x00, 0x00,
|
|
0x00, 0x14, 0x99, 0x70, // RTP Timestamp (15 seconds, 90kHz timebase).
|
|
0x00, 0x00, 0x01, 0xff, // Sender's Packet Count.
|
|
0x00, 0x07, 0x11, 0x0d, // Sender's Octet Count.
|
|
0x00, 0x00, 0x00, 0x02, // SSRC of receiver (to whom this report is for).
|
|
0x00, // Fraction lost.
|
|
0x00, 0x00, 0x02, // Cumulative Number of Packets Lost.
|
|
0x00, 0x00, 0x38, 0x40, // Highest Sequence Number Received.
|
|
0x00, 0x00, 0x03, 0x84, // Interarrival Jitter.
|
|
0xaf, 0xd3, 0xff, 0x00, // Sender Report ID.
|
|
0x00, 0x00, 0x83, 0xfa, // Delay since last Sender Report.
|
|
};
|
|
|
|
constexpr NtpTimestamp kNtpTimestampInSenderReport{0xe0732e5480000000};
|
|
|
|
const uint8_t kOtherPacket[] = {
|
|
0b10000000, // Version=2, Padding=no, ItemCount=0 byte.
|
|
204, // RTCP Packet type byte.
|
|
0x00, 0x01, // Length of remainder of packet, in 32-bit words.
|
|
0x00, 0x00, 0x00, 0x02, // SSRC of receiver.
|
|
};
|
|
// clang-format on
|
|
|
|
// A RTCP packet only containing non-sender-reports will not provide a Sender
|
|
// Report result.
|
|
EXPECT_FALSE(parser()->Parse(kOtherPacket));
|
|
|
|
// A compound RTCP packet containing a Sender Report alongside other things
|
|
// should be detected as "well-formed" by the parser and it should also
|
|
// provide a Sender Report result. Also, it shouldn't matter what the ordering
|
|
// is.
|
|
const absl::Span<const uint8_t> kCompoundCombinations[2][2] = {
|
|
{kSenderReportPacket, kOtherPacket},
|
|
{kOtherPacket, kSenderReportPacket},
|
|
};
|
|
for (const auto& combo : kCompoundCombinations) {
|
|
uint8_t compound_packet[sizeof(kSenderReportPacket) + sizeof(kOtherPacket)];
|
|
memcpy(compound_packet, combo[0].data(), combo[0].size());
|
|
memcpy(compound_packet + combo[0].size(), combo[1].data(), combo[1].size());
|
|
|
|
const auto parsed = parser()->Parse(compound_packet);
|
|
ASSERT_TRUE(parsed.has_value());
|
|
EXPECT_EQ(ToStatusReportId(kNtpTimestampInSenderReport), parsed->report_id);
|
|
EXPECT_EQ(ntp_converter().ToLocalTime(kNtpTimestampInSenderReport),
|
|
parsed->reference_time);
|
|
EXPECT_EQ(RtpTimeTicks() + RtpTimeDelta::FromTicks(1350000),
|
|
parsed->rtp_timestamp);
|
|
EXPECT_EQ(uint32_t{0x1ff}, parsed->send_packet_count);
|
|
EXPECT_EQ(uint32_t{0x7110d}, parsed->send_octet_count);
|
|
ASSERT_TRUE(parsed->report_block.has_value());
|
|
EXPECT_EQ(kReceiverSsrc, parsed->report_block->ssrc);
|
|
// Note: RtcpReportBlock parsing is unit-tested elsewhere.
|
|
}
|
|
}
|
|
|
|
// Tests that the SenderReportParser will not try to parse an empty packet.
|
|
TEST_F(SenderReportTest, WillNotParseEmptyPacket) {
|
|
const uint8_t kEmptyPacket[] = {};
|
|
EXPECT_FALSE(parser()->Parse(absl::Span<const uint8_t>(kEmptyPacket, 0)));
|
|
}
|
|
|
|
// Tests that the SenderReportParser will not parse anything from garbage data.
|
|
TEST_F(SenderReportTest, WillNotParseGarbage) {
|
|
// clang-format off
|
|
const uint8_t kGarbage[] = {
|
|
0x4f, 0x27, 0xeb, 0x22, 0x27, 0xeb, 0x22, 0x4f,
|
|
0xeb, 0x22, 0x4f, 0x27, 0x22, 0x4f, 0x27, 0xeb,
|
|
};
|
|
// clang-format on
|
|
EXPECT_FALSE(parser()->Parse(kGarbage));
|
|
}
|
|
|
|
// Assuming that SenderReportTest.Parsing has been proven the implementation,
|
|
// this test checks that the builder produces RTCP packets that can be parsed.
|
|
TEST_F(SenderReportTest, BuildPackets) {
|
|
for (int i = 0; i <= 1; ++i) {
|
|
const bool with_report_block = (i == 1);
|
|
|
|
RtcpSenderReport original;
|
|
original.reference_time = Clock::now();
|
|
original.rtp_timestamp = RtpTimeTicks() + RtpTimeDelta::FromTicks(5);
|
|
original.send_packet_count = 55;
|
|
original.send_octet_count = 20044;
|
|
if (with_report_block) {
|
|
RtcpReportBlock& report_block = original.report_block.emplace();
|
|
report_block.ssrc = kReceiverSsrc;
|
|
}
|
|
|
|
uint8_t buffer[kRtcpCommonHeaderSize + kRtcpSenderReportSize +
|
|
kRtcpReportBlockSize];
|
|
memset(buffer, 0, sizeof(buffer));
|
|
const auto result = builder()->BuildPacket(original, buffer);
|
|
ASSERT_TRUE(result.first.data());
|
|
const int expected_packet_size =
|
|
sizeof(buffer) - (with_report_block ? 0 : kRtcpReportBlockSize);
|
|
EXPECT_EQ(expected_packet_size, static_cast<int>(result.first.size()));
|
|
const StatusReportId expected_status_report_id = ToStatusReportId(
|
|
ntp_converter().ToNtpTimestamp(original.reference_time));
|
|
EXPECT_EQ(expected_status_report_id, result.second);
|
|
|
|
const auto parsed = parser()->Parse(result.first);
|
|
ASSERT_TRUE(parsed.has_value());
|
|
EXPECT_EQ(expected_status_report_id, parsed->report_id);
|
|
// Note: The reference time can be off by one platform clock tick due to
|
|
// a lossy conversion when going to and from the wire-format NtpTimestamps.
|
|
// See the unit tests in ntp_time_unittest.cc for further discussion.
|
|
EXPECT_LE(
|
|
std::abs((original.reference_time - parsed->reference_time).count()),
|
|
1);
|
|
EXPECT_EQ(original.rtp_timestamp, parsed->rtp_timestamp);
|
|
EXPECT_EQ(original.send_packet_count, parsed->send_packet_count);
|
|
EXPECT_EQ(original.send_octet_count, parsed->send_octet_count);
|
|
if (with_report_block) {
|
|
ASSERT_TRUE(parsed->report_block.has_value());
|
|
EXPECT_EQ(original.report_block->ssrc, parsed->report_block->ssrc);
|
|
// Note: RtcpReportBlock serialization/parsing is unit-tested elsewhere.
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(SenderReportTest, ComputesTimePointsFromReportIds) {
|
|
// Note: The time_points can be off by up to 16 µs because of the loss of
|
|
// precision caused by truncating the NtpTimestamps into StatusReportIds.
|
|
constexpr std::chrono::microseconds kEpsilon{16};
|
|
|
|
// Test a sampling of time points over the last 65536 seconds to confirm the
|
|
// rollover correction logic is working.
|
|
Clock::time_point on_or_before = Clock::now() + std::chrono::seconds(65536);
|
|
constexpr int kNumIterations = 16;
|
|
constexpr int kSecondsPerStep = 4096;
|
|
for (int i = 0; i < kNumIterations; ++i) {
|
|
const Clock::time_point expected_time =
|
|
on_or_before - std::chrono::seconds(i * kSecondsPerStep);
|
|
const auto report_id =
|
|
ToStatusReportId(ntp_converter().ToNtpTimestamp(expected_time));
|
|
const Clock::time_point report_time =
|
|
builder()->GetRecentReportTime(report_id, on_or_before);
|
|
EXPECT_GE(on_or_before, report_time);
|
|
const auto absolute_difference = (expected_time < report_time)
|
|
? (report_time - expected_time)
|
|
: (expected_time - report_time);
|
|
EXPECT_LE(absolute_difference, kEpsilon)
|
|
<< expected_time << " vs " << report_time;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace cast
|
|
} // namespace openscreen
|