blob: db53457c3b1a02da4e00c7af15da0980d336632a [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 <stddef.h>
#include <stdint.h>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/net/rtcp/receiver_rtcp_session.h"
#include "media/cast/net/rtcp/rtcp_builder.h"
#include "media/cast/net/rtcp/rtcp_session.h"
#include "media/cast/net/rtcp/rtcp_utility.h"
#include "media/cast/net/rtcp/sender_rtcp_session.h"
#include "media/cast/test/skewed_tick_clock.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
namespace cast {
namespace {
media::cast::RtcpTimeData CreateRtcpTimeData(base::TimeTicks now) {
media::cast::RtcpTimeData ret;
ret.timestamp = now;
media::cast::ConvertTimeTicksToNtp(now, &ret.ntp_seconds, &ret.ntp_fraction);
return ret;
}
using testing::_;
static const uint32_t kSenderSsrc = 0x10203;
static const uint32_t kReceiverSsrc = 0x40506;
static const int kInitialReceiverClockOffsetSeconds = -5;
static const uint16_t kTargetDelayMs = 100;
class FakeRtcpTransport : public PacedPacketSender {
public:
explicit FakeRtcpTransport(base::SimpleTestTickClock* clock)
: clock_(clock), packet_delay_(base::Milliseconds(42)) {}
void set_rtcp_destination(RtcpSession* rtcp_session) {
rtcp_session_ = rtcp_session;
}
base::TimeDelta packet_delay() const { return packet_delay_; }
void set_packet_delay(base::TimeDelta delay) { packet_delay_ = delay; }
bool SendRtcpPacket(uint32_t ssrc, PacketRef packet) final {
clock_->Advance(packet_delay_);
rtcp_session_->IncomingRtcpPacket(&packet->data[0], packet->data.size());
return true;
}
bool SendPackets(const SendPacketVector& packets) final { return false; }
bool ResendPackets(const SendPacketVector& packets,
const DedupInfo& dedup_info) final {
return false;
}
void CancelSendingPacket(const PacketKey& packet_key) final {}
private:
base::SimpleTestTickClock* const clock_;
base::TimeDelta packet_delay_;
RtcpSession* rtcp_session_; // RTCP destination.
DISALLOW_COPY_AND_ASSIGN(FakeRtcpTransport);
};
} // namespace
class RtcpTest : public ::testing::Test, public RtcpObserver {
protected:
RtcpTest()
: sender_clock_(new base::SimpleTestTickClock()),
receiver_clock_(new test::SkewedTickClock(sender_clock_.get())),
rtp_sender_pacer_(sender_clock_.get()),
rtp_receiver_pacer_(sender_clock_.get()),
rtcp_at_rtp_sender_(sender_clock_.get(),
&rtp_sender_pacer_,
this,
kSenderSsrc,
kReceiverSsrc),
rtcp_at_rtp_receiver_(receiver_clock_.get(),
kReceiverSsrc,
kSenderSsrc),
received_pli_(false) {
sender_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
receiver_clock_->SetSkew(1.0, // No skew.
base::Seconds(kInitialReceiverClockOffsetSeconds));
rtp_sender_pacer_.set_rtcp_destination(&rtcp_at_rtp_receiver_);
rtp_receiver_pacer_.set_rtcp_destination(&rtcp_at_rtp_sender_);
}
~RtcpTest() override = default;
// RtcpObserver implementation.
void OnReceivedCastMessage(const RtcpCastMessage& cast_message) override {
last_cast_message_ = cast_message;
}
void OnReceivedRtt(base::TimeDelta round_trip_time) override {
current_round_trip_time_ = round_trip_time;
}
void OnReceivedReceiverLog(const RtcpReceiverLogMessage& logs) override {
RtcpReceiverLogMessage().swap(last_logs_);
// Make a copy of the logs.
for (const RtcpReceiverFrameLogMessage& frame_log_msg : logs) {
last_logs_.push_back(
RtcpReceiverFrameLogMessage(frame_log_msg.rtp_timestamp_));
for (const RtcpReceiverEventLogMessage& event_log_msg :
frame_log_msg.event_log_messages_) {
RtcpReceiverEventLogMessage event_log;
event_log.type = event_log_msg.type;
event_log.event_timestamp = event_log_msg.event_timestamp;
event_log.delay_delta = event_log_msg.delay_delta;
event_log.packet_id = event_log_msg.packet_id;
last_logs_.back().event_log_messages_.push_back(event_log);
}
}
}
void OnReceivedPli() override { received_pli_ = true; }
PacketRef BuildRtcpPacketFromRtpReceiver(
const RtcpTimeData& time_data,
const RtcpCastMessage* cast_message,
const RtcpPliMessage* pli_message,
base::TimeDelta target_delay,
const ReceiverRtcpEventSubscriber::RtcpEvents* rtcp_events,
const RtpReceiverStatistics* rtp_receiver_statistics) {
RtcpBuilder builder(rtcp_at_rtp_receiver_.local_ssrc());
builder.Start();
RtcpReceiverReferenceTimeReport rrtr;
rrtr.ntp_seconds = time_data.ntp_seconds;
rrtr.ntp_fraction = time_data.ntp_fraction;
builder.AddRrtr(rrtr);
RtcpReportBlock report_block;
if (rtp_receiver_statistics) {
report_block.remote_ssrc = 0;
report_block.media_ssrc = rtcp_at_rtp_receiver_.remote_ssrc();
report_block.fraction_lost = rtp_receiver_statistics->fraction_lost;
report_block.cumulative_lost = rtp_receiver_statistics->cumulative_lost;
report_block.extended_high_sequence_number =
rtp_receiver_statistics->extended_high_sequence_number;
report_block.jitter = rtp_receiver_statistics->jitter;
report_block.last_sr = rtcp_at_rtp_receiver_.last_report_truncated_ntp();
base::TimeTicks last_report_received_time =
rtcp_at_rtp_receiver_.time_last_report_received();
if (!last_report_received_time.is_null()) {
uint32_t delay_seconds = 0;
uint32_t delay_fraction = 0;
base::TimeDelta delta = time_data.timestamp - last_report_received_time;
ConvertTimeToFractions(delta.InMicroseconds(), &delay_seconds,
&delay_fraction);
report_block.delay_since_last_sr =
ConvertToNtpDiff(delay_seconds, delay_fraction);
} else {
report_block.delay_since_last_sr = 0;
}
builder.AddRR(&report_block);
}
if (cast_message)
builder.AddCast(*cast_message, target_delay);
if (pli_message)
builder.AddPli(*pli_message);
if (rtcp_events)
builder.AddReceiverLog(*rtcp_events);
return builder.Finish();
}
std::unique_ptr<base::SimpleTestTickClock> sender_clock_;
std::unique_ptr<test::SkewedTickClock> receiver_clock_;
FakeRtcpTransport rtp_sender_pacer_;
FakeRtcpTransport rtp_receiver_pacer_;
SenderRtcpSession rtcp_at_rtp_sender_;
ReceiverRtcpSession rtcp_at_rtp_receiver_;
base::TimeDelta current_round_trip_time_;
RtcpCastMessage last_cast_message_;
RtcpReceiverLogMessage last_logs_;
bool received_pli_;
private:
DISALLOW_COPY_AND_ASSIGN(RtcpTest);
};
TEST_F(RtcpTest, LipSyncGleanedFromSenderReport) {
// Initially, expect no lip-sync info receiver-side without having first
// received a RTCP packet.
base::TimeTicks reference_time;
RtpTimeTicks rtp_timestamp;
ASSERT_FALSE(rtcp_at_rtp_receiver_.GetLatestLipSyncTimes(&rtp_timestamp,
&reference_time));
// Send a Sender Report to the receiver.
const base::TimeTicks reference_time_sent = sender_clock_->NowTicks();
const RtpTimeTicks rtp_timestamp_sent =
RtpTimeTicks().Expand(UINT32_C(0xbee5));
rtcp_at_rtp_sender_.SendRtcpReport(reference_time_sent, rtp_timestamp_sent, 1,
1);
// Now the receiver should have lip-sync info. Confirm that the lip-sync
// reference time is the same as that sent.
EXPECT_TRUE(rtcp_at_rtp_receiver_.GetLatestLipSyncTimes(&rtp_timestamp,
&reference_time));
const base::TimeTicks rolled_back_time =
(reference_time -
// Roll-back relative clock offset:
base::Seconds(kInitialReceiverClockOffsetSeconds) -
// Roll-back packet transmission time (because RTT is not yet known):
rtp_sender_pacer_.packet_delay());
EXPECT_NEAR(0, (reference_time_sent - rolled_back_time).InMicroseconds(), 5);
EXPECT_EQ(rtp_timestamp_sent, rtp_timestamp);
}
TEST_F(RtcpTest, RoundTripTimesDeterminedFromReportPingPong) {
const int iterations = 12;
// Sender does not know the RTT yet.
ASSERT_EQ(base::TimeDelta(), rtcp_at_rtp_sender_.current_round_trip_time());
// Do a number of ping-pongs, checking how the round trip times are measured
// by the sender.
base::TimeDelta expected_rtt_according_to_sender;
for (int i = 0; i < iterations; ++i) {
const base::TimeDelta one_way_trip_time =
base::Milliseconds(static_cast<int64_t>(1) << i);
rtp_sender_pacer_.set_packet_delay(one_way_trip_time);
rtp_receiver_pacer_.set_packet_delay(one_way_trip_time);
// Sender --> Receiver
base::TimeTicks reference_time_sent = sender_clock_->NowTicks();
const RtpTimeTicks rtp_timestamp_sent =
RtpTimeTicks().Expand<uint32_t>(0xbee5) + RtpTimeDelta::FromTicks(i);
rtcp_at_rtp_sender_.SendRtcpReport(reference_time_sent, rtp_timestamp_sent,
1, 1);
EXPECT_EQ(expected_rtt_according_to_sender,
rtcp_at_rtp_sender_.current_round_trip_time());
// Validate last reported callback value is same as that reported by method.
EXPECT_EQ(current_round_trip_time_,
rtcp_at_rtp_sender_.current_round_trip_time());
// Receiver --> Sender
RtpReceiverStatistics stats;
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(
CreateRtcpTimeData(receiver_clock_->NowTicks()), nullptr, nullptr,
base::TimeDelta(), nullptr, &stats));
expected_rtt_according_to_sender = one_way_trip_time * 2;
EXPECT_EQ(expected_rtt_according_to_sender,
rtcp_at_rtp_sender_.current_round_trip_time());
}
}
TEST_F(RtcpTest, ReportCastFeedback) {
// Sender has sent all frames up to and including first+5.
rtcp_at_rtp_sender_.WillSendFrame(FrameId::first() + 5);
// ACK all frames up to and including first+5, except NACK a few in first+1
// and first+2.
RtcpCastMessage cast_message(kSenderSsrc);
cast_message.ack_frame_id = FrameId::first() + 5;
PacketIdSet missing_packets1 = {3, 4};
cast_message.missing_frames_and_packets[FrameId::first() + 1] =
missing_packets1;
PacketIdSet missing_packets2 = {5, 6};
cast_message.missing_frames_and_packets[FrameId::first() + 2] =
missing_packets2;
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(
CreateRtcpTimeData(base::TimeTicks()), &cast_message, nullptr,
base::Milliseconds(kTargetDelayMs), nullptr, nullptr));
EXPECT_EQ(last_cast_message_.ack_frame_id, cast_message.ack_frame_id);
EXPECT_EQ(last_cast_message_.target_delay_ms, kTargetDelayMs);
EXPECT_EQ(last_cast_message_.missing_frames_and_packets.size(),
cast_message.missing_frames_and_packets.size());
EXPECT_TRUE(
std::equal(cast_message.missing_frames_and_packets.begin(),
cast_message.missing_frames_and_packets.end(),
last_cast_message_.missing_frames_and_packets.begin()));
}
TEST_F(RtcpTest, ReportPli) {
RtcpPliMessage pli_message(kSenderSsrc);
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(CreateRtcpTimeData(base::TimeTicks()),
nullptr, &pli_message, base::TimeDelta(),
nullptr, nullptr));
EXPECT_TRUE(received_pli_);
}
TEST_F(RtcpTest, DropLateRtcpPacket) {
// Sender has sent all frames up to and including first+2.
rtcp_at_rtp_sender_.WillSendFrame(FrameId::first() + 2);
// Receiver ACKs first+1.
RtcpCastMessage cast_message(kSenderSsrc);
cast_message.ack_frame_id = FrameId::first() + 1;
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(
CreateRtcpTimeData(receiver_clock_->NowTicks()), &cast_message,
nullptr, base::Milliseconds(kTargetDelayMs), nullptr, nullptr));
// Receiver ACKs first+2, but with a too-old timestamp.
RtcpCastMessage late_cast_message(kSenderSsrc);
late_cast_message.ack_frame_id = FrameId::first() + 2;
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(
CreateRtcpTimeData(receiver_clock_->NowTicks() - base::Seconds(10)),
&late_cast_message, nullptr, base::TimeDelta(), nullptr, nullptr));
// Validate data from second packet is dropped.
EXPECT_EQ(last_cast_message_.ack_frame_id, cast_message.ack_frame_id);
EXPECT_EQ(last_cast_message_.target_delay_ms, kTargetDelayMs);
// Re-send with fresh timestamp
late_cast_message.ack_frame_id = FrameId::first() + 2;
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(
CreateRtcpTimeData(receiver_clock_->NowTicks()), &late_cast_message,
nullptr, base::TimeDelta(), nullptr, nullptr));
EXPECT_EQ(last_cast_message_.ack_frame_id, late_cast_message.ack_frame_id);
EXPECT_EQ(last_cast_message_.target_delay_ms, 0);
}
TEST_F(RtcpTest, ReportReceiverEvents) {
const RtpTimeTicks kRtpTimeStamp =
media::cast::RtpTimeTicks().Expand(UINT32_C(100));
const base::TimeTicks kEventTimestamp = receiver_clock_->NowTicks();
const base::TimeDelta kDelayDelta = base::Milliseconds(100);
RtcpEvent event;
event.type = FRAME_ACK_SENT;
event.timestamp = kEventTimestamp;
event.delay_delta = kDelayDelta;
ReceiverRtcpEventSubscriber::RtcpEvents rtcp_events;
rtcp_events.push_back(std::make_pair(kRtpTimeStamp, event));
rtp_receiver_pacer_.SendRtcpPacket(
rtcp_at_rtp_receiver_.local_ssrc(),
BuildRtcpPacketFromRtpReceiver(
CreateRtcpTimeData(receiver_clock_->NowTicks()), nullptr, nullptr,
base::TimeDelta(), &rtcp_events, nullptr));
ASSERT_EQ(1UL, last_logs_.size());
RtcpReceiverFrameLogMessage frame_log = last_logs_.front();
EXPECT_EQ(frame_log.rtp_timestamp_, kRtpTimeStamp);
ASSERT_EQ(1UL, frame_log.event_log_messages_.size());
RtcpReceiverEventLogMessage log_msg = frame_log.event_log_messages_.back();
EXPECT_EQ(log_msg.type, event.type);
EXPECT_EQ(log_msg.delay_delta, event.delay_delta);
// Only 24 bits of event timestamp sent on wire.
uint32_t event_ts =
(event.timestamp - base::TimeTicks()).InMilliseconds() & 0xffffff;
uint32_t log_msg_ts =
(log_msg.event_timestamp - base::TimeTicks()).InMilliseconds() & 0xffffff;
EXPECT_EQ(log_msg_ts, event_ts);
}
} // namespace cast
} // namespace media