| // Copyright 2020 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/sender_packet_router.h" |
| |
| #include <chrono> |
| |
| #include "cast/streaming/constants.h" |
| #include "cast/streaming/mock_environment.h" |
| #include "cast/streaming/testing/simple_socket_subscriber.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "platform/base/ip_address.h" |
| #include "platform/test/fake_clock.h" |
| #include "platform/test/fake_task_runner.h" |
| #include "util/big_endian.h" |
| #include "util/chrono_helpers.h" |
| #include "util/osp_logging.h" |
| |
| using testing::_; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::Return; |
| |
| namespace openscreen { |
| namespace cast { |
| namespace { |
| |
| const IPEndpoint kRemoteEndpoint{ |
| // Use a random IPv6 address in the range reserved for "documentation |
| // purposes." |
| IPAddress::Parse("2001:db8:0d93:69c2:fd1a:49a6:a7c0:e8a6").value(), 25476}; |
| |
| const IPEndpoint kUnexpectedEndpoint{ |
| IPAddress::Parse("2001:db8:0d93:69c2:fd1a:49a6:a7c0:e8a7").value(), 25476}; |
| |
| // Limited burst parameters to simplify unit testing. |
| constexpr int kMaxPacketsPerBurst = 3; |
| constexpr auto kBurstInterval = milliseconds(10); |
| |
| constexpr Ssrc kAudioReceiverSsrc = 2; |
| constexpr Ssrc kVideoReceiverSsrc = 32; |
| |
| const uint8_t kGarbagePacket[] = { |
| 0x42, 0x61, 0x16, 0x17, 0x26, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, |
| 0x6e, 0x67, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, |
| 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x74, 0x63, 0x70, 0x5f}; |
| |
| // clang-format off |
| const uint8_t kValidAudioRtcpPacket[] = { |
| 0b10000000, // Version=2, Padding=no, ReportCount=0. |
| 201, // RTCP Packet type byte. |
| 0x00, 0x01, // Length of remainder of packet, in 32-bit words. |
| 0x00, 0x00, 0x00, 0x02, // Receiver SSRC. |
| }; |
| |
| const uint8_t kValidAudioRtpPacket[] = { |
| 0b10000000, // Version/Padding byte. |
| 96, // Payload type byte. |
| 0xbe, 0xef, // Sequence number. |
| 9, 8, 7, 6, // RTP timestamp. |
| 0, 0, 0, 2, // SSRC. |
| 0b10000000, // Is key frame, no extensions. |
| 5, // Frame ID. |
| 0xa, 0xb, // Packet ID. |
| 0xa, 0xc, // Max packet ID. |
| 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, // Payload. |
| }; |
| // clang-format on |
| |
| // Returns a copy of an |original| RTCP packet, but with its send-to SSRC |
| // modified to the given |alternate_ssrc|. |
| std::vector<uint8_t> MakeRtcpPacketWithAlternateReceiverSsrc( |
| absl::Span<const uint8_t> original, |
| Ssrc alternate_ssrc) { |
| constexpr int kOffsetToSsrcField = 4; |
| std::vector<uint8_t> out(original.begin(), original.end()); |
| OSP_CHECK_GE(out.size(), kOffsetToSsrcField + sizeof(uint32_t)); |
| WriteBigEndian(uint32_t{alternate_ssrc}, out.data() + kOffsetToSsrcField); |
| return out; |
| } |
| |
| // Serializes the |flag| and |send_time| into the front of |buffer| so the tests |
| // can make unique packets and confirm their identities after passing through |
| // various components. |
| absl::Span<uint8_t> MakeFakePacketWithFlag(char flag, |
| Clock::time_point send_time, |
| absl::Span<uint8_t> buffer) { |
| const Clock::duration::rep ticks = send_time.time_since_epoch().count(); |
| const auto packet_size = sizeof(ticks) + sizeof(flag); |
| buffer = buffer.subspan(0, packet_size); |
| OSP_CHECK_EQ(buffer.size(), packet_size); |
| WriteBigEndian(ticks, buffer.data()); |
| buffer[sizeof(ticks)] = flag; |
| return buffer; |
| } |
| |
| // Same as MakeFakePacketWithFlag(), but for tests that don't use the flag. |
| absl::Span<uint8_t> MakeFakePacket(Clock::time_point send_time, |
| absl::Span<uint8_t> buffer) { |
| return MakeFakePacketWithFlag('?', send_time, buffer); |
| } |
| |
| // Returns the flag that was placed in the given |fake_packet|, or '?' if |
| // unknown. |
| char ParseFlag(absl::Span<const uint8_t> fake_packet) { |
| constexpr auto kFlagOffset = sizeof(Clock::duration::rep); |
| if (fake_packet.size() == (kFlagOffset + sizeof(char))) { |
| return static_cast<char>(fake_packet[kFlagOffset]); |
| } |
| return '?'; |
| } |
| |
| // Deserializes and returns the timestamp that was placed in the given |packet| |
| // by MakeFakePacketWithFlag(). |
| Clock::time_point ParseTimestamp(absl::Span<const uint8_t> fake_packet) { |
| Clock::duration::rep ticks = 0; |
| if (fake_packet.size() >= sizeof(ticks)) { |
| ticks = ReadBigEndian<Clock::duration::rep>(fake_packet.data()); |
| } |
| return Clock::time_point() + Clock::duration(ticks); |
| } |
| |
| // Returns an empty version of |buffer|. |
| absl::Span<uint8_t> ToEmptyPacketBuffer(Clock::time_point send_time, |
| absl::Span<uint8_t> buffer) { |
| return buffer.subspan(0, 0); |
| } |
| |
| class MockSender : public SenderPacketRouter::Sender { |
| public: |
| MockSender() = default; |
| ~MockSender() override = default; |
| |
| MOCK_METHOD(void, |
| OnReceivedRtcpPacket, |
| (Clock::time_point arrival_time, |
| absl::Span<const uint8_t> packet), |
| (override)); |
| MOCK_METHOD(absl::Span<uint8_t>, |
| GetRtcpPacketForImmediateSend, |
| (Clock::time_point send_time, absl::Span<uint8_t> buffer), |
| (override)); |
| MOCK_METHOD(absl::Span<uint8_t>, |
| GetRtpPacketForImmediateSend, |
| (Clock::time_point send_time, absl::Span<uint8_t> buffer), |
| (override)); |
| MOCK_METHOD(Clock::time_point, GetRtpResumeTime, (), (override)); |
| }; |
| |
| class SenderPacketRouterTest : public testing::Test { |
| public: |
| SenderPacketRouterTest() |
| : clock_(Clock::now()), |
| task_runner_(&clock_), |
| env_(&FakeClock::now, &task_runner_), |
| router_(&env_, kMaxPacketsPerBurst, kBurstInterval) { |
| env_.SetSocketSubscriber(&socket_subscriber_); |
| } |
| |
| ~SenderPacketRouterTest() override = default; |
| |
| MockEnvironment* env() { return &env_; } |
| SenderPacketRouter* router() { return &router_; } |
| MockSender* audio_sender() { return &audio_sender_; } |
| MockSender* video_sender() { return &video_sender_; } |
| |
| void SimulatePacketArrivedNow(const IPEndpoint& source, |
| absl::Span<const uint8_t> packet) { |
| static_cast<Environment::PacketConsumer*>(&router_)->OnReceivedPacket( |
| source, env_.now(), std::vector<uint8_t>(packet.begin(), packet.end())); |
| } |
| |
| void AdvanceClockAndRunTasks(Clock::duration delta) { clock_.Advance(delta); } |
| void RunTasksUntilIdle() { task_runner_.RunTasksUntilIdle(); } |
| |
| private: |
| FakeClock clock_; |
| FakeTaskRunner task_runner_; |
| testing::NiceMock<MockEnvironment> env_; |
| SenderPacketRouter router_; |
| testing::NiceMock<MockSender> audio_sender_; |
| testing::NiceMock<MockSender> video_sender_; |
| SimpleSubscriber socket_subscriber_; |
| }; |
| |
| // Tests that the SenderPacketRouter is correctly configured from the specific |
| // burst parameters that were passed to its constructor. This confirms internal |
| // calculations based on these parameters. |
| TEST_F(SenderPacketRouterTest, IsConfiguredFromBurstParameters) { |
| EXPECT_EQ(env()->GetMaxPacketSize(), router()->max_packet_size()); |
| |
| // The following lower-bound/upper-bound values were hand-calculated based on |
| // the arguments that were passed to the SenderPacketRouter constructor, and |
| // assuming a packet size anywhere from 256 bytes to one megabyte. |
| // |
| // The exact value for max_burst_bitrate() is not known here because |
| // Environment::GetMaxPacketSize() depends on the platform and network medium. |
| // To test for an exact value would require duplicating the math in |
| // SenderPacketRouter::ComputeMaxBurstBitrate() here (and then *what* would we |
| // be testing?). |
| EXPECT_LE(614400, router()->max_burst_bitrate()); |
| EXPECT_GE(2147483647, router()->max_burst_bitrate()); |
| } |
| |
| TEST_F(SenderPacketRouterTest, IgnoresPacketsFromUnexpectedSources) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)).Times(0); |
| SimulatePacketArrivedNow(kUnexpectedEndpoint, |
| absl::Span<const uint8_t>(kValidAudioRtcpPacket)); |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| } |
| |
| TEST_F(SenderPacketRouterTest, IgnoresInboundPacketsContainingGarbage) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)).Times(0); |
| SimulatePacketArrivedNow(kUnexpectedEndpoint, |
| absl::Span<const uint8_t>(kGarbagePacket)); |
| SimulatePacketArrivedNow(kRemoteEndpoint, |
| absl::Span<const uint8_t>(kGarbagePacket)); |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| } |
| |
| // Note: RTP packets should be ignored since it wouldn't make sense for a |
| // Receiver to stream media to a Sender. |
| TEST_F(SenderPacketRouterTest, IgnoresInboundRtpPackets) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)).Times(0); |
| SimulatePacketArrivedNow(kUnexpectedEndpoint, |
| absl::Span<const uint8_t>(kValidAudioRtpPacket)); |
| SimulatePacketArrivedNow(kRemoteEndpoint, |
| absl::Span<const uint8_t>(kValidAudioRtpPacket)); |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| } |
| |
| TEST_F(SenderPacketRouterTest, IgnoresInboundRtcpPacketsFromUnknownReceivers) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| const std::vector<uint8_t> rtcp_packet_not_for_me = |
| MakeRtcpPacketWithAlternateReceiverSsrc(kValidAudioRtcpPacket, |
| kAudioReceiverSsrc + 1); |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)).Times(0); |
| SimulatePacketArrivedNow(kUnexpectedEndpoint, |
| absl::Span<const uint8_t>(rtcp_packet_not_for_me)); |
| SimulatePacketArrivedNow(kRemoteEndpoint, |
| absl::Span<const uint8_t>(rtcp_packet_not_for_me)); |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| } |
| |
| // Tests that the SenderPacketRouter forwards packets from Receivers to the |
| // appropriate Sender. |
| TEST_F(SenderPacketRouterTest, RoutesRTCPPacketsFromReceivers) { |
| EXPECT_CALL(*env(), SendPacket(_)).Times(0); |
| |
| const absl::Span<const uint8_t> audio_rtcp_packet(kValidAudioRtcpPacket); |
| std::vector<uint8_t> video_rtcp_packet = |
| MakeRtcpPacketWithAlternateReceiverSsrc(audio_rtcp_packet, |
| kVideoReceiverSsrc); |
| |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| |
| // It should route a valid audio RTCP packet to the audio Sender, and ignore a |
| // valid video RTCP packet (since the video Sender is not yet known to the |
| // SenderPacketRouter). |
| { |
| Clock::time_point arrival_time{}; |
| std::vector<uint8_t> received_packet; |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)) |
| .WillOnce(Invoke( |
| [&](Clock::time_point when, absl::Span<const uint8_t> packet) { |
| arrival_time = when; |
| received_packet.assign(packet.begin(), packet.end()); |
| })); |
| EXPECT_CALL(*video_sender(), OnReceivedRtcpPacket(_, _)).Times(0); |
| |
| const Clock::time_point expected_arrival_time = env()->now(); |
| SimulatePacketArrivedNow(kRemoteEndpoint, audio_rtcp_packet); |
| SimulatePacketArrivedNow(kRemoteEndpoint, video_rtcp_packet); |
| |
| Mock::VerifyAndClear(audio_sender()); |
| EXPECT_EQ(expected_arrival_time, arrival_time); |
| EXPECT_EQ(audio_rtcp_packet, received_packet); |
| |
| Mock::VerifyAndClear(video_sender()); |
| } |
| |
| AdvanceClockAndRunTasks(seconds(1)); |
| |
| // Register the video Sender with the router. Now, confirm audio RTCP packets |
| // still go to the audio Sender and video RTCP packets go to the video Sender. |
| router()->OnSenderCreated(kVideoReceiverSsrc, video_sender()); |
| { |
| Clock::time_point audio_arrival_time{}, video_arrival_time{}; |
| std::vector<uint8_t> received_audio_packet, received_video_packet; |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)) |
| .WillOnce(Invoke( |
| [&](Clock::time_point when, absl::Span<const uint8_t> packet) { |
| audio_arrival_time = when; |
| received_audio_packet.assign(packet.begin(), packet.end()); |
| })); |
| EXPECT_CALL(*video_sender(), OnReceivedRtcpPacket(_, _)) |
| .WillOnce(Invoke( |
| [&](Clock::time_point when, absl::Span<const uint8_t> packet) { |
| video_arrival_time = when; |
| received_video_packet.assign(packet.begin(), packet.end()); |
| })); |
| |
| const Clock::time_point expected_audio_arrival_time = env()->now(); |
| SimulatePacketArrivedNow(kRemoteEndpoint, audio_rtcp_packet); |
| |
| AdvanceClockAndRunTasks(milliseconds(11)); |
| |
| const Clock::time_point expected_video_arrival_time = env()->now(); |
| SimulatePacketArrivedNow(kRemoteEndpoint, video_rtcp_packet); |
| |
| Mock::VerifyAndClear(audio_sender()); |
| EXPECT_EQ(expected_audio_arrival_time, audio_arrival_time); |
| EXPECT_EQ(audio_rtcp_packet, received_audio_packet); |
| |
| Mock::VerifyAndClear(video_sender()); |
| EXPECT_EQ(expected_video_arrival_time, video_arrival_time); |
| EXPECT_EQ(video_rtcp_packet, received_video_packet); |
| } |
| |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| router()->OnSenderDestroyed(kVideoReceiverSsrc); |
| } |
| |
| // Tests that the SenderPacketRouter schedules periodic RTCP packet sends, |
| // starting once the Sender requests the first RTCP send. |
| TEST_F(SenderPacketRouterTest, SchedulesPeriodicTransmissionOfRTCPPackets) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| |
| constexpr int kNumIterations = 5; |
| |
| EXPECT_CALL(*audio_sender(), OnReceivedRtcpPacket(_, _)).Times(0); |
| EXPECT_CALL(*audio_sender(), GetRtcpPacketForImmediateSend(_, _)) |
| .Times(kNumIterations) |
| .WillRepeatedly(Invoke(&MakeFakePacket)); |
| EXPECT_CALL(*audio_sender(), GetRtpPacketForImmediateSend(_, _)).Times(0); |
| ON_CALL(*audio_sender(), GetRtpResumeTime()) |
| .WillByDefault(Return(SenderPacketRouter::kNever)); |
| |
| // Capture every packet sent for analysis at the end of this test. |
| std::vector<std::vector<uint8_t>> packets_sent; |
| EXPECT_CALL(*env(), SendPacket(_)) |
| .WillRepeatedly(Invoke([&](absl::Span<const uint8_t> packet) { |
| packets_sent.emplace_back(packet.begin(), packet.end()); |
| })); |
| |
| const Clock::time_point first_send_time = env()->now(); |
| router()->RequestRtcpSend(kAudioReceiverSsrc); |
| RunTasksUntilIdle(); // The first RTCP packet should be sent immediately. |
| for (int i = 1; i < kNumIterations; ++i) { |
| AdvanceClockAndRunTasks(kRtcpReportInterval); |
| } |
| |
| // Ensure each RTCP packet was sent and in-sequence. |
| Mock::VerifyAndClear(env()); |
| ASSERT_EQ(kNumIterations, static_cast<int>(packets_sent.size())); |
| for (int i = 0; i < kNumIterations; ++i) { |
| const Clock::time_point expected_send_time = |
| first_send_time + i * kRtcpReportInterval; |
| EXPECT_EQ(expected_send_time, ParseTimestamp(packets_sent[i])); |
| } |
| |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| } |
| |
| // Tests that the SenderPacketRouter schedules RTP packet bursts from a single |
| // Sender. |
| TEST_F(SenderPacketRouterTest, SchedulesAndTransmitsRTPBursts) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| router()->OnSenderCreated(kVideoReceiverSsrc, video_sender()); |
| |
| // Capture every packet sent for analysis at the end of this test. |
| std::vector<std::vector<uint8_t>> packets_sent; |
| EXPECT_CALL(*env(), SendPacket(_)) |
| .WillRepeatedly(Invoke([&](absl::Span<const uint8_t> packet) { |
| packets_sent.emplace_back(packet.begin(), packet.end()); |
| })); |
| |
| // Simulate a typical video Sender RTP at-startup sending sequence: First, at |
| // t=0ms, the Sender wants to send its large 10-packet key frame. This will |
| // require four bursts, since only 3 packets can be sent per burst. |
| // |
| // While the first frame is being sent, a smaller 4-packet frame is enqueued, |
| // and the Sender will want to start sending this immediately after the first |
| // frame. Part of this second frame will be sent in the fourth burst, and the |
| // rest in the fifth burst. |
| // |
| // After the fifth burst, the Sender will schedule a "kickstart packet" for |
| // 25ms later. However, when the SenderPacketRouter later asks the Sender for |
| // that packet, the Sender will change its mind and decide not to send |
| // anything. |
| // |
| // At t=100ms, the next frame of video is enqueued in the Sender and it |
| // requests that RTP sending resume for that. This is a small 1-packet frame. |
| const Clock::time_point start_time = env()->now(); |
| int num_get_rtp_calls = 0; |
| EXPECT_CALL(*video_sender(), GetRtpPacketForImmediateSend(_, _)) |
| .Times(14 + 2) |
| .WillRepeatedly( |
| Invoke([&](Clock::time_point send_time, absl::Span<uint8_t> buffer) { |
| ++num_get_rtp_calls; |
| |
| // 14 packets are sent: The first through fourth bursts send three |
| // packets each, and the fifth burst sends two. |
| if (num_get_rtp_calls <= 14) { |
| return MakeFakePacket(send_time, buffer); |
| } |
| |
| // 2 "done signals" are then sent: One is at the end of the fifth |
| // burst, one is for a "nothing to send" sixth burst. |
| return ToEmptyPacketBuffer(send_time, buffer); |
| })); |
| const Clock::time_point kickstart_time = |
| start_time + 4 * kBurstInterval + milliseconds(25); |
| int num_get_resume_calls = 0; |
| EXPECT_CALL(*video_sender(), GetRtpResumeTime()) |
| .Times(4 + 1 + 1) |
| .WillRepeatedly(Invoke([&] { |
| ++num_get_resume_calls; |
| |
| // After each of the first through fourth bursts, the Sender wants to |
| // transmit more right away. |
| if (num_get_resume_calls <= 4) { |
| return env()->now(); |
| } |
| |
| // After the fifth burst, the Sender requests resuming for kickstart |
| // later. |
| if (num_get_resume_calls == 5) { |
| return kickstart_time; |
| } |
| |
| // After the sixth burst, the Sender pauses RTP sending indefinitely. |
| return SenderPacketRouter::kNever; |
| })); |
| router()->RequestRtpSend(kVideoReceiverSsrc); |
| // Execute first burst. |
| RunTasksUntilIdle(); |
| // Execute second through fifth bursts. |
| for (int i = 1; i <= 4; ++i) { |
| AdvanceClockAndRunTasks(kBurstInterval); |
| } |
| // Execute the sixth burst at the kickstart time. |
| AdvanceClockAndRunTasks(kickstart_time - env()->now()); |
| Mock::VerifyAndClear(video_sender()); |
| |
| // Now, resume RTP sending for one more 1-packet frame, and then pause RTP |
| // sending again. |
| EXPECT_CALL(*video_sender(), GetRtpPacketForImmediateSend(_, _)) |
| .WillOnce(Invoke(&MakeFakePacket)) // Frame 2, only packet. |
| .WillOnce(Invoke(&ToEmptyPacketBuffer)); // Done for now. |
| // After the seventh burst, the Sender pauses RTP sending again. |
| EXPECT_CALL(*video_sender(), GetRtpResumeTime()) |
| .WillOnce(Return(SenderPacketRouter::kNever)); |
| // Advance to the resume time. Nothing should happen until RequestRtpSend() is |
| // called. |
| const Clock::time_point resume_time = start_time + milliseconds(100); |
| AdvanceClockAndRunTasks(resume_time - env()->now()); |
| router()->RequestRtpSend(kVideoReceiverSsrc); |
| // Execute seventh burst. |
| RunTasksUntilIdle(); |
| // Run for one more second, but nothing should be happening since sending is |
| // paused. |
| AdvanceClockAndRunTasks(seconds(1)); |
| Mock::VerifyAndClear(video_sender()); |
| |
| // Confirm 15 packets got sent and contain the expected data (which tracks |
| // when they were sent). |
| ASSERT_EQ(15, static_cast<int>(packets_sent.size())); |
| Clock::time_point expected_time; |
| int packet_idx = 0; |
| // First burst through fourth burst. |
| for (int burst_number = 0; burst_number < 4; ++burst_number) { |
| expected_time = start_time + burst_number * kBurstInterval; |
| EXPECT_EQ(expected_time, ParseTimestamp(packets_sent[packet_idx++])); |
| EXPECT_EQ(expected_time, ParseTimestamp(packets_sent[packet_idx++])); |
| EXPECT_EQ(expected_time, ParseTimestamp(packets_sent[packet_idx++])); |
| } |
| // Fifth burst. |
| expected_time += kBurstInterval; |
| EXPECT_EQ(expected_time, ParseTimestamp(packets_sent[packet_idx++])); |
| EXPECT_EQ(expected_time, ParseTimestamp(packets_sent[packet_idx++])); |
| // Seventh burst (sixth burst sent nothing). |
| EXPECT_EQ(resume_time, ParseTimestamp(packets_sent[packet_idx++])); |
| |
| router()->OnSenderDestroyed(kVideoReceiverSsrc); |
| } |
| |
| // Tests that the SenderPacketRouter schedules packet sends based on transmit |
| // prority: RTCP before RTP, and the audio Sender's packets before the video |
| // Sender's. |
| TEST_F(SenderPacketRouterTest, SchedulesAndTransmitsAccountingForPriority) { |
| env()->set_remote_endpoint(kRemoteEndpoint); |
| ASSERT_LT(ComparePriority(kAudioReceiverSsrc, kVideoReceiverSsrc), 0); |
| router()->OnSenderCreated(kVideoReceiverSsrc, video_sender()); |
| router()->OnSenderCreated(kAudioReceiverSsrc, audio_sender()); |
| |
| // Capture every packet sent for analysis at the end of this test. |
| std::vector<std::vector<uint8_t>> packets_sent; |
| EXPECT_CALL(*env(), SendPacket(_)) |
| .WillRepeatedly(Invoke([&](absl::Span<const uint8_t> packet) { |
| packets_sent.emplace_back(packet.begin(), packet.end()); |
| })); |
| |
| // These indicate how often one packet will be sent from each Sender. |
| constexpr Clock::duration kAudioRtpInterval = milliseconds(10); |
| constexpr Clock::duration kVideoRtpInterval = milliseconds(33); |
| |
| // Note: The priority flags used in this test ('0'..'3') indicate |
| // lowest-to-highest priority. |
| EXPECT_CALL(*audio_sender(), GetRtcpPacketForImmediateSend(_, _)) |
| .WillRepeatedly( |
| Invoke([](Clock::time_point send_time, absl::Span<uint8_t> buffer) { |
| return MakeFakePacketWithFlag('3', send_time, buffer); |
| })); |
| int num_audio_get_rtp_calls = 0; |
| EXPECT_CALL(*audio_sender(), GetRtpPacketForImmediateSend(_, _)) |
| .WillRepeatedly( |
| Invoke([&](Clock::time_point send_time, absl::Span<uint8_t> buffer) { |
| // Alternate between returning a single packet and a "done for now" |
| // signal. |
| ++num_audio_get_rtp_calls; |
| if (num_audio_get_rtp_calls % 2) { |
| return MakeFakePacketWithFlag('1', send_time, buffer); |
| } |
| return buffer.subspan(0, 0); |
| })); |
| EXPECT_CALL(*video_sender(), GetRtcpPacketForImmediateSend(_, _)) |
| .WillRepeatedly( |
| Invoke([](Clock::time_point send_time, absl::Span<uint8_t> buffer) { |
| return MakeFakePacketWithFlag('2', send_time, buffer); |
| })); |
| int num_video_get_rtp_calls = 0; |
| EXPECT_CALL(*video_sender(), GetRtpPacketForImmediateSend(_, _)) |
| .WillRepeatedly( |
| Invoke([&](Clock::time_point send_time, absl::Span<uint8_t> buffer) { |
| // Alternate between returning a single packet and a "done for now" |
| // signal. |
| ++num_video_get_rtp_calls; |
| if (num_video_get_rtp_calls % 2) { |
| return MakeFakePacketWithFlag('0', send_time, buffer); |
| } |
| return buffer.subspan(0, 0); |
| })); |
| EXPECT_CALL(*audio_sender(), GetRtpResumeTime()).WillRepeatedly(Invoke([&] { |
| return env()->now() + kAudioRtpInterval; |
| })); |
| EXPECT_CALL(*video_sender(), GetRtpResumeTime()).WillRepeatedly(Invoke([&] { |
| return env()->now() + kVideoRtpInterval; |
| })); |
| |
| // Request starting both RTCP and RTP sends for both Senders, in a random |
| // order. |
| router()->RequestRtcpSend(kVideoReceiverSsrc); |
| router()->RequestRtpSend(kAudioReceiverSsrc); |
| router()->RequestRtcpSend(kAudioReceiverSsrc); |
| router()->RequestRtpSend(kVideoReceiverSsrc); |
| |
| // Run the SenderPacketRouter for 3 seconds. |
| constexpr Clock::duration kSimulationDuration = seconds(3); |
| constexpr Clock::duration kSimulationStepPeriod = milliseconds(1); |
| const Clock::time_point start_time = env()->now(); |
| RunTasksUntilIdle(); |
| const Clock::time_point end_time = start_time + kSimulationDuration; |
| while (env()->now() <= end_time) { |
| AdvanceClockAndRunTasks(kSimulationStepPeriod); |
| } |
| |
| // Examine the packets that were actually sent, and confirm that the priority |
| // ordering was maintained. |
| ASSERT_EQ(384, static_cast<int>(packets_sent.size())); |
| // The very first packet sent should be an audio RTCP packet. |
| EXPECT_EQ('3', ParseFlag(packets_sent[0])); |
| EXPECT_EQ(start_time, ParseTimestamp(packets_sent[0])); |
| // Scan the rest, checking that packets sent in the same burst (i.e., having |
| // the same send timestamp) were sent in priority order. |
| char last_priority_flag = '3'; |
| Clock::time_point last_timestamp = start_time; |
| for (int i = 1; i < static_cast<int>(packets_sent.size()) && |
| !testing::Test::HasFailure(); |
| ++i) { |
| const char priority_flag = ParseFlag(packets_sent[i]); |
| const Clock::time_point timestamp = ParseTimestamp(packets_sent[i]); |
| EXPECT_LE(last_timestamp, timestamp) << "packet[" << i << ']'; |
| if (timestamp == last_timestamp) { |
| EXPECT_GT(last_priority_flag, priority_flag) << "packet[" << i << ']'; |
| } |
| last_priority_flag = priority_flag; |
| last_timestamp = timestamp; |
| } |
| |
| router()->OnSenderDestroyed(kVideoReceiverSsrc); |
| router()->OnSenderDestroyed(kAudioReceiverSsrc); |
| } |
| |
| } // namespace |
| } // namespace cast |
| } // namespace openscreen |