// 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 "cast/streaming/receiver.h" #include #include #include #include #include #include "absl/types/span.h" #include "cast/streaming/compound_rtcp_parser.h" #include "cast/streaming/constants.h" #include "cast/streaming/encoded_frame.h" #include "cast/streaming/frame_crypto.h" #include "cast/streaming/mock_environment.h" #include "cast/streaming/receiver_packet_router.h" #include "cast/streaming/rtcp_common.h" #include "cast/streaming/rtcp_session.h" #include "cast/streaming/rtp_defines.h" #include "cast/streaming/rtp_packetizer.h" #include "cast/streaming/rtp_time.h" #include "cast/streaming/sender_report_builder.h" #include "cast/streaming/session_config.h" #include "cast/streaming/ssrc.h" #include "cast/streaming/testing/simple_socket_subscriber.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "platform/api/time.h" #include "platform/api/udp_socket.h" #include "platform/base/error.h" #include "platform/base/ip_address.h" #include "platform/base/udp_packet.h" #include "platform/test/fake_clock.h" #include "platform/test/fake_task_runner.h" #include "util/chrono_helpers.h" #include "util/osp_logging.h" using testing::_; using testing::AtLeast; using testing::Gt; using testing::Invoke; using testing::SaveArg; namespace openscreen { namespace cast { namespace { // Receiver configuration. constexpr Ssrc kSenderSsrc = 1; constexpr Ssrc kReceiverSsrc = 2; constexpr int kRtpTimebase = 48000; constexpr milliseconds kTargetPlayoutDelay{100}; constexpr auto kAesKey = std::array{{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}}; constexpr auto kCastIvMask = std::array{{0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00}}; constexpr milliseconds kTargetPlayoutDelayChange{800}; // Additional configuration for the Sender. constexpr RtpPayloadType kRtpPayloadType = RtpPayloadType::kVideoVp8; constexpr int kMaxRtpPacketSize = 64; // A simulated one-way network delay, and round-trip network delay. constexpr auto kOneWayNetworkDelay = milliseconds(3); constexpr auto kRoundTripNetworkDelay = 2 * kOneWayNetworkDelay; static_assert(kRoundTripNetworkDelay < kTargetPlayoutDelay && kRoundTripNetworkDelay < kTargetPlayoutDelayChange, "Network delay must be smaller than target playout delay."); // An EncodedFrame for unit testing, one of a sequence of simulated frames, each // of 10 ms duration. The first frame will be a key frame; and any later frames // will be non-key, dependent on the prior frame. Frame 5 (the 6th frame in the // zero-based sequence) will include a target playout delay change, an increase // to 800 ms. Frames with different IDs will contain vary in their payload data // size, but are always 3 or more packets' worth of data. struct SimulatedFrame : public EncodedFrame { static constexpr milliseconds kFrameDuration = milliseconds(10); static constexpr milliseconds kTargetPlayoutDelayChange = milliseconds(800); static constexpr int kPlayoutChangeAtFrame = 5; SimulatedFrame(Clock::time_point first_frame_reference_time, int which) { frame_id = FrameId::first() + which; if (which == 0) { dependency = EncodedFrame::KEY_FRAME; referenced_frame_id = frame_id; } else { dependency = EncodedFrame::DEPENDS_ON_ANOTHER; referenced_frame_id = frame_id - 1; } rtp_timestamp = GetRtpStartTime() + RtpTimeDelta::FromDuration(kFrameDuration * which, kRtpTimebase); reference_time = first_frame_reference_time + kFrameDuration * which; if (which == kPlayoutChangeAtFrame) { new_playout_delay = kTargetPlayoutDelayChange; } constexpr int kAdditionalBytesEachSuccessiveFrame = 3; buffer_.resize(3 * kMaxRtpPacketSize + which * kAdditionalBytesEachSuccessiveFrame); for (size_t i = 0; i < buffer_.size(); ++i) { buffer_[i] = static_cast(which + static_cast(i)); } data = absl::Span(buffer_); } static RtpTimeTicks GetRtpStartTime() { return RtpTimeTicks::FromTimeSinceOrigin(seconds(0), kRtpTimebase); } static milliseconds GetExpectedPlayoutDelay(int which) { return (which < kPlayoutChangeAtFrame) ? kTargetPlayoutDelay : kTargetPlayoutDelayChange; } private: std::vector buffer_; }; // static constexpr milliseconds SimulatedFrame::kFrameDuration; constexpr milliseconds SimulatedFrame::kTargetPlayoutDelayChange; constexpr int SimulatedFrame::kPlayoutChangeAtFrame; // Processes packets from the Receiver under test, as a real Sender might, and // allows the unit tests to set expectations on events of interest to confirm // proper behavior of the Receiver. class MockSender : public CompoundRtcpParser::Client { public: MockSender(TaskRunner* task_runner, UdpSocket::Client* receiver) : task_runner_(task_runner), receiver_(receiver), sender_endpoint_{ // Use a random IPv6 address in the range reserved for // "documentation purposes." Thus, the following is a fake address // that should be blocked by the OS (and all network packet // routers). But, these tests don't use real sockets, so... IPAddress::Parse("2001:db8:0d93:69c2:fd1a:49a6:a7c0:e8a6").value(), 2344}, rtcp_session_(kSenderSsrc, kReceiverSsrc, FakeClock::now()), sender_report_builder_(&rtcp_session_), rtcp_parser_(&rtcp_session_, this), crypto_(kAesKey, kCastIvMask), rtp_packetizer_(kRtpPayloadType, kSenderSsrc, kMaxRtpPacketSize) {} ~MockSender() override = default; void set_max_feedback_frame_id(FrameId f) { max_feedback_frame_id_ = f; } // Called by the test procedures to generate a Sender Report containing the // given lip-sync timestamps, and send it to the Receiver. The caller must // spin the TaskRunner for the RTCP packet to be delivered to the Receiver. StatusReportId SendSenderReport(Clock::time_point reference_time, RtpTimeTicks rtp_timestamp) { // Generate the Sender Report RTCP packet. uint8_t buffer[kMaxRtpPacketSizeForIpv4UdpOnEthernet]; RtcpSenderReport sender_report; sender_report.reference_time = reference_time; sender_report.rtp_timestamp = rtp_timestamp; const auto packet_and_report_id = sender_report_builder_.BuildPacket(sender_report, buffer); // Send the RTCP packet as a UdpPacket directly to the Receiver instance. UdpPacket packet_to_send(packet_and_report_id.first.begin(), packet_and_report_id.first.end()); packet_to_send.set_source(sender_endpoint_); task_runner_->PostTaskWithDelay( [receiver = receiver_, packet = std::move(packet_to_send)]() mutable { receiver->OnRead(nullptr, ErrorOr(std::move(packet))); }, kOneWayNetworkDelay); return packet_and_report_id.second; } // Sets which frame is currently being sent by this MockSender. Test code must // call SendRtpPackets() to send the packets. void SetFrameBeingSent(const EncodedFrame& frame) { frame_being_sent_ = crypto_.Encrypt(frame); } // Returns a vector containing each packet ID once (of the current frame being // sent). |permutation| controls the sort order of the vector: zero will // provide all the packet IDs in order, and greater values will provide them // in a different, predictable order. std::vector GetAllPacketIds(int permutation) { const int num_packets = rtp_packetizer_.ComputeNumberOfPackets(frame_being_sent_); OSP_CHECK_GT(num_packets, 0); std::vector ids; ids.reserve(num_packets); const FramePacketId last_packet_id = static_cast(num_packets - 1); for (FramePacketId packet_id = 0; packet_id <= last_packet_id; ++packet_id) { ids.push_back(packet_id); } for (int i = 0; i < permutation; ++i) { std::next_permutation(ids.begin(), ids.end()); } return ids; } // Send the specified packets of the current frame being sent. void SendRtpPackets(const std::vector& packets_to_send) { uint8_t buffer[kMaxRtpPacketSize]; for (FramePacketId packet_id : packets_to_send) { const auto span = rtp_packetizer_.GeneratePacket(frame_being_sent_, packet_id, buffer); UdpPacket packet_to_send(span.begin(), span.end()); packet_to_send.set_source(sender_endpoint_); task_runner_->PostTaskWithDelay( [receiver = receiver_, packet = std::move(packet_to_send)]() mutable { receiver->OnRead(nullptr, ErrorOr(std::move(packet))); }, kOneWayNetworkDelay); } } // Called to process a packet from the Receiver. void OnPacketFromReceiver(absl::Span packet) { EXPECT_TRUE(rtcp_parser_.Parse(packet, max_feedback_frame_id_)); } // CompoundRtcpParser::Client implementation: Tests set expectations on these // mocks to confirm that the receiver is providing the right data to the // sender in its RTCP packets. MOCK_METHOD1(OnReceiverReferenceTimeAdvanced, void(Clock::time_point reference_time)); MOCK_METHOD1(OnReceiverReport, void(const RtcpReportBlock& receiver_report)); MOCK_METHOD0(OnReceiverIndicatesPictureLoss, void()); MOCK_METHOD2(OnReceiverCheckpoint, void(FrameId frame_id, milliseconds playout_delay)); MOCK_METHOD1(OnReceiverHasFrames, void(std::vector acks)); MOCK_METHOD1(OnReceiverIsMissingPackets, void(std::vector nacks)); private: TaskRunner* const task_runner_; UdpSocket::Client* const receiver_; const IPEndpoint sender_endpoint_; RtcpSession rtcp_session_; SenderReportBuilder sender_report_builder_; CompoundRtcpParser rtcp_parser_; FrameCrypto crypto_; RtpPacketizer rtp_packetizer_; FrameId max_feedback_frame_id_ = FrameId::first() + kMaxUnackedFrames; EncryptedFrame frame_being_sent_; }; class MockConsumer : public Receiver::Consumer { public: MOCK_METHOD1(OnFramesReady, void(int next_frame_buffer_size)); }; class ReceiverTest : public testing::Test { public: ReceiverTest() : clock_(Clock::now()), task_runner_(&clock_), env_(&FakeClock::now, &task_runner_), packet_router_(&env_), receiver_(&env_, &packet_router_, {/* .sender_ssrc = */ kSenderSsrc, /* .receiver_ssrc = */ kReceiverSsrc, /* .rtp_timebase = */ kRtpTimebase, /* .channels = */ 2, /* .target_playout_delay = */ kTargetPlayoutDelay, /* .aes_secret_key = */ kAesKey, /* .aes_iv_mask = */ kCastIvMask, /* .is_pli_enabled = */ true}), sender_(&task_runner_, &env_) { env_.SetSocketSubscriber(&socket_subscriber_); ON_CALL(env_, SendPacket(_)) .WillByDefault(Invoke([this](absl::Span packet) { task_runner_.PostTaskWithDelay( [sender = &sender_, copy_of_packet = std::vector( packet.begin(), packet.end())]() mutable { sender->OnPacketFromReceiver(std::move(copy_of_packet)); }, kOneWayNetworkDelay); })); receiver_.SetConsumer(&consumer_); } ~ReceiverTest() override = default; Receiver* receiver() { return &receiver_; } MockSender* sender() { return &sender_; } MockConsumer* consumer() { return &consumer_; } void AdvanceClockAndRunTasks(Clock::duration delta) { clock_.Advance(delta); } void RunTasksUntilIdle() { task_runner_.RunTasksUntilIdle(); } // Sends the initial Sender Report with lip-sync timing information to // "unblock" the Receiver, and confirms the Receiver immediately replies with // a corresponding Receiver Report. void ExchangeInitialReportPackets() { const Clock::time_point start_time = FakeClock::now(); sender_.SendSenderReport(start_time, SimulatedFrame::GetRtpStartTime()); AdvanceClockAndRunTasks( kOneWayNetworkDelay); // Transmit report to Receiver. // The Receiver will immediately reply with a Receiver Report. EXPECT_CALL(sender_, OnReceiverCheckpoint(FrameId::leader(), kTargetPlayoutDelay)) .Times(1); AdvanceClockAndRunTasks(kOneWayNetworkDelay); // Transmit reply to Sender. testing::Mock::VerifyAndClearExpectations(&sender_); } // Consume one frame from the Receiver, and verify that it is the same as the // |sent_frame|. Exception: The |reference_time| is the playout time on the // Receiver's end, while it refers to the capture time on the Sender's end. void ConsumeAndVerifyFrame(const SimulatedFrame& sent_frame) { SCOPED_TRACE(testing::Message() << "for frame " << sent_frame.frame_id); const int payload_size = receiver()->AdvanceToNextFrame(); ASSERT_NE(Receiver::kNoFramesReady, payload_size); std::vector buffer(payload_size); EncodedFrame received_frame = receiver()->ConsumeNextFrame(absl::Span(buffer)); EXPECT_EQ(sent_frame.dependency, received_frame.dependency); EXPECT_EQ(sent_frame.frame_id, received_frame.frame_id); EXPECT_EQ(sent_frame.referenced_frame_id, received_frame.referenced_frame_id); EXPECT_EQ(sent_frame.rtp_timestamp, received_frame.rtp_timestamp); EXPECT_EQ(sent_frame.reference_time + kOneWayNetworkDelay + SimulatedFrame::GetExpectedPlayoutDelay(sent_frame.frame_id - FrameId::first()), received_frame.reference_time); EXPECT_EQ(sent_frame.new_playout_delay, received_frame.new_playout_delay); EXPECT_EQ(sent_frame.data, received_frame.data); } // Consume zero or more frames from the Receiver, verifying that they are the // same as the SimulatedFrame that was sent. void ConsumeAndVerifyFrames(int first, int last, Clock::time_point start_time) { for (int i = first; i <= last; ++i) { ConsumeAndVerifyFrame(SimulatedFrame(start_time, i)); } } private: FakeClock clock_; FakeTaskRunner task_runner_; testing::NiceMock env_; ReceiverPacketRouter packet_router_; Receiver receiver_; testing::NiceMock sender_; testing::NiceMock consumer_; SimpleSubscriber socket_subscriber_; }; // Tests that the Receiver processes RTCP packets correctly and sends RTCP // reports at regular intervals. TEST_F(ReceiverTest, ReceivesAndSendsRtcpPackets) { // Sender-side expectations, after the Receiver has processed the first Sender // Report. Clock::time_point receiver_reference_time{}; EXPECT_CALL(*sender(), OnReceiverReferenceTimeAdvanced(_)) .WillOnce(SaveArg<0>(&receiver_reference_time)); RtcpReportBlock receiver_report; EXPECT_CALL(*sender(), OnReceiverReport(_)) .WillOnce(SaveArg<0>(&receiver_report)); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::leader(), kTargetPlayoutDelay)) .Times(1); // Have the MockSender send a Sender Report with lip-sync timing information. const Clock::time_point sender_reference_time = FakeClock::now(); const RtpTimeTicks sender_rtp_timestamp = RtpTimeTicks::FromTimeSinceOrigin(seconds(1), kRtpTimebase); const StatusReportId sender_report_id = sender()->SendSenderReport(sender_reference_time, sender_rtp_timestamp); AdvanceClockAndRunTasks(kRoundTripNetworkDelay); // Expect the MockSender got back a Receiver Report that includes its SSRC and // the last Sender Report ID. testing::Mock::VerifyAndClearExpectations(sender()); EXPECT_EQ(kSenderSsrc, receiver_report.ssrc); EXPECT_EQ(sender_report_id, receiver_report.last_status_report_id); // Confirm the clock offset math: Since the Receiver and MockSender share the // same underlying FakeClock, the Receiver should be ahead of the Sender, // which reflects the simulated one-way network packet travel time (of the // Sender Report). // // Note: The offset can be affected by the lossy conversion when going to and // from the wire-format NtpTimestamps. See the unit tests in // ntp_time_unittest.cc for further discussion. constexpr auto kAllowedNtpRoundingError = microseconds(2); EXPECT_NEAR( to_microseconds(kOneWayNetworkDelay).count(), to_microseconds(receiver_reference_time - sender_reference_time).count(), kAllowedNtpRoundingError.count()); // Without the Sender doing anything, the Receiver should continue providing // RTCP reports at regular intervals. Simulate three intervals of time, // verifying that the Receiver did send reports. Clock::time_point last_receiver_reference_time = receiver_reference_time; for (int i = 0; i < 3; ++i) { receiver_reference_time = Clock::time_point(); EXPECT_CALL(*sender(), OnReceiverReferenceTimeAdvanced(_)) .WillRepeatedly(SaveArg<0>(&receiver_reference_time)); AdvanceClockAndRunTasks(kRtcpReportInterval); testing::Mock::VerifyAndClearExpectations(sender()); EXPECT_LT(last_receiver_reference_time, receiver_reference_time); last_receiver_reference_time = receiver_reference_time; } } // Tests that the Receiver processes RTP packets, which might arrive in-order or // out of order, but such that each frame is completely received in-order. Also, // confirms that target playout delay changes are processed/applied correctly. TEST_F(ReceiverTest, ReceivesFramesInOrder) { const Clock::time_point start_time = FakeClock::now(); ExchangeInitialReportPackets(); EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(10); for (int i = 0; i <= 9; ++i) { EXPECT_CALL(*sender(), OnReceiverCheckpoint( FrameId::first() + i, SimulatedFrame::GetExpectedPlayoutDelay(i))) .Times(1); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(_)).Times(0); sender()->SetFrameBeingSent(SimulatedFrame(start_time, i)); // Send the frame's packets in-order half the time, out-of-order the other // half. const int permutation = (i % 2) ? i : 0; sender()->SendRtpPackets(sender()->GetAllPacketIds(permutation)); AdvanceClockAndRunTasks(kRoundTripNetworkDelay); // The Receiver should immediately ACK once it has received all the RTP // packets to complete the frame. testing::Mock::VerifyAndClearExpectations(sender()); // Advance to next frame transmission time. AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration - kRoundTripNetworkDelay); } // When the Receiver has all of the frames and they are complete, it should // send out a low-frequency periodic RTCP "ping." Verify that there is one and // only one "ping" sent when the clock moves forward by one default report // interval during a period of inactivity. EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + 9, kTargetPlayoutDelayChange)) .Times(1); AdvanceClockAndRunTasks(kRtcpReportInterval); testing::Mock::VerifyAndClearExpectations(sender()); ConsumeAndVerifyFrames(0, 9, start_time); EXPECT_EQ(Receiver::kNoFramesReady, receiver()->AdvanceToNextFrame()); } // Tests that the Receiver processes RTP packets, can receive frames out of // order, and issues the appropriate ACK/NACK feedback to the Sender as it // realizes what it has and what it's missing. TEST_F(ReceiverTest, ReceivesFramesOutOfOrder) { const Clock::time_point start_time = FakeClock::now(); ExchangeInitialReportPackets(); constexpr static int kOutOfOrderFrames[] = {3, 4, 2, 0, 1}; for (int i : kOutOfOrderFrames) { // Expectations are different as each frame is sent and received. switch (i) { case 3: { // Note that frame 4 will not yet be known to the Receiver, and so it // should not be mentioned in any of the feedback for this case. EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::leader(), kTargetPlayoutDelay)) .Times(AtLeast(1)); EXPECT_CALL( *sender(), OnReceiverHasFrames(std::vector({FrameId::first() + 3}))) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(std::vector({ PacketNack{FrameId::first(), kAllPacketsLost}, PacketNack{FrameId::first() + 1, kAllPacketsLost}, PacketNack{FrameId::first() + 2, kAllPacketsLost}, }))) .Times(AtLeast(1)); EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); break; } case 4: { EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::leader(), kTargetPlayoutDelay)) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverHasFrames(std::vector( {FrameId::first() + 3, FrameId::first() + 4}))) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(std::vector({ PacketNack{FrameId::first(), kAllPacketsLost}, PacketNack{FrameId::first() + 1, kAllPacketsLost}, PacketNack{FrameId::first() + 2, kAllPacketsLost}, }))) .Times(AtLeast(1)); EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); break; } case 2: { EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::leader(), kTargetPlayoutDelay)) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverHasFrames(std::vector( {FrameId::first() + 2, FrameId::first() + 3, FrameId::first() + 4}))) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(std::vector({ PacketNack{FrameId::first(), kAllPacketsLost}, PacketNack{FrameId::first() + 1, kAllPacketsLost}, }))) .Times(AtLeast(1)); EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); break; } case 0: { EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first(), kTargetPlayoutDelay)) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverHasFrames(std::vector( {FrameId::first() + 2, FrameId::first() + 3, FrameId::first() + 4}))) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(std::vector( {PacketNack{FrameId::first() + 1, kAllPacketsLost}}))) .Times(AtLeast(1)); EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); break; } case 1: { EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + 4, kTargetPlayoutDelay)) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverHasFrames(_)).Times(0); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(_)).Times(0); EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); break; } default: OSP_NOTREACHED(); } sender()->SetFrameBeingSent(SimulatedFrame(start_time, i)); sender()->SendRtpPackets(sender()->GetAllPacketIds(i)); AdvanceClockAndRunTasks(kRoundTripNetworkDelay); // While there are known incomplete frames, the Receiver should send RTCP // packets more frequently than the default "ping" interval. Thus, advancing // the clock by this much should result in several feedback reports // transmitted to the Sender. AdvanceClockAndRunTasks(kRtcpReportInterval - kRoundTripNetworkDelay); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); } ConsumeAndVerifyFrames(0, 4, start_time); EXPECT_EQ(Receiver::kNoFramesReady, receiver()->AdvanceToNextFrame()); } // Tests that the Receiver will respond to a key frame request from its client // by sending a Picture Loss Indicator (PLI) to the Sender, and then will // automatically stop sending the PLI once a key frame has been received. TEST_F(ReceiverTest, RequestsKeyFrameToRectifyPictureLoss) { const Clock::time_point start_time = FakeClock::now(); ExchangeInitialReportPackets(); // Send and Receive three frames in-order, normally. for (int i = 0; i <= 2; ++i) { EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + i, kTargetPlayoutDelay)) .Times(1); sender()->SetFrameBeingSent(SimulatedFrame(start_time, i)); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(kRoundTripNetworkDelay); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); // Advance to next frame transmission time. AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration - kRoundTripNetworkDelay); } ConsumeAndVerifyFrames(0, 2, start_time); // Simulate the Consumer requesting a key frame after picture loss (e.g., a // decoder failure). Ensure the Sender is immediately notified. EXPECT_CALL(*sender(), OnReceiverIndicatesPictureLoss()).Times(1); receiver()->RequestKeyFrame(); AdvanceClockAndRunTasks(kOneWayNetworkDelay); // Propagate request to Sender. testing::Mock::VerifyAndClearExpectations(sender()); // The Sender sends another frame that is not a key frame and, upon receipt, // the Receiver should repeat its "cry" for a key frame. EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + 3, kTargetPlayoutDelay)) .Times(1); EXPECT_CALL(*sender(), OnReceiverIndicatesPictureLoss()).Times(AtLeast(1)); sender()->SetFrameBeingSent(SimulatedFrame(start_time, 3)); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration - kOneWayNetworkDelay); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); ConsumeAndVerifyFrames(3, 3, start_time); // Finally, the Sender responds to the PLI condition by sending a key frame. // Confirm the Receiver has stopped indicating picture loss after having // received the key frame. EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + 4, kTargetPlayoutDelay)) .Times(1); EXPECT_CALL(*sender(), OnReceiverIndicatesPictureLoss()).Times(0); SimulatedFrame key_frame(start_time, 4); key_frame.dependency = EncodedFrame::KEY_FRAME; key_frame.referenced_frame_id = key_frame.frame_id; sender()->SetFrameBeingSent(key_frame); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); // The client has not yet consumed the key frame, so any calls to // RequestKeyFrame() should not set the PLI condition again. EXPECT_CALL(*sender(), OnReceiverIndicatesPictureLoss()).Times(0); receiver()->RequestKeyFrame(); AdvanceClockAndRunTasks(kOneWayNetworkDelay); testing::Mock::VerifyAndClearExpectations(sender()); // After consuming the requested key frame, the client should be able to set // the PLI condition again with another RequestKeyFrame() call. ConsumeAndVerifyFrame(key_frame); EXPECT_CALL(*sender(), OnReceiverIndicatesPictureLoss()).Times(1); receiver()->RequestKeyFrame(); AdvanceClockAndRunTasks(kOneWayNetworkDelay); testing::Mock::VerifyAndClearExpectations(sender()); } TEST_F(ReceiverTest, PLICanBeDisabled) { receiver()->SetPliEnabledForTesting(false); #if OSP_DCHECK_IS_ON() EXPECT_DEATH(receiver()->RequestKeyFrame(), ".*PLI is not enabled.*"); #else EXPECT_CALL(*sender(), OnReceiverIndicatesPictureLoss()).Times(0); receiver()->RequestKeyFrame(); AdvanceClockAndRunTasks(kOneWayNetworkDelay); testing::Mock::VerifyAndClearExpectations(sender()); #endif } // Tests that the Receiver will start dropping packets once its frame queue is // full (i.e., when the consumer is not pulling them out of the queue). Since // the Receiver will stop ACK'ing frames, the Sender will become stalled. TEST_F(ReceiverTest, EatsItsFill) { const Clock::time_point start_time = FakeClock::now(); ExchangeInitialReportPackets(); // Send and Receive the maximum possible number of frames in-order, normally. for (int i = 0; i < kMaxUnackedFrames; ++i) { EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); EXPECT_CALL(*sender(), OnReceiverCheckpoint( FrameId::first() + i, SimulatedFrame::GetExpectedPlayoutDelay(i))) .Times(1); sender()->SetFrameBeingSent(SimulatedFrame(start_time, i)); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); } // Sending one more frame should be ignored. Over and over. None of the // feedback reports from the Receiver should indicate it is collecting packets // for future frames. int ignored_frame = kMaxUnackedFrames; for (int i = 0; i < 5; ++i) { EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + (ignored_frame - 1), kTargetPlayoutDelayChange)) .Times(AtLeast(0)); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(_)).Times(0); sender()->SetFrameBeingSent(SimulatedFrame(start_time, ignored_frame)); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); } // Consume only one frame, and confirm the Receiver allows only one frame more // to be received. ConsumeAndVerifyFrames(0, 0, start_time); int no_longer_ignored_frame = ignored_frame; ++ignored_frame; EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + no_longer_ignored_frame, kTargetPlayoutDelayChange)) .Times(AtLeast(1)); EXPECT_CALL(*sender(), OnReceiverIsMissingPackets(_)).Times(0); // This frame should be received successfully. sender()->SetFrameBeingSent( SimulatedFrame(start_time, no_longer_ignored_frame)); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration); // This second frame should be ignored, however. sender()->SetFrameBeingSent(SimulatedFrame(start_time, ignored_frame)); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration); testing::Mock::VerifyAndClearExpectations(sender()); testing::Mock::VerifyAndClearExpectations(consumer()); } // Tests that incomplete frames that would be played-out too late are dropped, // but only as inter-frame data dependency requirements permit, and only if no // target playout delay change information would have been missed. TEST_F(ReceiverTest, DropsLateFrames) { const Clock::time_point start_time = FakeClock::now(); ExchangeInitialReportPackets(); // Before any packets have been sent/received, the Receiver should indicate no // frames are ready. EXPECT_EQ(Receiver::kNoFramesReady, receiver()->AdvanceToNextFrame()); // Set a ridiculously-large estimated player processing time so that the logic // thinks every frame going to play out too late. receiver()->SetPlayerProcessingTime(seconds(3)); // In this test there are eight frames total: // - Frame 0: Key frame. // - Frames 1-4: Non-key frames. // - Frame 5: Non-key frame that contains a target playout delay change. // - Frame 6: Key frame. // - Frame 7: Non-key frame. ASSERT_EQ(SimulatedFrame::kPlayoutChangeAtFrame, 5); SimulatedFrame frames[8] = {{start_time, 0}, {start_time, 1}, {start_time, 2}, {start_time, 3}, {start_time, 4}, {start_time, 5}, {start_time, 6}, {start_time, 7}}; frames[6].dependency = EncodedFrame::KEY_FRAME; frames[6].referenced_frame_id = frames[6].frame_id; // Send just packet 1 (NOT packet 0) of all the frames. The Receiver should // never notify the consumer via the callback, nor report that any frames are // ready, because none of the frames have been completely received. EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); EXPECT_CALL(*sender(), OnReceiverCheckpoint(_, _)).Times(0); for (int i = 0; i <= 7; ++i) { sender()->SetFrameBeingSent(frames[i]); // Assumption: There are at least three packets in each frame, else the test // is not exercising the logic meaningfully. ASSERT_LE(size_t{3}, sender()->GetAllPacketIds(0).size()); sender()->SendRtpPackets({FramePacketId{1}}); AdvanceClockAndRunTasks(SimulatedFrame::kFrameDuration); } testing::Mock::VerifyAndClearExpectations(consumer()); testing::Mock::VerifyAndClearExpectations(sender()); EXPECT_EQ(Receiver::kNoFramesReady, receiver()->AdvanceToNextFrame()); // Send all the packets of Frame 6 (the second key frame) and Frame 7. The // Receiver still cannot drop any frames because it has not seen packet 0 of // every prior frame. In other words, it cannot ignore any possibility of a // target playout delay change from the Sender. EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); EXPECT_CALL(*sender(), OnReceiverCheckpoint(_, _)).Times(0); for (int i = 6; i <= 7; ++i) { sender()->SetFrameBeingSent(frames[i]); sender()->SendRtpPackets(sender()->GetAllPacketIds(0)); } AdvanceClockAndRunTasks(kRoundTripNetworkDelay); testing::Mock::VerifyAndClearExpectations(consumer()); testing::Mock::VerifyAndClearExpectations(sender()); EXPECT_EQ(Receiver::kNoFramesReady, receiver()->AdvanceToNextFrame()); // Send packet 0 for all but Frame 5, which contains a target playout delay // change. All but the last two frames will still be incomplete. The Receiver // still cannot drop any frames because it doesn't know whether Frame 5 had a // target playout delay change. EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); EXPECT_CALL(*sender(), OnReceiverCheckpoint(_, _)).Times(0); for (int i = 0; i <= 7; ++i) { if (i == 5) { continue; } sender()->SetFrameBeingSent(frames[i]); sender()->SendRtpPackets({FramePacketId{0}}); } AdvanceClockAndRunTasks(kRoundTripNetworkDelay); testing::Mock::VerifyAndClearExpectations(consumer()); testing::Mock::VerifyAndClearExpectations(sender()); EXPECT_EQ(Receiver::kNoFramesReady, receiver()->AdvanceToNextFrame()); // Finally, send packet 0 for Frame 5. Now, the Receiver will drop every frame // before the completely-received second key frame, as they are all still // incomplete and will play-out too late. When it drops the frames, it will // notify the sender of the new checkpoint so that it stops trying to // re-transmit the dropped frames. EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); EXPECT_CALL(*sender(), OnReceiverCheckpoint(FrameId::first() + 7, kTargetPlayoutDelayChange)) .Times(1); sender()->SetFrameBeingSent(frames[5]); sender()->SendRtpPackets({FramePacketId{0}}); AdvanceClockAndRunTasks(kRoundTripNetworkDelay); // Note: Consuming Frame 6 will trigger the checkpoint advancement, since the // call to AdvanceToNextFrame() contains the frame skipping/dropping logic. ConsumeAndVerifyFrame(frames[6]); testing::Mock::VerifyAndClearExpectations(consumer()); testing::Mock::VerifyAndClearExpectations(sender()); // After consuming Frame 6, the Receiver knows Frame 7 is also available and // should have scheduled an immediate task to notify the Consumer of this. EXPECT_CALL(*consumer(), OnFramesReady(Gt(0))).Times(1); AdvanceClockAndRunTasks(kOneWayNetworkDelay); testing::Mock::VerifyAndClearExpectations(consumer()); // Now consume Frame 7. This shouldn't trigger any further checkpoint // advancement. EXPECT_CALL(*consumer(), OnFramesReady(_)).Times(0); EXPECT_CALL(*sender(), OnReceiverCheckpoint(_, _)).Times(0); ConsumeAndVerifyFrame(frames[7]); AdvanceClockAndRunTasks(kOneWayNetworkDelay); testing::Mock::VerifyAndClearExpectations(consumer()); testing::Mock::VerifyAndClearExpectations(sender()); } } // namespace } // namespace cast } // namespace openscreen