blob: f4d3d08e59a52c0728f4b59cd8780d3d7f2adbcf [file] [log] [blame]
// Copyright 2014 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 "media/cast/net/rtcp/rtcp_utility.h"
#include <stdint.h>
#include <memory>
#include "base/macros.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/base/fake_single_thread_task_runner.h"
#include "media/cast/cast_environment.h"
#include "media/cast/net/cast_transport_defines.h"
#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace cast {
static const uint32_t kRemoteSsrc = 0x10203;
static const uint32_t kLocalSsrc = 0x40506;
static const uint32_t kUnknownSsrc = 0xDEAD;
static const base::TimeDelta kTargetDelay = base::Milliseconds(100);
class RtcpParserTest : public ::testing::Test {
protected:
RtcpParserTest()
: testing_clock_(new base::SimpleTestTickClock()),
task_runner_(new FakeSingleThreadTaskRunner(testing_clock_.get())) {}
bool HasAnything(const RtcpParser& parser) {
return parser.has_sender_report() ||
parser.has_last_report() ||
parser.has_receiver_log() ||
parser.has_cast_message() ||
parser.has_receiver_reference_time_report();
}
void ExpectSenderInfo(const RtcpParser& parser) {
EXPECT_TRUE(parser.has_sender_report());
EXPECT_EQ(kNtpHigh, parser.sender_report().ntp_seconds);
EXPECT_EQ(kNtpLow, parser.sender_report().ntp_fraction);
EXPECT_EQ(kRtpTimestamp,
parser.sender_report().rtp_timestamp.lower_32_bits());
EXPECT_EQ(kSendPacketCount, parser.sender_report().send_packet_count);
EXPECT_EQ(kSendOctetCount, parser.sender_report().send_octet_count);
}
void ExpectLastReport(const RtcpParser& parser) {
EXPECT_TRUE(parser.has_last_report());
EXPECT_EQ(kLastSr, parser.last_report());
EXPECT_EQ(kDelayLastSr, parser.delay_since_last_report());
}
void ExpectReceiverReference(const RtcpParser& parser) {
EXPECT_TRUE(parser.has_receiver_reference_time_report());
EXPECT_EQ(kRemoteSsrc, parser.receiver_reference_time_report().remote_ssrc);
EXPECT_EQ(kNtpHigh, parser.receiver_reference_time_report().ntp_seconds);
EXPECT_EQ(kNtpLow, parser.receiver_reference_time_report().ntp_fraction);
}
void ExpectCastFeedback(const RtcpParser& parser) {
EXPECT_TRUE(parser.has_cast_message());
EXPECT_EQ(kRemoteSsrc, parser.cast_message().remote_ssrc);
EXPECT_EQ(FrameId::first() + kAckFrameId,
parser.cast_message().ack_frame_id);
auto frame_it = parser.cast_message().missing_frames_and_packets.begin();
EXPECT_TRUE(
frame_it != parser.cast_message().missing_frames_and_packets.end());
EXPECT_EQ(FrameId::first() + kLostFrameId, frame_it->first);
EXPECT_EQ(frame_it->second.size(), 1UL);
EXPECT_EQ(*frame_it->second.begin(), kRtcpCastAllPacketsLost);
++frame_it;
EXPECT_TRUE(
frame_it != parser.cast_message().missing_frames_and_packets.end());
EXPECT_EQ(FrameId::first() + kFrameIdWithLostPackets, frame_it->first);
EXPECT_EQ(3UL, frame_it->second.size());
auto packet_it = frame_it->second.begin();
EXPECT_EQ(kLostPacketId1, *packet_it);
++packet_it;
EXPECT_EQ(kLostPacketId2, *packet_it);
++packet_it;
EXPECT_EQ(kLostPacketId3, *packet_it);
++frame_it;
EXPECT_TRUE(
frame_it == parser.cast_message().missing_frames_and_packets.end());
}
void ExpectExtendedCastFeedback(
const RtcpParser& parser,
const std::vector<FrameId>& receiving_frames) {
EXPECT_TRUE(parser.has_cst2_message());
EXPECT_EQ(kFeedbackSeq, parser.cast_message().feedback_count);
ASSERT_EQ(parser.cast_message().received_later_frames.size(),
receiving_frames.size());
for (size_t i = 0; i < receiving_frames.size(); ++i)
EXPECT_EQ(parser.cast_message().received_later_frames[i],
receiving_frames[i]);
}
void ExpectReceiverLog(const RtcpParser& parser,
const RtcpReceiverLogMessage& expected_receiver_log) {
EXPECT_TRUE(parser.has_receiver_log());
EXPECT_EQ(expected_receiver_log.size(), parser.receiver_log().size());
auto expected_it = expected_receiver_log.begin();
auto incoming_it = parser.receiver_log().begin();
for (; incoming_it != parser.receiver_log().end();
++incoming_it, ++expected_it) {
EXPECT_EQ(expected_it->rtp_timestamp_, incoming_it->rtp_timestamp_);
EXPECT_EQ(expected_it->event_log_messages_.size(),
incoming_it->event_log_messages_.size());
auto event_incoming_it = incoming_it->event_log_messages_.begin();
auto event_expected_it = expected_it->event_log_messages_.begin();
for (; event_incoming_it != incoming_it->event_log_messages_.end();
++event_incoming_it, ++event_expected_it) {
EXPECT_EQ(event_expected_it->type, event_incoming_it->type);
EXPECT_EQ(event_expected_it->event_timestamp,
event_incoming_it->event_timestamp);
if (event_expected_it->type == PACKET_RECEIVED) {
EXPECT_EQ(event_expected_it->packet_id, event_incoming_it->packet_id);
} else {
EXPECT_EQ(event_expected_it->delay_delta,
event_incoming_it->delay_delta);
}
}
}
}
std::unique_ptr<base::SimpleTestTickClock> testing_clock_;
scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
private:
DISALLOW_COPY_AND_ASSIGN(RtcpParserTest);
};
TEST_F(RtcpParserTest, BrokenPacketIsIgnored) {
const char bad_packet[] = {0, 0, 0, 0};
RtcpParser parser(kLocalSsrc, kRemoteSsrc);
base::BigEndianReader reader(bad_packet, sizeof(bad_packet));
EXPECT_FALSE(parser.Parse(&reader));
}
TEST_F(RtcpParserTest, UnknownBlockIgnored) {
// Only unknown data, nothing happens.
TestRtcpPacketBuilder p;
p.AddUnknownBlock();
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p.Reader()));
EXPECT_FALSE(HasAnything(parser1));
// Add valid sender report *after* unknown data - should work fine.
p.AddSr(kRemoteSsrc, 0);
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p.Reader()));
ExpectSenderInfo(parser2);
}
TEST_F(RtcpParserTest, InjectSenderReportPacket) {
TestRtcpPacketBuilder p;
p.AddSr(kRemoteSsrc, 0);
// Expected to be ignored since the sender ssrc does not match our
// remote ssrc.
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p.Reader()));
EXPECT_FALSE(HasAnything(parser1));
// Expected to be pass through since the sender ssrc match our remote ssrc.
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p.Reader()));
ExpectSenderInfo(parser2);
}
TEST_F(RtcpParserTest, InjectReceiveReportPacket) {
TestRtcpPacketBuilder p1;
p1.AddRr(kRemoteSsrc, 1);
p1.AddRb(kUnknownSsrc);
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
RtcpParser parser1(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser1.Parse(p1.Reader()));
EXPECT_FALSE(HasAnything(parser1));
TestRtcpPacketBuilder p2;
p2.AddRr(kRemoteSsrc, 1);
p2.AddRb(kLocalSsrc);
// Expected to be pass through since the sender ssrc match our local ssrc.
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p2.Reader()));
ExpectLastReport(parser2);
}
TEST_F(RtcpParserTest, InjectSenderReportWithReportBlockPacket) {
TestRtcpPacketBuilder p1;
p1.AddSr(kRemoteSsrc, 1);
p1.AddRb(kUnknownSsrc);
// Sender report expected to be ignored since the sender ssrc does not match
// our remote ssrc.
// Report block expected to be ignored since the source ssrc does not match
// our local ssrc.
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p1.Reader()));
EXPECT_FALSE(HasAnything(parser1));
// Sender report expected to be pass through since the sender ssrc match our
// remote ssrc.
// Report block expected to be ignored since the source ssrc does not match
// our local ssrc.
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p1.Reader()));
ExpectSenderInfo(parser2);
EXPECT_FALSE(parser2.has_last_report());
// Sender report expected to be ignored since the sender ssrc does not match
// our remote ssrc.
// Report block expected to be ignored too since it's a part of the
// sender report.
TestRtcpPacketBuilder p2;
p2.AddSr(kRemoteSsrc, 1);
p2.AddRb(kLocalSsrc);
RtcpParser parser3(kLocalSsrc, 0);
EXPECT_TRUE(parser3.Parse(p2.Reader()));
EXPECT_FALSE(parser3.has_last_report());
// Sender report expected to be pass through since the sender ssrc match our
// remote ssrc.
// Report block expected to be pass through since the sender ssrc match
// our local ssrc.
RtcpParser parser4(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser4.Parse(p2.Reader()));
ExpectSenderInfo(parser4);
ExpectLastReport(parser4);
}
TEST_F(RtcpParserTest, InjectSenderReportPacketWithDlrr) {
TestRtcpPacketBuilder p;
p.AddSr(kRemoteSsrc, 0);
p.AddXrHeader(kRemoteSsrc);
p.AddXrUnknownBlock();
p.AddXrExtendedDlrrBlock(kRemoteSsrc);
p.AddXrUnknownBlock();
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p.Reader()));
EXPECT_FALSE(HasAnything(parser1));
// Expected to be pass through since the sender ssrc match our local ssrc.
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p.Reader()));
ExpectSenderInfo(parser2);
// DLRRs are ignored.
EXPECT_FALSE(parser2.has_last_report());
}
TEST_F(RtcpParserTest, InjectReceiverReportPacketWithRrtr) {
TestRtcpPacketBuilder p1;
p1.AddRr(kRemoteSsrc, 1);
p1.AddRb(kUnknownSsrc);
p1.AddXrHeader(kRemoteSsrc);
p1.AddXrRrtrBlock();
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p1.Reader()));
EXPECT_FALSE(HasAnything(parser1));
TestRtcpPacketBuilder p2;
p2.AddRr(kRemoteSsrc, 1);
p2.AddRb(kLocalSsrc);
p2.AddXrHeader(kRemoteSsrc);
p2.AddXrRrtrBlock();
// Expected to be pass through since the sender ssrc match our local ssrc.
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p2.Reader()));
ExpectLastReport(parser2);
ExpectReceiverReference(parser2);
}
TEST_F(RtcpParserTest, InjectReceiverReportPacketWithIntraFrameRequest) {
TestRtcpPacketBuilder p1;
p1.AddRr(kRemoteSsrc, 1);
p1.AddRb(kUnknownSsrc);
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p1.Reader()));
EXPECT_FALSE(HasAnything(parser1));
TestRtcpPacketBuilder p2;
p2.AddRr(kRemoteSsrc, 1);
p2.AddRb(kLocalSsrc);
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(p2.Reader()));
ExpectLastReport(parser2);
}
TEST_F(RtcpParserTest, InjectReceiverReportPacketWithCastFeedback) {
TestRtcpPacketBuilder p1;
p1.AddRr(kRemoteSsrc, 1);
p1.AddRb(kUnknownSsrc);
p1.AddCast(kRemoteSsrc, kUnknownSsrc, kTargetDelay);
// Expected to be ignored since the source ssrc does not match our
// local ssrc.
RtcpParser parser1(kLocalSsrc, 0);
EXPECT_TRUE(parser1.Parse(p1.Reader()));
EXPECT_FALSE(HasAnything(parser1));
TestRtcpPacketBuilder p2;
p2.AddRr(kRemoteSsrc, 1);
p2.AddRb(kLocalSsrc);
p2.AddCast(kRemoteSsrc, kLocalSsrc, kTargetDelay);
// Expected to be pass through since the sender ssrc match our local ssrc.
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
parser2.SetMaxValidFrameId(FrameId::first() + kAckFrameId);
EXPECT_TRUE(parser2.Parse(p2.Reader()));
ExpectLastReport(parser2);
ExpectCastFeedback(parser2);
}
TEST_F(RtcpParserTest, ExtendedCastFeedbackParsing) {
// Empty ACK field.
TestRtcpPacketBuilder builder;
builder.AddRr(kRemoteSsrc, 1);
builder.AddRb(kLocalSsrc);
builder.AddCast(kRemoteSsrc, kLocalSsrc, kTargetDelay);
std::vector<FrameId> receiving_frames;
builder.AddCst2(receiving_frames);
RtcpParser parser(kLocalSsrc, kRemoteSsrc);
parser.SetMaxValidFrameId(FrameId::first() + kAckFrameId + 3);
EXPECT_TRUE(parser.Parse(builder.Reader()));
ExpectLastReport(parser);
ExpectCastFeedback(parser);
ExpectExtendedCastFeedback(parser, receiving_frames);
// One ACK bitmask.
TestRtcpPacketBuilder builder2;
builder2.AddRr(kRemoteSsrc, 1);
builder2.AddRb(kLocalSsrc);
builder2.AddCast(kRemoteSsrc, kLocalSsrc, kTargetDelay);
receiving_frames.push_back(FrameId::first() + kAckFrameId + 2);
receiving_frames.push_back(FrameId::first() + kAckFrameId + 3);
builder2.AddCst2(receiving_frames);
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
parser2.SetMaxValidFrameId(FrameId::first() + kAckFrameId + 3);
EXPECT_TRUE(parser2.Parse(builder2.Reader()));
ExpectLastReport(parser2);
ExpectCastFeedback(parser2);
ExpectExtendedCastFeedback(parser2, receiving_frames);
// Mutiple ACK bitmasks.
TestRtcpPacketBuilder builder3;
builder3.AddRr(kRemoteSsrc, 1);
builder3.AddRb(kLocalSsrc);
builder3.AddCast(kRemoteSsrc, kLocalSsrc, kTargetDelay);
receiving_frames.clear();
for (size_t i = 0; i < 15; ++i)
receiving_frames.push_back(FrameId::first() + kAckFrameId + 2 + i * 10);
builder3.AddCst2(receiving_frames);
RtcpParser parser3(kLocalSsrc, kRemoteSsrc);
parser3.SetMaxValidFrameId(FrameId::first() + kAckFrameId + 152);
EXPECT_TRUE(parser3.Parse(builder3.Reader()));
ExpectLastReport(parser3);
ExpectCastFeedback(parser3);
ExpectExtendedCastFeedback(parser3, receiving_frames);
// Expected to be ignored if there is error in the extended ACK message.
TestRtcpPacketBuilder builder4;
builder4.AddRr(kRemoteSsrc, 1);
builder4.AddRb(kLocalSsrc);
builder4.AddCast(kRemoteSsrc, kLocalSsrc, kTargetDelay);
builder4.AddErrorCst2();
RtcpParser parser4(kLocalSsrc, kRemoteSsrc);
parser4.SetMaxValidFrameId(FrameId::first() + kAckFrameId + 3);
EXPECT_TRUE(parser4.Parse(builder4.Reader()));
ExpectLastReport(parser4);
ExpectCastFeedback(parser4);
EXPECT_FALSE(parser4.has_cst2_message());
// Expected to be ignored if there is only "CST2" identifier.
TestRtcpPacketBuilder builder5;
builder5.AddRr(kRemoteSsrc, 1);
builder5.AddRb(kLocalSsrc);
receiving_frames.clear();
builder5.AddCst2(receiving_frames);
RtcpParser parser5(kLocalSsrc, kRemoteSsrc);
parser5.SetMaxValidFrameId(FrameId::first() + kAckFrameId + 3);
EXPECT_TRUE(parser5.Parse(builder5.Reader()));
ExpectLastReport(parser5);
EXPECT_FALSE(parser5.has_cst2_message());
}
TEST_F(RtcpParserTest, InjectReceiverReportPli) {
// Expect to be ignored since the sender ssrc does not match.
TestRtcpPacketBuilder builder1;
builder1.AddPli(kUnknownSsrc, kLocalSsrc);
RtcpParser parser1(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser1.Parse(builder1.Reader()));
EXPECT_FALSE(parser1.has_picture_loss_indicator());
// Expect to be ignored since the receiver ssrc does not match.
TestRtcpPacketBuilder builder2;
builder2.AddPli(kRemoteSsrc, kUnknownSsrc);
RtcpParser parser2(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser2.Parse(builder2.Reader()));
EXPECT_FALSE(parser2.has_picture_loss_indicator());
TestRtcpPacketBuilder builder3;
builder3.AddPli(kRemoteSsrc, kLocalSsrc);
RtcpParser parser3(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser3.Parse(builder3.Reader()));
EXPECT_TRUE(parser3.has_picture_loss_indicator());
}
TEST_F(RtcpParserTest, InjectReceiverReportWithReceiverLogVerificationBase) {
static const uint32_t kTimeBaseMs = 12345678;
static const uint32_t kTimeDelayMs = 10;
static const uint32_t kDelayDeltaMs = 123;
base::SimpleTestTickClock testing_clock;
testing_clock.Advance(base::Milliseconds(kTimeBaseMs));
RtcpReceiverLogMessage receiver_log;
RtcpReceiverFrameLogMessage frame_log(RtpTimeTicks().Expand(kRtpTimestamp));
RtcpReceiverEventLogMessage event_log;
event_log.type = FRAME_ACK_SENT;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.delay_delta = base::Milliseconds(kDelayDeltaMs);
frame_log.event_log_messages_.push_back(event_log);
testing_clock.Advance(base::Milliseconds(kTimeDelayMs));
event_log.type = PACKET_RECEIVED;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.packet_id = kLostPacketId1;
frame_log.event_log_messages_.push_back(event_log);
event_log.type = PACKET_RECEIVED;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.packet_id = kLostPacketId2;
frame_log.event_log_messages_.push_back(event_log);
receiver_log.push_back(frame_log);
TestRtcpPacketBuilder p;
p.AddRr(kRemoteSsrc, 1);
p.AddRb(kLocalSsrc);
p.AddReceiverLog(kRemoteSsrc);
p.AddReceiverFrameLog(kRtpTimestamp, 3, kTimeBaseMs);
p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
p.AddReceiverEventLog(kLostPacketId1, PACKET_RECEIVED, kTimeDelayMs);
p.AddReceiverEventLog(kLostPacketId2, PACKET_RECEIVED, kTimeDelayMs);
RtcpParser parser(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser.Parse(p.Reader()));
ExpectReceiverLog(parser, receiver_log);
}
TEST_F(RtcpParserTest, InjectReceiverReportWithReceiverLogVerificationMulti) {
static const uint32_t kTimeBaseMs = 12345678;
static const uint32_t kTimeDelayMs = 10;
static const int kDelayDeltaMs = 123; // To be varied for every frame.
base::SimpleTestTickClock testing_clock;
testing_clock.Advance(base::Milliseconds(kTimeBaseMs));
RtcpReceiverLogMessage receiver_log;
for (int j = 0; j < 100; ++j) {
RtcpReceiverFrameLogMessage frame_log(RtpTimeTicks().Expand(kRtpTimestamp));
RtcpReceiverEventLogMessage event_log;
event_log.type = FRAME_ACK_SENT;
event_log.event_timestamp = testing_clock.NowTicks();
event_log.delay_delta = base::Milliseconds((j - 50) * kDelayDeltaMs);
frame_log.event_log_messages_.push_back(event_log);
receiver_log.push_back(frame_log);
testing_clock.Advance(base::Milliseconds(kTimeDelayMs));
}
TestRtcpPacketBuilder p;
p.AddRr(kRemoteSsrc, 1);
p.AddRb(kLocalSsrc);
p.AddReceiverLog(kRemoteSsrc);
for (int i = 0; i < 100; ++i) {
p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs);
const int delay = (i - 50) * kDelayDeltaMs;
p.AddReceiverEventLog(static_cast<uint16_t>(delay), FRAME_ACK_SENT, 0);
}
RtcpParser parser(kLocalSsrc, kRemoteSsrc);
EXPECT_TRUE(parser.Parse(p.Reader()));
ExpectReceiverLog(parser, receiver_log);
}
TEST(RtcpUtilityTest, NtpAndTime) {
const int64_t kSecondsbetweenYear1900and2010 = INT64_C(40176 * 24 * 60 * 60);
const int64_t kSecondsbetweenYear1900and2030 = INT64_C(47481 * 24 * 60 * 60);
uint32_t ntp_seconds_1 = 0;
uint32_t ntp_fraction_1 = 0;
base::TimeTicks input_time = base::TimeTicks::Now();
ConvertTimeTicksToNtp(input_time, &ntp_seconds_1, &ntp_fraction_1);
// Verify absolute value.
EXPECT_GT(ntp_seconds_1, kSecondsbetweenYear1900and2010);
EXPECT_LT(ntp_seconds_1, kSecondsbetweenYear1900and2030);
base::TimeTicks out_1 = ConvertNtpToTimeTicks(ntp_seconds_1, ntp_fraction_1);
EXPECT_EQ(input_time, out_1); // Verify inverse.
base::TimeDelta time_delta = base::Milliseconds(1000);
input_time += time_delta;
uint32_t ntp_seconds_2 = 0;
uint32_t ntp_fraction_2 = 0;
ConvertTimeTicksToNtp(input_time, &ntp_seconds_2, &ntp_fraction_2);
base::TimeTicks out_2 = ConvertNtpToTimeTicks(ntp_seconds_2, ntp_fraction_2);
EXPECT_EQ(input_time, out_2); // Verify inverse.
// Verify delta.
EXPECT_EQ((out_2 - out_1), time_delta);
EXPECT_EQ((ntp_seconds_2 - ntp_seconds_1), UINT32_C(1));
EXPECT_NEAR(ntp_fraction_2, ntp_fraction_1, 1);
time_delta = base::Milliseconds(500);
input_time += time_delta;
uint32_t ntp_seconds_3 = 0;
uint32_t ntp_fraction_3 = 0;
ConvertTimeTicksToNtp(input_time, &ntp_seconds_3, &ntp_fraction_3);
base::TimeTicks out_3 = ConvertNtpToTimeTicks(ntp_seconds_3, ntp_fraction_3);
EXPECT_EQ(input_time, out_3); // Verify inverse.
// Verify delta.
EXPECT_EQ((out_3 - out_2), time_delta);
EXPECT_NEAR((ntp_fraction_3 - ntp_fraction_2), 0xffffffff / 2, 1);
}
} // namespace cast
} // namespace media