1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/rtp_packetizer.h"
6 
7 #include <chrono>
8 #include <memory>
9 
10 #include "absl/types/optional.h"
11 #include "cast/streaming/frame_crypto.h"
12 #include "cast/streaming/rtp_defines.h"
13 #include "cast/streaming/rtp_packet_parser.h"
14 #include "cast/streaming/ssrc.h"
15 #include "gtest/gtest.h"
16 #include "util/chrono_helpers.h"
17 #include "util/crypto/random_bytes.h"
18 
19 namespace openscreen {
20 namespace cast {
21 namespace {
22 
23 constexpr RtpPayloadType kPayloadType = RtpPayloadType::kAudioOpus;
24 
25 // Returns true if |needle| is fully within |haystack|.
IsSubspan(absl::Span<const uint8_t> needle,absl::Span<const uint8_t> haystack)26 bool IsSubspan(absl::Span<const uint8_t> needle,
27                absl::Span<const uint8_t> haystack) {
28   return (needle.data() >= haystack.data()) &&
29          ((needle.data() + needle.size()) <=
30           (haystack.data() + haystack.size()));
31 }
32 
33 class RtpPacketizerTest : public testing::Test {
34  public:
35   RtpPacketizerTest() = default;
36   ~RtpPacketizerTest() = default;
37 
packetizer()38   RtpPacketizer* packetizer() { return &packetizer_; }
39 
CreateFrame(FrameId frame_id,bool is_key_frame,milliseconds new_playout_delay,int payload_size) const40   EncryptedFrame CreateFrame(FrameId frame_id,
41                              bool is_key_frame,
42                              milliseconds new_playout_delay,
43                              int payload_size) const {
44     EncodedFrame frame;
45     frame.dependency = is_key_frame ? EncodedFrame::KEY_FRAME
46                                     : EncodedFrame::DEPENDS_ON_ANOTHER;
47     frame.frame_id = frame_id;
48     frame.referenced_frame_id = is_key_frame ? frame_id : (frame_id - 1);
49     frame.rtp_timestamp = RtpTimeTicks() + RtpTimeDelta::FromTicks(987);
50     frame.reference_time = Clock::now();
51     frame.new_playout_delay = new_playout_delay;
52 
53     std::unique_ptr<uint8_t[]> buffer(new uint8_t[payload_size]);
54     for (int i = 0; i < payload_size; ++i) {
55       buffer[i] = static_cast<uint8_t>(i);
56     }
57     frame.data = absl::Span<uint8_t>(buffer.get(), payload_size);
58 
59     return crypto_.Encrypt(frame);
60   }
61 
62   // Generates one of the frame's packets, then parses it and checks for the
63   // expected values. Thus, this test assumes PacketParser is already working
64   // (i.e., all RtpPacketParser unit tests are passing).
TestGeneratePacket(const EncryptedFrame & frame,FramePacketId packet_id)65   void TestGeneratePacket(const EncryptedFrame& frame,
66                           FramePacketId packet_id) {
67     SCOPED_TRACE(testing::Message() << "packet_id=" << packet_id);
68 
69     const int frame_payload_size = frame.data.size();
70     constexpr int kExpectedRtpHeaderSize = 23;
71     const int packet_payload_size =
72         kMaxRtpPacketSizeForIpv4UdpOnEthernet - kExpectedRtpHeaderSize;
73     const int final_packet_payload_size =
74         frame_payload_size % packet_payload_size;
75     const int num_packets = 1 + frame_payload_size / packet_payload_size;
76 
77     // Generate a RTP packet and parse it.
78     uint8_t scratch[kMaxRtpPacketSizeForIpv4UdpOnEthernet];
79     memset(scratch, 0, sizeof(scratch));
80     const auto packet = packetizer_.GeneratePacket(frame, packet_id, scratch);
81     ASSERT_TRUE(IsSubspan(packet, scratch));
82 
83     const auto result = parser_.Parse(packet);
84     ASSERT_TRUE(result);
85 
86     // Check that RTP header fields match expected values.
87     EXPECT_EQ(kPayloadType, result->payload_type);
88     EXPECT_EQ(frame.rtp_timestamp, result->rtp_timestamp);
89     EXPECT_EQ(frame.dependency == EncodedFrame::KEY_FRAME,
90               result->is_key_frame);
91     EXPECT_EQ(frame.frame_id, result->frame_id);
92     EXPECT_EQ(packet_id, result->packet_id);
93     EXPECT_EQ(static_cast<FramePacketId>(num_packets - 1),
94               result->max_packet_id);
95     EXPECT_EQ(frame.referenced_frame_id, result->referenced_frame_id);
96 
97     // The sequence number field MUST be different for each packet, regardless
98     // of whether the exact same packet is being re-generated.
99     if (last_sequence_number_) {
100       EXPECT_EQ(static_cast<uint16_t>(*last_sequence_number_ + 1),
101                 result->sequence_number);
102     }
103     last_sequence_number_ = result->sequence_number;
104 
105     // If there is a playout delay change starting with this |frame|, it must
106     // only be mentioned in the first packet.
107     if (packet_id == FramePacketId{0}) {
108       EXPECT_EQ(frame.new_playout_delay, result->new_playout_delay);
109     } else {
110       EXPECT_EQ(milliseconds(0), result->new_playout_delay);
111     }
112 
113     // Check that the RTP payload is correct for this packet.
114     ASSERT_TRUE(IsSubspan(result->payload, packet));
115     // Last packet is smaller, as its payload is just the remaining bytes.
116     const int expected_payload_size = (int{packet_id} == (num_packets - 1))
117                                           ? final_packet_payload_size
118                                           : packet_payload_size;
119     EXPECT_EQ(expected_payload_size, static_cast<int>(result->payload.size()));
120     const absl::Span<const uint8_t> expected_bytes(
121         frame.data.data() + (packet_id * packet_payload_size),
122         expected_payload_size);
123     EXPECT_EQ(expected_bytes, result->payload);
124   }
125 
126  private:
127   // The RtpPacketizer instance under test, plus some surrounding dependencies
128   // to generate its input and examine its output.
129   const Ssrc ssrc_{GenerateSsrc(true)};
130   const FrameCrypto crypto_{GenerateRandomBytes16(), GenerateRandomBytes16()};
131   RtpPacketizer packetizer_{kPayloadType, ssrc_,
132                             kMaxRtpPacketSizeForIpv4UdpOnEthernet};
133   RtpPacketParser parser_{ssrc_};
134 
135   // absl::nullopt until the random starting sequence number, from the first
136   // packet generated by TestGeneratePacket(), is known.
137   absl::optional<uint16_t> last_sequence_number_;
138 };
139 
140 // Tests that all packets are generated for one key frame, followed by 9 "delta"
141 // frames. The key frame is larger than the other frames, as is typical in a
142 // real-world usage scenario.
TEST_F(RtpPacketizerTest,GeneratesPacketsForSequenceOfFrames)143 TEST_F(RtpPacketizerTest, GeneratesPacketsForSequenceOfFrames) {
144   for (int i = 0; i < 10; ++i) {
145     const bool is_key_frame = (i == 0);
146     const int frame_payload_size = is_key_frame ? 48269 : 10000;
147     const EncryptedFrame frame =
148         CreateFrame(FrameId::first() + i, is_key_frame, milliseconds(0),
149                     frame_payload_size);
150     SCOPED_TRACE(testing::Message() << "frame_id=" << frame.frame_id);
151     const int num_packets = packetizer()->ComputeNumberOfPackets(frame);
152     ASSERT_EQ(is_key_frame ? 34 : 7, num_packets);
153 
154     for (int j = 0; j < num_packets; ++j) {
155       TestGeneratePacket(frame, static_cast<FramePacketId>(j));
156       if (testing::Test::HasFailure()) {
157         return;
158       }
159     }
160   }
161 }
162 
163 // Tests that all packets are generated for a key frame that includes a playout
164 // delay change. Only the first packet should mention the playout delay change.
TEST_F(RtpPacketizerTest,GeneratesPacketsForFrameWithLatencyChange)165 TEST_F(RtpPacketizerTest, GeneratesPacketsForFrameWithLatencyChange) {
166   const int frame_payload_size = 38383;
167   const EncryptedFrame frame = CreateFrame(
168       FrameId::first() + 42, true, milliseconds(543), frame_payload_size);
169   const int num_packets = packetizer()->ComputeNumberOfPackets(frame);
170   ASSERT_EQ(27, num_packets);
171 
172   for (int i = 0; i < num_packets; ++i) {
173     TestGeneratePacket(frame, static_cast<FramePacketId>(i));
174     if (testing::Test::HasFailure()) {
175       return;
176     }
177   }
178 }
179 
180 // Tests that a single, valid RTP packet is generated for a frame with no data
181 // payload. Having no payload is valid with some codecs (e.g., complete audio
182 // silence can be represented by an empty payload).
TEST_F(RtpPacketizerTest,GeneratesOnePacketForFrameWithNoPayload)183 TEST_F(RtpPacketizerTest, GeneratesOnePacketForFrameWithNoPayload) {
184   const int frame_payload_size = 0;
185   const EncryptedFrame frame = CreateFrame(FrameId::first() + 99, false,
186                                            milliseconds(0), frame_payload_size);
187   ASSERT_EQ(1, packetizer()->ComputeNumberOfPackets(frame));
188   TestGeneratePacket(frame, FramePacketId{0});
189 }
190 
191 // Tests that re-generating the same packet for re-transmission works, including
192 // a different sequence counter value in the packet each time.
TEST_F(RtpPacketizerTest,GeneratesPacketForRetransmission)193 TEST_F(RtpPacketizerTest, GeneratesPacketForRetransmission) {
194   const int frame_payload_size = 16384;
195   const EncryptedFrame frame =
196       CreateFrame(FrameId::first(), true, milliseconds(0), frame_payload_size);
197   const int num_packets = packetizer()->ComputeNumberOfPackets(frame);
198   ASSERT_EQ(12, num_packets);
199 
200   for (int i = 0; i < 10; ++i) {
201     // Keep generating the same packet. TestGeneratePacket() will check that a
202     // different sequence number is used each time.
203     TestGeneratePacket(frame, FramePacketId{3});
204   }
205 }
206 
207 }  // namespace
208 }  // namespace cast
209 }  // namespace openscreen
210