red_payload_splitter_unittest.cc (15609B)
1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // Unit tests for RedPayloadSplitter class. 12 13 #include "modules/audio_coding/neteq/red_payload_splitter.h" 14 15 #include <cstddef> 16 #include <cstdint> 17 #include <cstring> 18 #include <optional> 19 #include <utility> // pair 20 21 #include "api/audio_codecs/audio_format.h" 22 #include "api/environment/environment.h" 23 #include "api/environment/environment_factory.h" 24 #include "api/make_ref_counted.h" 25 #include "modules/audio_coding/neteq/decoder_database.h" 26 #include "modules/audio_coding/neteq/packet.h" 27 #include "rtc_base/checks.h" 28 #include "rtc_base/numerics/safe_conversions.h" 29 #include "test/gtest.h" 30 #include "test/mock_audio_decoder_factory.h" 31 32 using ::testing::Return; 33 using ::testing::ReturnNull; 34 35 namespace webrtc { 36 37 static const int kRedPayloadType = 100; 38 static const size_t kPayloadLength = 10; 39 static const uint16_t kSequenceNumber = 0; 40 static const uint32_t kBaseTimestamp = 0x12345678; 41 42 // A possible Opus packet that contains FEC is the following. 43 // The frame is 20 ms in duration. 44 // 45 // 0 1 2 3 46 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 47 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 // |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x| | 49 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 50 // | Compressed frame 1 (N-2 bytes)... : 51 // : | 52 // | | 53 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 void CreateOpusFecPayload(uint8_t* payload, 55 size_t payload_length, 56 uint8_t payload_value) { 57 if (payload_length < 2) { 58 return; 59 } 60 payload[0] = 0x08; 61 payload[1] = 0x40; 62 memset(&payload[2], payload_value, payload_length - 2); 63 } 64 65 // RED headers (according to RFC 2198): 66 // 67 // 0 1 2 3 68 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 69 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 // |F| block PT | timestamp offset | block length | 71 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 // 73 // Last RED header: 74 // 0 1 2 3 4 5 6 7 75 // +-+-+-+-+-+-+-+-+ 76 // |0| Block PT | 77 // +-+-+-+-+-+-+-+-+ 78 79 // Creates a RED packet, with `num_payloads` payloads, with payload types given 80 // by the values in array `payload_types` (which must be of length 81 // `num_payloads`). Each redundant payload is `timestamp_offset` samples 82 // "behind" the the previous payload. 83 Packet CreateRedPayload(size_t num_payloads, 84 uint8_t* payload_types, 85 int timestamp_offset, 86 bool embed_opus_fec = false) { 87 Packet packet; 88 packet.payload_type = kRedPayloadType; 89 packet.timestamp = kBaseTimestamp; 90 packet.sequence_number = kSequenceNumber; 91 packet.payload.SetSize((kPayloadLength + 1) + 92 (num_payloads - 1) * 93 (kPayloadLength + kRedHeaderLength)); 94 uint8_t* payload_ptr = packet.payload.data(); 95 for (size_t i = 0; i < num_payloads; ++i) { 96 // Write the RED headers. 97 if (i == num_payloads - 1) { 98 // Special case for last payload. 99 *payload_ptr = payload_types[i] & 0x7F; // F = 0; 100 ++payload_ptr; 101 break; 102 } 103 *payload_ptr = payload_types[i] & 0x7F; 104 // Not the last block; set F = 1. 105 *payload_ptr |= 0x80; 106 ++payload_ptr; 107 int this_offset = 108 checked_cast<int>((num_payloads - i - 1) * timestamp_offset); 109 *payload_ptr = this_offset >> 6; 110 ++payload_ptr; 111 RTC_DCHECK_LE(kPayloadLength, 1023); // Max length described by 10 bits. 112 *payload_ptr = ((this_offset & 0x3F) << 2) | (kPayloadLength >> 8); 113 ++payload_ptr; 114 *payload_ptr = kPayloadLength & 0xFF; 115 ++payload_ptr; 116 } 117 for (size_t i = 0; i < num_payloads; ++i) { 118 // Write `i` to all bytes in each payload. 119 if (embed_opus_fec) { 120 CreateOpusFecPayload(payload_ptr, kPayloadLength, 121 static_cast<uint8_t>(i)); 122 } else { 123 memset(payload_ptr, static_cast<int>(i), kPayloadLength); 124 } 125 payload_ptr += kPayloadLength; 126 } 127 return packet; 128 } 129 130 // Create a packet with all payload bytes set to `payload_value`. 131 Packet CreatePacket(uint8_t payload_type, 132 size_t payload_length, 133 uint8_t payload_value, 134 bool opus_fec = false) { 135 Packet packet; 136 packet.payload_type = payload_type; 137 packet.timestamp = kBaseTimestamp; 138 packet.sequence_number = kSequenceNumber; 139 packet.payload.SetSize(payload_length); 140 if (opus_fec) { 141 CreateOpusFecPayload(packet.payload.data(), packet.payload.size(), 142 payload_value); 143 } else { 144 memset(packet.payload.data(), payload_value, packet.payload.size()); 145 } 146 return packet; 147 } 148 149 // Checks that `packet` has the attributes given in the remaining parameters. 150 void VerifyPacket(const Packet& packet, 151 size_t payload_length, 152 uint8_t payload_type, 153 uint16_t sequence_number, 154 uint32_t timestamp, 155 uint8_t payload_value, 156 Packet::Priority priority) { 157 EXPECT_EQ(payload_length, packet.payload.size()); 158 EXPECT_EQ(payload_type, packet.payload_type); 159 EXPECT_EQ(sequence_number, packet.sequence_number); 160 EXPECT_EQ(timestamp, packet.timestamp); 161 EXPECT_EQ(priority, packet.priority); 162 ASSERT_FALSE(packet.payload.empty()); 163 for (size_t i = 0; i < packet.payload.size(); ++i) { 164 ASSERT_EQ(payload_value, packet.payload.data()[i]); 165 } 166 } 167 168 void VerifyPacket(const Packet& packet, 169 size_t payload_length, 170 uint8_t payload_type, 171 uint16_t sequence_number, 172 uint32_t timestamp, 173 uint8_t payload_value, 174 bool primary) { 175 return VerifyPacket(packet, payload_length, payload_type, sequence_number, 176 timestamp, payload_value, 177 Packet::Priority{0, primary ? 0 : 1}); 178 } 179 180 // Start of test definitions. 181 182 TEST(RedPayloadSplitter, CreateAndDestroy) { 183 RedPayloadSplitter* splitter = new RedPayloadSplitter; 184 delete splitter; 185 } 186 187 // Packet A is split into A1 and A2. 188 TEST(RedPayloadSplitter, OnePacketTwoPayloads) { 189 uint8_t payload_types[] = {0, 0}; 190 const int kTimestampOffset = 160; 191 PacketList packet_list; 192 packet_list.push_back(CreateRedPayload(2, payload_types, kTimestampOffset)); 193 RedPayloadSplitter splitter; 194 EXPECT_TRUE(splitter.SplitRed(&packet_list)); 195 ASSERT_EQ(2u, packet_list.size()); 196 // Check first packet. The first in list should always be the primary payload. 197 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[1], 198 kSequenceNumber, kBaseTimestamp, 1, true); 199 packet_list.pop_front(); 200 // Check second packet. 201 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0], 202 kSequenceNumber, kBaseTimestamp - kTimestampOffset, 0, false); 203 } 204 205 // Packets A and B are not split at all. Only the RED header in each packet is 206 // removed. 207 TEST(RedPayloadSplitter, TwoPacketsOnePayload) { 208 uint8_t payload_types[] = {0}; 209 const int kTimestampOffset = 160; 210 // Create first packet, with a single RED payload. 211 PacketList packet_list; 212 packet_list.push_back(CreateRedPayload(1, payload_types, kTimestampOffset)); 213 // Create second packet, with a single RED payload. 214 { 215 Packet packet = CreateRedPayload(1, payload_types, kTimestampOffset); 216 // Manually change timestamp and sequence number of second packet. 217 packet.timestamp += kTimestampOffset; 218 packet.sequence_number++; 219 packet_list.push_back(std::move(packet)); 220 } 221 RedPayloadSplitter splitter; 222 EXPECT_TRUE(splitter.SplitRed(&packet_list)); 223 ASSERT_EQ(2u, packet_list.size()); 224 // Check first packet. 225 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0], 226 kSequenceNumber, kBaseTimestamp, 0, true); 227 packet_list.pop_front(); 228 // Check second packet. 229 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0], 230 kSequenceNumber + 1, kBaseTimestamp + kTimestampOffset, 0, true); 231 } 232 233 // Packets A and B are split into packets A1, A2, A3, B1, B2, B3, with 234 // attributes as follows: 235 // 236 // A1* A2 A3 B1* B2 B3 237 // Payload type 0 1 2 0 1 2 238 // Timestamp b b-o b-2o b+o b b-o 239 // Sequence number 0 0 0 1 1 1 240 // 241 // b = kBaseTimestamp, o = kTimestampOffset, * = primary. 242 TEST(RedPayloadSplitter, TwoPacketsThreePayloads) { 243 uint8_t payload_types[] = {2, 1, 0}; // Primary is the last one. 244 const int kTimestampOffset = 160; 245 // Create first packet, with 3 RED payloads. 246 PacketList packet_list; 247 packet_list.push_back(CreateRedPayload(3, payload_types, kTimestampOffset)); 248 // Create first packet, with 3 RED payloads. 249 { 250 Packet packet = CreateRedPayload(3, payload_types, kTimestampOffset); 251 // Manually change timestamp and sequence number of second packet. 252 packet.timestamp += kTimestampOffset; 253 packet.sequence_number++; 254 packet_list.push_back(std::move(packet)); 255 } 256 RedPayloadSplitter splitter; 257 EXPECT_TRUE(splitter.SplitRed(&packet_list)); 258 ASSERT_EQ(6u, packet_list.size()); 259 // Check first packet, A1. 260 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[2], 261 kSequenceNumber, kBaseTimestamp, 2, {0, 0}); 262 packet_list.pop_front(); 263 // Check second packet, A2. 264 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[1], 265 kSequenceNumber, kBaseTimestamp - kTimestampOffset, 1, {0, 1}); 266 packet_list.pop_front(); 267 // Check third packet, A3. 268 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0], 269 kSequenceNumber, kBaseTimestamp - 2 * kTimestampOffset, 0, 270 {0, 2}); 271 packet_list.pop_front(); 272 // Check fourth packet, B1. 273 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[2], 274 kSequenceNumber + 1, kBaseTimestamp + kTimestampOffset, 2, 275 {0, 0}); 276 packet_list.pop_front(); 277 // Check fifth packet, B2. 278 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[1], 279 kSequenceNumber + 1, kBaseTimestamp, 1, {0, 1}); 280 packet_list.pop_front(); 281 // Check sixth packet, B3. 282 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0], 283 kSequenceNumber + 1, kBaseTimestamp - kTimestampOffset, 0, 284 {0, 2}); 285 } 286 287 // Creates a list with 4 packets with these payload types: 288 // 0 = CNGnb 289 // 1 = PCMu 290 // 2 = DTMF (AVT) 291 // 3 = PCMa 292 // We expect the method CheckRedPayloads to discard the PCMa packet, since it 293 // is a non-CNG, non-DTMF payload of another type than the first speech payload 294 // found in the list (which is PCMu). 295 TEST(RedPayloadSplitter, CheckRedPayloads) { 296 const Environment env = CreateEnvironment(); 297 PacketList packet_list; 298 for (uint8_t i = 0; i <= 3; ++i) { 299 // Create packet with payload type `i`, payload length 10 bytes, all 0. 300 packet_list.push_back(CreatePacket(i, 10, 0)); 301 } 302 303 // Use a real DecoderDatabase object here instead of a mock, since it is 304 // easier to just register the payload types and let the actual implementation 305 // do its job. 306 DecoderDatabase decoder_database( 307 env, make_ref_counted<MockAudioDecoderFactory>(), std::nullopt); 308 decoder_database.RegisterPayload(0, SdpAudioFormat("cn", 8000, 1)); 309 decoder_database.RegisterPayload(1, SdpAudioFormat("pcmu", 8000, 1)); 310 decoder_database.RegisterPayload(2, 311 SdpAudioFormat("telephone-event", 8000, 1)); 312 decoder_database.RegisterPayload(1, SdpAudioFormat("pcma", 8000, 1)); 313 314 RedPayloadSplitter splitter; 315 splitter.CheckRedPayloads(&packet_list, decoder_database); 316 317 ASSERT_EQ(3u, packet_list.size()); // Should have dropped the last packet. 318 // Verify packets. The loop verifies that payload types 0, 1, and 2 are in the 319 // list. 320 for (int i = 0; i <= 2; ++i) { 321 VerifyPacket(packet_list.front(), 10, i, kSequenceNumber, kBaseTimestamp, 0, 322 true); 323 packet_list.pop_front(); 324 } 325 EXPECT_TRUE(packet_list.empty()); 326 } 327 328 // This test creates a RED packet where the payloads also have the payload type 329 // for RED. That is, some kind of weird nested RED packet. This is not supported 330 // and the splitter should discard all packets. 331 TEST(RedPayloadSplitter, CheckRedPayloadsRecursiveRed) { 332 const Environment env = CreateEnvironment(); 333 PacketList packet_list; 334 for (uint8_t i = 0; i <= 3; ++i) { 335 // Create packet with RED payload type, payload length 10 bytes, all 0. 336 packet_list.push_back(CreatePacket(kRedPayloadType, 10, 0)); 337 } 338 339 // Use a real DecoderDatabase object here instead of a mock, since it is 340 // easier to just register the payload types and let the actual implementation 341 // do its job. 342 DecoderDatabase decoder_database( 343 env, make_ref_counted<MockAudioDecoderFactory>(), std::nullopt); 344 decoder_database.RegisterPayload(kRedPayloadType, 345 SdpAudioFormat("red", 8000, 1)); 346 347 RedPayloadSplitter splitter; 348 splitter.CheckRedPayloads(&packet_list, decoder_database); 349 350 EXPECT_TRUE(packet_list.empty()); // Should have dropped all packets. 351 } 352 353 // Packet A is split into A1, A2 and A3. But the length parameter is off, so 354 // the last payloads should be discarded. 355 TEST(RedPayloadSplitter, WrongPayloadLength) { 356 uint8_t payload_types[] = {0, 0, 0}; 357 const int kTimestampOffset = 160; 358 PacketList packet_list; 359 { 360 Packet packet = CreateRedPayload(3, payload_types, kTimestampOffset); 361 // Manually tamper with the payload length of the packet. 362 // This is one byte too short for the second payload (out of three). 363 // We expect only the first payload to be returned. 364 packet.payload.SetSize(packet.payload.size() - (kPayloadLength + 1)); 365 packet_list.push_back(std::move(packet)); 366 } 367 RedPayloadSplitter splitter; 368 EXPECT_FALSE(splitter.SplitRed(&packet_list)); 369 ASSERT_EQ(1u, packet_list.size()); 370 // Check first packet. 371 VerifyPacket(packet_list.front(), kPayloadLength, payload_types[0], 372 kSequenceNumber, kBaseTimestamp - 2 * kTimestampOffset, 0, 373 {0, 2}); 374 packet_list.pop_front(); 375 } 376 377 // Test that we reject packets too short to contain a RED header. 378 TEST(RedPayloadSplitter, RejectsIncompleteHeaders) { 379 RedPayloadSplitter splitter; 380 381 uint8_t payload_types[] = {0, 0}; 382 const int kTimestampOffset = 160; 383 384 PacketList packet_list; 385 386 // Truncate the packet such that the first block can not be parsed. 387 packet_list.push_back(CreateRedPayload(2, payload_types, kTimestampOffset)); 388 packet_list.front().payload.SetSize(4); 389 EXPECT_FALSE(splitter.SplitRed(&packet_list)); 390 EXPECT_FALSE(packet_list.empty()); 391 392 // Truncate the packet such that the first block can not be parsed. 393 packet_list.front().payload.SetSize(3); 394 EXPECT_FALSE(splitter.SplitRed(&packet_list)); 395 EXPECT_FALSE(packet_list.empty()); 396 } 397 398 } // namespace webrtc