| // 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 |