diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index f17567cba3..cab7bec915 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -644,6 +644,7 @@ if (rtc_include_tests) { "../../api:field_trials_registry", "../../api:frame_transformer_factory", "../../api:libjingle_peerconnection_api", + "../../api:make_ref_counted", "../../api:mock_frame_encryptor", "../../api:mock_transformable_video_frame", "../../api:rtp_headers", diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer2.cc b/modules/rtp_rtcp/source/flexfec_header_reader_writer2.cc index 7682dba66f..a6ac68b74b 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer2.cc +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer2.cc @@ -37,10 +37,10 @@ constexpr size_t kMaxFecPackets = kMaxMediaPackets; constexpr size_t kFlexfecPacketMaskSizes[] = {2, 6, 14}; // Size (in bytes) of part of header which is not packet mask specific. -constexpr size_t kBaseHeaderSize = 12; +constexpr size_t kBaseHeaderSize = 8; // Size (in bytes) of part of header which is stream specific. -constexpr size_t kStreamSpecificHeaderSize = 6; +constexpr size_t kStreamSpecificHeaderSize = 2; // Size (in bytes) of header, given the single stream packet mask size, i.e. // the number of K-bits set. @@ -82,9 +82,15 @@ FlexfecHeaderReader2::FlexfecHeaderReader2() FlexfecHeaderReader2::~FlexfecHeaderReader2() = default; // TODO(brandtr): Update this function when we support flexible masks, -// retransmissions, and/or several protected SSRCs. +// and retransmissions. bool FlexfecHeaderReader2::ReadFecHeader( ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const { + // Protected ssrcs should already be populated from RTP header. + if (fec_packet->protected_streams.empty()) { + RTC_LOG(LS_WARNING) + << "Discarding FlexFEC packet with no protected sources."; + return false; + } if (fec_packet->pkt->data.size() <= kBaseHeaderSize + kStreamSpecificHeaderSize) { RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet."; @@ -105,100 +111,103 @@ bool FlexfecHeaderReader2::ReadFecHeader( "not yet support this, thus discarding packet."; return false; } - uint8_t ssrc_count = ByteReader::ReadBigEndian(&data[8]); - if (ssrc_count != 1) { - RTC_LOG(LS_INFO) - << "FlexFEC packet protecting multiple media SSRCs. We do not " - "yet support this, thus discarding packet."; - return false; - } - uint32_t protected_ssrc = ByteReader::ReadBigEndian(&data[12]); - uint16_t seq_num_base = ByteReader::ReadBigEndian(&data[16]); - // Parse the FlexFEC packet mask and remove the interleaved K-bits. + // First seq_num will be in byte index 8 // (See FEC header schematic in flexfec_header_reader_writer.h.) - // We store the packed packet mask in-band, which "destroys" the standards - // compliance of the header. That is fine though, since the code that - // reads from the header (from this point and onwards) is aware of this. - // TODO(brandtr): When the FEC packet classes have been refactored, store - // the packed packet masks out-of-band, thus leaving the FlexFEC header as is. - // - // We treat the mask parts as unsigned integers with host order endianness - // in order to simplify the bit shifting between bytes. - if (fec_packet->pkt->data.size() < kHeaderSizes[0]) { - RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet."; - return false; - } - uint8_t* const packet_mask = data + kPacketMaskOffset; - bool k_bit0 = (packet_mask[0] & 0x80) != 0; - uint16_t mask_part0 = ByteReader::ReadBigEndian(&packet_mask[0]); - // Shift away K-bit 0, implicitly clearing the last bit. - mask_part0 <<= 1; - ByteWriter::WriteBigEndian(&packet_mask[0], mask_part0); - size_t packet_mask_size; - if (k_bit0) { - // The first K-bit is set, and the packet mask is thus only 2 bytes long. - // We have now read the entire FEC header, and the rest of the packet - // is payload. - packet_mask_size = kFlexfecPacketMaskSizes[0]; - } else { - if (fec_packet->pkt->data.size() < kHeaderSizes[1]) { + size_t byte_index = 8; + for (size_t i = 0; i < fec_packet->protected_streams.size(); ++i) { + if (fec_packet->pkt->data.size() < byte_index + kStreamSpecificHeaderSize) { + RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet."; return false; } - bool k_bit1 = (packet_mask[2] & 0x80) != 0; - // We have already shifted the first two bytes of the packet mask one step - // to the left, thus removing K-bit 0. We will now shift the next four bytes - // of the packet mask two steps to the left. (One step for the removed - // K-bit 0, and one step for the to be removed K-bit 1). - uint8_t bit15 = (packet_mask[2] >> 6) & 0x01; - packet_mask[1] |= bit15; - uint32_t mask_part1 = ByteReader::ReadBigEndian(&packet_mask[2]); - // Shift away K-bit 1 and bit 15, implicitly clearing the last two bits. - mask_part1 <<= 2; - ByteWriter::WriteBigEndian(&packet_mask[2], mask_part1); - if (k_bit1) { - // The first K-bit is clear, but the second K-bit is set. The packet - // mask is thus 6 bytes long. We have now read the entire FEC header, - // and the rest of the packet is payload. - packet_mask_size = kFlexfecPacketMaskSizes[1]; + + fec_packet->protected_streams[i].seq_num_base = + ByteReader::ReadBigEndian(&data[byte_index]); + byte_index += kStreamSpecificHeaderSize; + + // Parse the FlexFEC packet mask and remove the interleaved K-bits. + // (See FEC header schematic in flexfec_header_reader_writer.h.) + // We store the packed packet mask in-band, which "destroys" the standards + // compliance of the header. That is fine though, since the code that + // reads from the header (from this point and onwards) is aware of this. + // TODO(brandtr): When the FEC packet classes have been refactored, store + // the packed packet masks out-of-band, thus leaving the FlexFEC header as + // is. + // + // We treat the mask parts as unsigned integers with host order endianness + // in order to simplify the bit shifting between bytes. + if (fec_packet->pkt->data.size() < + (byte_index + kFlexfecPacketMaskSizes[0])) { + RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet."; + return false; + } + fec_packet->protected_streams[i].packet_mask_offset = byte_index; + bool k_bit0 = (data[byte_index] & 0x80) != 0; + uint16_t mask_part0 = + ByteReader::ReadBigEndian(&data[byte_index]); + // Shift away K-bit 0, implicitly clearing the last bit. + mask_part0 <<= 1; + ByteWriter::WriteBigEndian(&data[byte_index], mask_part0); + byte_index += kFlexfecPacketMaskSizes[0]; + if (k_bit0) { + // The first K-bit is set, and the packet mask is thus only 2 bytes long. + // We have finished reading the properties for current ssrc. + fec_packet->protected_streams[i].packet_mask_size = + kFlexfecPacketMaskSizes[0]; } else { - if (fec_packet->pkt->data.size() < kHeaderSizes[2]) { - RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet."; + if (fec_packet->pkt->data.size() < + (byte_index + kFlexfecPacketMaskSizes[1] - + kFlexfecPacketMaskSizes[0])) { return false; } - bool k_bit2 = (packet_mask[6] & 0x80) != 0; - if (k_bit2) { - // The first and second K-bits are clear, but the third K-bit is set. - // The packet mask is thus 14 bytes long. We have now read the entire - // FEC header, and the rest of the packet is payload. - packet_mask_size = kFlexfecPacketMaskSizes[2]; + bool k_bit1 = (data[byte_index] & 0x80) != 0; + // We have already shifted the first two bytes of the packet mask one step + // to the left, thus removing K-bit 0. We will now shift the next four + // bytes of the packet mask two steps to the left. (One step for the + // removed K-bit 0, and one step for the to be removed K-bit 1). + uint8_t bit15 = (data[byte_index] >> 6) & 0x01; + data[byte_index - 1] |= bit15; + uint32_t mask_part1 = + ByteReader::ReadBigEndian(&data[byte_index]); + // Shift away K-bit 1 and bit 15, implicitly clearing the last two bits. + mask_part1 <<= 2; + ByteWriter::WriteBigEndian(&data[byte_index], mask_part1); + byte_index += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0]; + if (k_bit1) { + // The first K-bit is clear, but the second K-bit is set. The packet + // mask is thus 6 bytes long. We have finished reading the properties + // for current ssrc. + fec_packet->protected_streams[i].packet_mask_size = + kFlexfecPacketMaskSizes[1]; } else { - RTC_LOG(LS_WARNING) - << "Discarding FlexFEC packet with malformed header."; - return false; + if (fec_packet->pkt->data.size() < + (byte_index + kFlexfecPacketMaskSizes[2] - + kFlexfecPacketMaskSizes[1])) { + RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet."; + return false; + } + fec_packet->protected_streams[i].packet_mask_size = + kFlexfecPacketMaskSizes[2]; + // At this point, K-bits 0 and 1 have been removed, and the front-most + // part of the FlexFEC packet mask has been packed accordingly. We will + // now shift the remaning part of the packet mask two steps to the left. + // This corresponds to the (in total) two K-bits, which have been + // removed. + uint8_t tail_bits = (data[byte_index] >> 6) & 0x03; + data[byte_index - 1] |= tail_bits; + uint64_t mask_part2 = + ByteReader::ReadBigEndian(&data[byte_index]); + // Shift away bit 46, and bit 47, which were copied to the previous + // part of the mask, implicitly clearing the last two bits. + mask_part2 <<= 2; + ByteWriter::WriteBigEndian(&data[byte_index], mask_part2); + byte_index += kFlexfecPacketMaskSizes[2] - kFlexfecPacketMaskSizes[1]; } - // At this point, K-bits 0 and 1 have been removed, and the front-most - // part of the FlexFEC packet mask has been packed accordingly. We will - // now shift the remaning part of the packet mask three steps to the left. - // This corresponds to the (in total) three K-bits, which have been - // removed. - uint8_t tail_bits = (packet_mask[6] >> 5) & 0x03; - packet_mask[5] |= tail_bits; - uint64_t mask_part2 = - ByteReader::ReadBigEndian(&packet_mask[6]); - // Shift away K-bit 2, bit 46, and bit 47, implicitly clearing the last - // three bits. - mask_part2 <<= 3; - ByteWriter::WriteBigEndian(&packet_mask[6], mask_part2); } } - // Store "ULPFECized" packet mask info. - fec_packet->fec_header_size = FlexfecHeaderSize(packet_mask_size); - fec_packet->protected_streams = {{.ssrc = protected_ssrc, - .seq_num_base = seq_num_base, - .packet_mask_offset = kPacketMaskOffset, - .packet_mask_size = packet_mask_size}}; + fec_packet->fec_header_size = byte_index; + // In FlexFEC, all media packets are protected in their entirety. fec_packet->protection_length = fec_packet->pkt->data.size() - fec_packet->fec_header_size; diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer2.h b/modules/rtp_rtcp/source/flexfec_header_reader_writer2.h index 79ee918338..7b9a052509 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer2.h +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer2.h @@ -18,43 +18,25 @@ namespace webrtc { -// FlexFEC header, minimum 20 bytes. +// FlexFEC header in flexible mode (R=0, F=0), minimum 12 bytes. +// https://datatracker.ietf.org/doc/html/rfc8627#section-4.2.2.1 +// // 0 1 2 3 // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 0 |R|F|P|X| CC |M| PT recovery | length recovery | +// 0 |0|0|P|X| CC |M| PT recovery | length recovery | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // 4 | TS recovery | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 8 | SSRCCount | reserved | -// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// 12 | SSRC_i | +// 8 | SN base_i |k| Mask [0-14] | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 16 | SN base_i |k| Mask [0-14] | +// 12 |k| Mask [15-45] (optional) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 20 |k| Mask [15-45] (optional) | +// 16 | Mask [46-109] (optional) | +// 20 | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 24 |k| | -// +-+ Mask [46-108] (optional) | -// 28 | | -// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// : ... next in SSRC_i ... : +// | ... next SN base and Mask for CSRC_i in CSRC list ... | // -// -// FlexFEC header in 'inflexible' mode (F = 1), 20 bytes. -// 0 1 2 3 -// 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 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 0 |0|1|P|X| CC |M| PT recovery | length recovery | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 4 | TS recovery | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 8 | SSRCCount | reserved | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 12 | SSRC_i | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 16 | SN base_i | M (columns) | N (rows) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ class FlexfecHeaderReader2 : public FecHeaderReader { public: diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer2_unittest.cc b/modules/rtp_rtcp/source/flexfec_header_reader_writer2_unittest.cc index bbbb7eb79e..7c8324017e 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer2_unittest.cc +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer2_unittest.cc @@ -10,27 +10,596 @@ #include "modules/rtp_rtcp/source/flexfec_header_reader_writer2.h" +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/make_ref_counted.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/forward_error_correction.h" +#include "modules/rtp_rtcp/source/forward_error_correction_internal.h" +#include "rtc_base/checks.h" +#include "rtc_base/random.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { -// TODO(bugs.webrtc.org/15002): reimplement all tests after changes -// to parser, and add tests for multi stream cases. +namespace { -TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit0Set) {} +using Packet = ForwardErrorCorrection::Packet; +using ProtectedStream = ForwardErrorCorrection::ProtectedStream; +using ReceivedFecPacket = ForwardErrorCorrection::ReceivedFecPacket; +using ::testing::ElementsAreArray; -TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit1Set) {} +constexpr uint8_t kMask0[] = {0xAB, 0xCD}; // First K bit is set. +constexpr uint8_t kMask1[] = {0x12, 0x34, // First K bit cleared. + 0xF6, 0x78, 0x9A, 0xBC}; // Second K bit set. +constexpr uint8_t kMask2[] = {0x12, 0x34, // First K bit cleared. + 0x56, 0x78, 0x9A, 0xBC, // Second K bit cleared. + 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}; -TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit2Set) {} +// Reader tests. +constexpr uint8_t kFlexible = 0b00 << 6; +constexpr uint8_t kPtRecovery = 123; +constexpr uint8_t kLengthRecovery[] = {0xab, 0xcd}; +constexpr uint8_t kTsRecovery[] = {0x01, 0x23, 0x45, 0x67}; +constexpr uint8_t kSnBases[4][2] = {{0x01, 0x02}, + {0x03, 0x04}, + {0x05, 0x06}, + {0x07, 0x08}}; +constexpr uint8_t kPayloadBits = 0x00; + +struct FecPacketStreamProperties { + ProtectedStream stream; + rtc::ArrayView mask; +}; + +void VerifyReadHeaders(size_t expected_fec_header_size, + const ReceivedFecPacket& read_packet, + std::vector expected) { + EXPECT_EQ(read_packet.fec_header_size, expected_fec_header_size); + const size_t protected_streams_num = read_packet.protected_streams.size(); + EXPECT_EQ(protected_streams_num, expected.size()); + for (size_t i = 0; i < protected_streams_num; ++i) { + SCOPED_TRACE(i); + ProtectedStream protected_stream = read_packet.protected_streams[i]; + EXPECT_EQ(protected_stream.ssrc, expected[i].stream.ssrc); + EXPECT_EQ(protected_stream.seq_num_base, expected[i].stream.seq_num_base); + EXPECT_EQ(protected_stream.packet_mask_offset, + expected[i].stream.packet_mask_offset); + EXPECT_EQ(protected_stream.packet_mask_size, + expected[i].stream.packet_mask_size); + // Ensure that the K-bits are removed and the packet mask has been packed. + EXPECT_THAT(rtc::MakeArrayView(read_packet.pkt->data.cdata() + + protected_stream.packet_mask_offset, + protected_stream.packet_mask_size), + ElementsAreArray(expected[i].mask)); + } + EXPECT_EQ(read_packet.pkt->data.size() - expected_fec_header_size, + read_packet.protection_length); +} + +} // namespace + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit0SetSingleStream) { + constexpr uint8_t kKBit0 = 1 << 7; + constexpr size_t kExpectedFecHeaderSize = 12; + constexpr uint16_t kSnBase = 0x0102; + constexpr uint8_t kFlexfecPktMask[] = {kKBit0 | 0x08, 0x81}; + constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02}; + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3], + kSnBase >> 8, kSnBase & 0xFF, kFlexfecPktMask[0], kFlexfecPktMask[1], + kPayloadBits, kPayloadBits, kPayloadBits, kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask)}, + .mask = kUlpfecPacketMask}}; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit1SetSingleStream) { + constexpr uint8_t kKBit0 = 0 << 7; + constexpr uint8_t kKBit1 = 1 << 7; + constexpr size_t kExpectedFecHeaderSize = 16; + constexpr uint16_t kSnBase = 0x0102; + constexpr uint8_t kFlexfecPktMask[] = {kKBit0 | 0x48, 0x81, // + kKBit1 | 0x02, 0x11, 0x00, 0x21}; + constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, // + 0x08, 0x44, 0x00, 0x84}; + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], + kLengthRecovery[1], kTsRecovery[0], kTsRecovery[1], + kTsRecovery[2], kTsRecovery[3], kSnBase >> 8, + kSnBase & 0xFF, kFlexfecPktMask[0], kFlexfecPktMask[1], + kFlexfecPktMask[2], kFlexfecPktMask[3], kFlexfecPktMask[4], + kFlexfecPktMask[5], kPayloadBits, kPayloadBits, + kPayloadBits, kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask)}, + .mask = kUlpfecPacketMask}}; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithNoKBitsSetSingleStream) { + constexpr uint8_t kKBit0 = 0 << 7; + constexpr uint8_t kKBit1 = 0 << 7; + constexpr size_t kExpectedFecHeaderSize = 24; + constexpr uint16_t kSnBase = 0x0102; + constexpr uint8_t kFlexfecPacketMask[] = {kKBit0 | 0x48, 0x81, // + kKBit1 | 0x02, 0x11, 0x00, 0x21, // + 0x01, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11}; + constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, // + 0x08, 0x44, 0x00, 0x84, // + 0x04, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44}; + constexpr uint8_t kPacketData[] = {kFlexible, + kPtRecovery, + kLengthRecovery[0], + kLengthRecovery[1], + kTsRecovery[0], + kTsRecovery[1], + kTsRecovery[2], + kTsRecovery[3], + kSnBase >> 8, + kSnBase & 0xFF, + kFlexfecPacketMask[0], + kFlexfecPacketMask[1], + kFlexfecPacketMask[2], + kFlexfecPacketMask[3], + kFlexfecPacketMask[4], + kFlexfecPacketMask[5], + kFlexfecPacketMask[6], + kFlexfecPacketMask[7], + kFlexfecPacketMask[8], + kFlexfecPacketMask[9], + kFlexfecPacketMask[10], + kFlexfecPacketMask[11], + kFlexfecPacketMask[12], + kFlexfecPacketMask[13], + kPayloadBits, + kPayloadBits, + kPayloadBits, + kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask)}, + .mask = kUlpfecPacketMask}}; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit0Set2Streams) { + constexpr uint8_t kKBit0 = 1 << 7; + constexpr size_t kExpectedFecHeaderSize = 16; + constexpr uint16_t kSnBase0 = 0x0102; + constexpr uint16_t kSnBase1 = 0x0304; + constexpr uint8_t kFlexfecPktMask1[] = {kKBit0 | 0x08, 0x81}; + constexpr uint8_t kUlpfecPacketMask1[] = {0x11, 0x02}; + constexpr uint8_t kFlexfecPktMask2[] = {kKBit0 | 0x04, 0x41}; + constexpr uint8_t kUlpfecPacketMask2[] = {0x08, 0x82}; + + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3], + kSnBase0 >> 8, kSnBase0 & 0xFF, kFlexfecPktMask1[0], kFlexfecPktMask1[1], + kSnBase1 >> 8, kSnBase1 & 0xFF, kFlexfecPktMask2[0], kFlexfecPktMask2[1], + kPayloadBits, kPayloadBits, kPayloadBits, kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase0, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask1)}, + .mask = kUlpfecPacketMask1}, + {.stream = {.ssrc = 0x02, + .seq_num_base = kSnBase1, + .packet_mask_offset = 14, + .packet_mask_size = std::size(kUlpfecPacketMask2)}, + .mask = kUlpfecPacketMask2}, + }; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithKBit1Set2Streams) { + constexpr uint8_t kKBit0 = 0 << 7; + constexpr uint8_t kKBit1 = 1 << 7; + constexpr size_t kExpectedFecHeaderSize = 24; + constexpr uint16_t kSnBase0 = 0x0102; + constexpr uint16_t kSnBase1 = 0x0304; + constexpr uint8_t kFlexfecPktMask1[] = {kKBit0 | 0x48, 0x81, // + kKBit1 | 0x02, 0x11, 0x00, 0x21}; + constexpr uint8_t kUlpfecPacketMask1[] = {0x91, 0x02, // + 0x08, 0x44, 0x00, 0x84}; + constexpr uint8_t kFlexfecPktMask2[] = {kKBit0 | 0x57, 0x82, // + kKBit1 | 0x04, 0x33, 0x00, 0x51}; + constexpr uint8_t kUlpfecPacketMask2[] = {0xAF, 0x04, // + 0x10, 0xCC, 0x01, 0x44}; + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], + kLengthRecovery[1], kTsRecovery[0], kTsRecovery[1], + kTsRecovery[2], kTsRecovery[3], kSnBase0 >> 8, + kSnBase0 & 0xFF, kFlexfecPktMask1[0], kFlexfecPktMask1[1], + kFlexfecPktMask1[2], kFlexfecPktMask1[3], kFlexfecPktMask1[4], + kFlexfecPktMask1[5], kSnBase1 >> 8, kSnBase1 & 0xFF, + kFlexfecPktMask2[0], kFlexfecPktMask2[1], kFlexfecPktMask2[2], + kFlexfecPktMask2[3], kFlexfecPktMask2[4], kFlexfecPktMask2[5], + kPayloadBits, kPayloadBits, kPayloadBits, + kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase0, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask1)}, + .mask = kUlpfecPacketMask1}, + {.stream = {.ssrc = 0x02, + .seq_num_base = kSnBase1, + .packet_mask_offset = 18, + .packet_mask_size = std::size(kUlpfecPacketMask2)}, + .mask = kUlpfecPacketMask2}, + }; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithNoKBitsSet2Streams) { + constexpr uint8_t kKBit0 = 0 << 7; + constexpr uint8_t kKBit1 = 0 << 7; + constexpr size_t kExpectedFecHeaderSize = 40; + constexpr uint16_t kSnBase0 = 0x0102; + constexpr uint16_t kSnBase1 = 0x0304; + constexpr uint8_t kFlexfecPktMask1[] = {kKBit0 | 0x48, 0x81, // + kKBit1 | 0x02, 0x11, 0x00, 0x21, // + 0x01, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11}; + constexpr uint8_t kUlpfecPacketMask1[] = {0x91, 0x02, // + 0x08, 0x44, 0x00, 0x84, // + 0x04, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44}; + constexpr uint8_t kFlexfecPktMask2[] = {kKBit0 | 0x32, 0x84, // + kKBit1 | 0x05, 0x23, 0x00, 0x55, // + 0xA3, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x35}; + constexpr uint8_t kUlpfecPacketMask2[] = {0x65, 0x08, // + 0x14, 0x8C, 0x01, 0x56, // + 0x8C, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0xD4}; + + constexpr uint8_t kPacketData[] = {kFlexible, + kPtRecovery, + kLengthRecovery[0], + kLengthRecovery[1], + kTsRecovery[0], + kTsRecovery[1], + kTsRecovery[2], + kTsRecovery[3], + kSnBase0 >> 8, + kSnBase0 & 0xFF, + kFlexfecPktMask1[0], + kFlexfecPktMask1[1], + kFlexfecPktMask1[2], + kFlexfecPktMask1[3], + kFlexfecPktMask1[4], + kFlexfecPktMask1[5], + kFlexfecPktMask1[6], + kFlexfecPktMask1[7], + kFlexfecPktMask1[8], + kFlexfecPktMask1[9], + kFlexfecPktMask1[10], + kFlexfecPktMask1[11], + kFlexfecPktMask1[12], + kFlexfecPktMask1[13], + kSnBase1 >> 8, + kSnBase1 & 0xFF, + kFlexfecPktMask2[0], + kFlexfecPktMask2[1], + kFlexfecPktMask2[2], + kFlexfecPktMask2[3], + kFlexfecPktMask2[4], + kFlexfecPktMask2[5], + kFlexfecPktMask2[6], + kFlexfecPktMask2[7], + kFlexfecPktMask2[8], + kFlexfecPktMask2[9], + kFlexfecPktMask2[10], + kFlexfecPktMask2[11], + kFlexfecPktMask2[12], + kFlexfecPktMask2[13], + kPayloadBits, + kPayloadBits, + kPayloadBits, + kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase0, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask1)}, + .mask = kUlpfecPacketMask1}, + {.stream = {.ssrc = 0x02, + .seq_num_base = kSnBase1, + .packet_mask_offset = 26, + .packet_mask_size = std::size(kUlpfecPacketMask2)}, + .mask = kUlpfecPacketMask2}, + }; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadsHeaderWithMultipleStreamsMultipleMasks) { + constexpr uint8_t kBit0 = 0 << 7; + constexpr uint8_t kBit1 = 1 << 7; + constexpr size_t kExpectedFecHeaderSize = 44; + constexpr uint16_t kSnBase0 = 0x0102; + constexpr uint16_t kSnBase1 = 0x0304; + constexpr uint16_t kSnBase2 = 0x0506; + constexpr uint16_t kSnBase3 = 0x0708; + constexpr uint8_t kFlexfecPacketMask1[] = {kBit1 | 0x29, 0x91}; + constexpr uint8_t kUlpfecPacketMask1[] = {0x53, 0x22}; + constexpr uint8_t kFlexfecPacketMask2[] = {kBit0 | 0x32, 0xA1, // + kBit1 | 0x02, 0x11, 0x00, 0x21}; + constexpr uint8_t kUlpfecPacketMask2[] = {0x65, 0x42, // + 0x08, 0x44, 0x00, 0x84}; + constexpr uint8_t kFlexfecPacketMask3[] = {kBit0 | 0x48, 0x81, // + kBit0 | 0x02, 0x11, 0x00, 0x21, // + 0x01, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11}; + constexpr uint8_t kUlpfecPacketMask3[] = {0x91, 0x02, // + 0x08, 0x44, 0x00, 0x84, // + 0x04, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44}; + constexpr uint8_t kFlexfecPacketMask4[] = {kBit0 | 0x32, 0x84, // + kBit1 | 0x05, 0x23, 0x00, 0x55}; + constexpr uint8_t kUlpfecPacketMask4[] = {0x65, 0x08, // + 0x14, 0x8C, 0x01, 0x54}; + constexpr uint8_t kPacketData[] = {kFlexible, + kPtRecovery, + kLengthRecovery[0], + kLengthRecovery[1], + kTsRecovery[0], + kTsRecovery[1], + kTsRecovery[2], + kTsRecovery[3], + kSnBase0 >> 8, + kSnBase0 & 0xFF, + kFlexfecPacketMask1[0], + kFlexfecPacketMask1[1], + kSnBase1 >> 8, + kSnBase1 & 0xFF, + kFlexfecPacketMask2[0], + kFlexfecPacketMask2[1], + kFlexfecPacketMask2[2], + kFlexfecPacketMask2[3], + kFlexfecPacketMask2[4], + kFlexfecPacketMask2[5], + kSnBase2 >> 8, + kSnBase2 & 0xFF, + kFlexfecPacketMask3[0], + kFlexfecPacketMask3[1], + kFlexfecPacketMask3[2], + kFlexfecPacketMask3[3], + kFlexfecPacketMask3[4], + kFlexfecPacketMask3[5], + kFlexfecPacketMask3[6], + kFlexfecPacketMask3[7], + kFlexfecPacketMask3[8], + kFlexfecPacketMask3[9], + kFlexfecPacketMask3[10], + kFlexfecPacketMask3[11], + kFlexfecPacketMask3[12], + kFlexfecPacketMask3[13], + kSnBase3 >> 8, + kSnBase3 & 0xFF, + kFlexfecPacketMask4[0], + kFlexfecPacketMask4[1], + kFlexfecPacketMask4[2], + kFlexfecPacketMask4[3], + kFlexfecPacketMask4[4], + kFlexfecPacketMask4[5], + kPayloadBits, + kPayloadBits, + kPayloadBits, + kPayloadBits}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = { + {.ssrc = 0x01}, {.ssrc = 0x02}, {.ssrc = 0x03}, {.ssrc = 0x04}}; + + FlexfecHeaderReader2 reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + std::vector expected = { + {.stream = {.ssrc = 0x01, + .seq_num_base = kSnBase0, + .packet_mask_offset = 10, + .packet_mask_size = std::size(kUlpfecPacketMask1)}, + .mask = kUlpfecPacketMask1}, + {.stream = {.ssrc = 0x02, + .seq_num_base = kSnBase1, + .packet_mask_offset = 14, + .packet_mask_size = std::size(kUlpfecPacketMask2)}, + .mask = kUlpfecPacketMask2}, + {.stream = {.ssrc = 0x03, + .seq_num_base = kSnBase2, + .packet_mask_offset = 22, + .packet_mask_size = std::size(kUlpfecPacketMask3)}, + .mask = kUlpfecPacketMask3}, + {.stream = {.ssrc = 0x04, + .seq_num_base = kSnBase3, + .packet_mask_offset = 38, + .packet_mask_size = std::size(kUlpfecPacketMask4)}, + .mask = kUlpfecPacketMask4}, + }; + + VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected); +} + +TEST(FlexfecHeaderReader2Test, ReadPacketWithoutProtectedSsrcsShouldFail) { + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3]}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + // No protected ssrcs. + read_packet.protected_streams = {}; + + FlexfecHeaderReader2 reader; + EXPECT_FALSE(reader.ReadFecHeader(&read_packet)); +} TEST(FlexfecHeaderReader2Test, - ReadPacketWithoutStreamSpecificHeaderShouldFail) {} + ReadPacketWithoutStreamSpecificHeaderShouldFail) { + // Simulate short received packet. + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3]}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + read_packet.pkt->data.SetData(kPacketData); + read_packet.protected_streams = {{.ssrc = 0x01}}; -TEST(FlexfecHeaderReader2Test, ReadShortPacketWithKBit0SetShouldFail) {} + FlexfecHeaderReader2 reader; + EXPECT_FALSE(reader.ReadFecHeader(&read_packet)); +} -TEST(FlexfecHeaderReader2Test, ReadShortPacketWithKBit1SetShouldFail) {} +TEST(FlexfecHeaderReader2Test, ReadShortPacketWithKBit0SetShouldFail) { + // Simulate short received packet. + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3], + kSnBases[0][0], kSnBases[0][1], kMask0[0], kMask0[1]}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + // Expected to have 2 bytes of mask but length of packet misses 1 byte. + read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 1); + read_packet.protected_streams = {{.ssrc = 0x01}}; -TEST(FlexfecHeaderReader2Test, ReadShortPacketWithKBit2SetShouldFail) {} + FlexfecHeaderReader2 reader; + EXPECT_FALSE(reader.ReadFecHeader(&read_packet)); +} + +TEST(FlexfecHeaderReader2Test, ReadShortPacketWithKBit1SetShouldFail) { + // Simulate short received packet. + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3], + kSnBases[0][0], kSnBases[0][1], kMask1[0], kMask1[1], + kMask1[2], kMask1[3], kMask1[4], kMask1[5]}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + // Expected to have 6 bytes of mask but length of packet misses 2 bytes. + read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 2); + read_packet.protected_streams = {{.ssrc = 0x01}}; + + FlexfecHeaderReader2 reader; + EXPECT_FALSE(reader.ReadFecHeader(&read_packet)); +} + +TEST(FlexfecHeaderReader2Test, ReadShortPacketWithKBit1ClearedShouldFail) { + // Simulate short received packet. + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3], + kSnBases[0][0], kSnBases[0][1], kMask2[0], kMask2[1], + kMask2[2], kMask2[3], kMask2[4], kMask2[5], + kMask2[6], kMask2[7], kMask2[8], kMask2[9], + kMask2[10], kMask2[11], kMask2[12], kMask2[13]}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + // Expected to have 14 bytes of mask but length of packet misses 2 bytes. + read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 2); + read_packet.protected_streams = {{.ssrc = 0x01}}; + + FlexfecHeaderReader2 reader; + EXPECT_FALSE(reader.ReadFecHeader(&read_packet)); +} + +TEST(FlexfecHeaderReader2Test, ReadShortPacketMultipleStreamsShouldFail) { + // Simulate short received packet with 2 protected ssrcs. + constexpr uint8_t kPacketData[] = { + kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1], + kTsRecovery[0], kTsRecovery[1], kTsRecovery[2], kTsRecovery[3], + kSnBases[0][0], kSnBases[0][1], kMask0[0], kMask0[1], + kSnBases[1][0], kSnBases[1][1], kMask2[0], kMask2[1], + kMask2[2], kMask2[3], kMask2[4], kMask2[5], + kMask2[6], kMask2[7], kMask2[8], kMask2[9], + kMask2[10], kMask2[11], kMask2[12], kMask2[13]}; + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::make_ref_counted(); + // Subtract 2 bytes from length, so the read will fail on parsing second + read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 2); + read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}}; + + FlexfecHeaderReader2 reader; + EXPECT_FALSE(reader.ReadFecHeader(&read_packet)); +} + +// TODO(bugs.webrtc.org/15002): reimplement and add tests for multi stream cases +// after updating the Writer code. TEST(FlexfecHeaderWriter2Test, FinalizesHeaderWithKBit0Set) {}