// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include "base/bind.h" #include "base/memory/raw_ptr.h" #include "base/test/simple_test_tick_clock.h" #include "base/time/time.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)) {} FakeRtcpTransport(const FakeRtcpTransport&) = delete; FakeRtcpTransport& operator=(const FakeRtcpTransport&) = delete; 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: const raw_ptr clock_; base::TimeDelta packet_delay_; raw_ptr rtcp_session_; // RTCP destination. }; } // namespace class RtcpTest : public ::testing::Test, public RtcpObserver { public: RtcpTest(const RtcpTest&) = delete; RtcpTest& operator=(const RtcpTest&) = delete; 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 sender_clock_; std::unique_ptr 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_; }; 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(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(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