summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc')
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc929
1 files changed, 929 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc
new file mode 100644
index 0000000000..6995ba3871
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc
@@ -0,0 +1,929 @@
+/*
+ * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/rtp_rtcp/source/flexfec_header_reader_writer.h"
+
+#include <string.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#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 {
+
+namespace {
+
+using Packet = ForwardErrorCorrection::Packet;
+using ProtectedStream = ForwardErrorCorrection::ProtectedStream;
+using ReceivedFecPacket = ForwardErrorCorrection::ReceivedFecPacket;
+using ::testing::Each;
+using ::testing::ElementsAreArray;
+
+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};
+
+constexpr size_t kMediaPacketLength = 1234;
+
+// 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 FecPacketStreamReadProperties {
+ ProtectedStream stream;
+ rtc::ArrayView<const uint8_t> mask;
+};
+
+struct FecPacketStreamWriteProperties {
+ size_t byte_index;
+ uint16_t seq_num_base;
+ rtc::ArrayView<const uint8_t> mask;
+};
+
+Packet WritePacket(
+ std::vector<FecHeaderWriter::ProtectedStream> protected_streams) {
+ Packet written_packet;
+ written_packet.data.SetSize(kMediaPacketLength);
+ uint8_t* data = written_packet.data.MutableData();
+ for (size_t i = 0; i < written_packet.data.size(); ++i) {
+ data[i] = i;
+ }
+
+ FlexfecHeaderWriter writer;
+ writer.FinalizeFecHeader(protected_streams, written_packet);
+ return written_packet;
+}
+
+void VerifyReadHeaders(size_t expected_fec_header_size,
+ const ReceivedFecPacket& read_packet,
+ std::vector<FecPacketStreamReadProperties> 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);
+}
+
+void VerifyFinalizedHeaders(
+ const Packet& written_packet,
+ std::vector<FecPacketStreamWriteProperties> expected) {
+ const uint8_t* packet = written_packet.data.data();
+ EXPECT_EQ(packet[0] & 0x80, 0x00); // F bit clear.
+ EXPECT_EQ(packet[0] & 0x40, 0x00); // R bit clear.
+ for (size_t i = 0; i < expected.size(); ++i) {
+ SCOPED_TRACE(i);
+ // Verify value of seq_num_base.
+ EXPECT_EQ(
+ ByteReader<uint16_t>::ReadBigEndian(packet + expected[i].byte_index),
+ expected[i].seq_num_base);
+ // Verify mask.
+ EXPECT_THAT(rtc::MakeArrayView(packet + expected[i].byte_index + 2,
+ expected[i].mask.size()),
+ ElementsAreArray(expected[i].mask));
+ }
+}
+
+void VerifyWrittenAndReadHeaders(
+ std::vector<FecHeaderWriter::ProtectedStream> write_protected_streams,
+ uint16_t expected_header_size) {
+ // Write FEC Header.
+ Packet written_packet = WritePacket(write_protected_streams);
+
+ // Read FEC Header using written data.
+ ReceivedFecPacket read_packet;
+ read_packet.pkt = rtc::make_ref_counted<Packet>();
+ read_packet.pkt->data = written_packet.data;
+ for (const FecHeaderWriter::ProtectedStream& stream :
+ write_protected_streams) {
+ read_packet.protected_streams.push_back({.ssrc = stream.ssrc});
+ }
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ // Verify header contents.
+ EXPECT_EQ(read_packet.fec_header_size, expected_header_size);
+ EXPECT_EQ(read_packet.protected_streams.size(),
+ write_protected_streams.size());
+ for (size_t i = 0; i < write_protected_streams.size(); ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(read_packet.protected_streams[i].seq_num_base,
+ write_protected_streams[i].seq_num_base);
+
+ size_t mask_write_size = write_protected_streams[i].packet_mask.size();
+ // Read mask returned may be larger than the mask that was sent to the
+ // writer; That is ok as long as the specified part of the mask matches, and
+ // the rest is 0s.
+ FlexfecHeaderWriter writer;
+ size_t expected_mask_read_size = writer.MinPacketMaskSize(
+ write_protected_streams[i].packet_mask.data(), mask_write_size);
+ EXPECT_EQ(read_packet.protected_streams[i].packet_mask_size,
+ expected_mask_read_size);
+
+ const uint8_t* read_mask_ptr =
+ read_packet.pkt->data.cdata() +
+ read_packet.protected_streams[i].packet_mask_offset;
+ // Verify actual mask bits.
+ EXPECT_THAT(rtc::MakeArrayView(read_mask_ptr, mask_write_size),
+ ElementsAreArray(write_protected_streams[i].packet_mask));
+ // If read mask size is larger than written mask size, verify all other bits
+ // are 0.
+ EXPECT_THAT(rtc::MakeArrayView(read_mask_ptr + mask_write_size,
+ expected_mask_read_size - mask_write_size),
+ Each(0));
+ }
+
+ // Verify that the call to ReadFecHeader did not tamper with the payload.
+ EXPECT_THAT(
+ rtc::MakeArrayView(
+ read_packet.pkt->data.cdata() + read_packet.fec_header_size,
+ read_packet.pkt->data.size() - read_packet.fec_header_size),
+ ElementsAreArray(written_packet.data.cdata() + expected_header_size,
+ written_packet.data.size() - expected_header_size));
+}
+
+} // namespace
+
+TEST(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {
+ {.ssrc = 0x01}, {.ssrc = 0x02}, {.ssrc = 0x03}, {.ssrc = 0x04}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
+
+ std::vector<FecPacketStreamReadProperties> 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(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ // No protected ssrcs.
+ read_packet.protected_streams = {};
+
+ FlexfecHeaderReader reader;
+ EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
+}
+
+TEST(FlexfecHeaderReaderTest, 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<Packet>();
+ read_packet.pkt->data.SetData(kPacketData);
+ read_packet.protected_streams = {{.ssrc = 0x01}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
+}
+
+TEST(FlexfecHeaderReaderTest, 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<Packet>();
+ // 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}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
+}
+
+TEST(FlexfecHeaderReaderTest, 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<Packet>();
+ // 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}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
+}
+
+TEST(FlexfecHeaderReaderTest, 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<Packet>();
+ // 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}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
+}
+
+TEST(FlexfecHeaderReaderTest, 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<Packet>();
+ // 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}};
+
+ FlexfecHeaderReader reader;
+ EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
+}
+
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit0SetSingleStream) {
+ constexpr uint8_t kFlexfecPacketMask[] = {0x88, 0x81};
+ constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02};
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ Packet written_packet = WritePacket({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}});
+
+ std::vector<FecPacketStreamWriteProperties> expected = {
+ {.byte_index = 8,
+ .seq_num_base = kMediaStartSeqNum,
+ .mask = kFlexfecPacketMask}};
+
+ VerifyFinalizedHeaders(written_packet, expected);
+}
+
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit1SetSingleStream) {
+ constexpr uint8_t kFlexfecPacketMask[] = {0x48, 0x81, 0x82, 0x11, 0x00, 0x21};
+ constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ Packet written_packet = WritePacket({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}});
+
+ std::vector<FecPacketStreamWriteProperties> expected = {
+ {.byte_index = 8,
+ .seq_num_base = kMediaStartSeqNum,
+ .mask = kFlexfecPacketMask}};
+
+ VerifyFinalizedHeaders(written_packet, expected);
+}
+
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithNoKBitsSetSingleStream) {
+ constexpr uint8_t kFlexfecPacketMask[] = {
+ 0x11, 0x11, // K-bit 0 clear.
+ 0x11, 0x11, 0x11, 0x10, // K-bit 1 clear.
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
+ };
+ constexpr uint8_t kUlpfecPacketMask[] = {0x22, 0x22, 0x44, 0x44, 0x44, 0x41};
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ Packet written_packet = WritePacket({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}});
+
+ std::vector<FecPacketStreamWriteProperties> expected = {
+ {.byte_index = 8,
+ .seq_num_base = kMediaStartSeqNum,
+ .mask = kFlexfecPacketMask}};
+
+ VerifyFinalizedHeaders(written_packet, expected);
+}
+
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderMultipleStreamsMultipleMasks) {
+ constexpr uint8_t kFlexfecPacketMask1[] = {
+ 0x11, 0x11, // K-bit 0 clear.
+ 0x11, 0x11, 0x11, 0x10, // K-bit 1 clear.
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
+ };
+ constexpr uint8_t kUlpfecPacketMask1[] = {0x22, 0x22, 0x44, 0x44, 0x44, 0x41};
+ constexpr uint16_t kMediaStartSeqNum1 = 1234;
+ constexpr uint8_t kFlexfecPacketMask2[] = {0x88, 0x81};
+ constexpr uint8_t kUlpfecPacketMask2[] = {0x11, 0x02};
+ constexpr uint16_t kMediaStartSeqNum2 = 2345;
+ constexpr uint8_t kFlexfecPacketMask3[] = {0x48, 0x81, 0x82,
+ 0x11, 0x00, 0x21};
+ constexpr uint8_t kUlpfecPacketMask3[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
+ constexpr uint16_t kMediaStartSeqNum3 = 3456;
+ constexpr uint8_t kFlexfecPacketMask4[] = {
+ 0x55, 0xAA, // K-bit 0 clear.
+ 0x22, 0xAB, 0xCD, 0xEF, // K-bit 1 clear.
+ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
+ };
+ constexpr uint8_t kUlpfecPacketMask4[] = {0xAB, 0x54, 0x8A, 0xAF, 0x37, 0xBF};
+ constexpr uint16_t kMediaStartSeqNum4 = 4567;
+
+ Packet written_packet = WritePacket({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum1,
+ .packet_mask = kUlpfecPacketMask1},
+ {.ssrc = 0x02,
+ .seq_num_base = kMediaStartSeqNum2,
+ .packet_mask = kUlpfecPacketMask2},
+ {.ssrc = 0x03,
+ .seq_num_base = kMediaStartSeqNum3,
+ .packet_mask = kUlpfecPacketMask3},
+ {.ssrc = 0x04,
+ .seq_num_base = kMediaStartSeqNum4,
+ .packet_mask = kUlpfecPacketMask4}});
+
+ std::vector<FecPacketStreamWriteProperties> expected = {
+ {.byte_index = 8,
+ .seq_num_base = kMediaStartSeqNum1,
+ .mask = kFlexfecPacketMask1},
+ {.byte_index = 24,
+ .seq_num_base = kMediaStartSeqNum2,
+ .mask = kFlexfecPacketMask2},
+ {.byte_index = 28,
+ .seq_num_base = kMediaStartSeqNum3,
+ .mask = kFlexfecPacketMask3},
+ {.byte_index = 36,
+ .seq_num_base = kMediaStartSeqNum4,
+ .mask = kFlexfecPacketMask4}};
+
+ VerifyFinalizedHeaders(written_packet, expected);
+}
+
+// TODO(bugs.webrtc.org/15002): reimplement and add tests for multi stream cases
+// after updating the MinPacketMaskSize and FecHeaderSize functions.
+
+TEST(FlexfecHeaderWriterTest, ContractsShortUlpfecPacketMaskWithBit15Clear) {}
+
+TEST(FlexfecHeaderWriterTest, ExpandsShortUlpfecPacketMaskWithBit15Set) {}
+
+TEST(FlexfecHeaderWriterTest,
+ ContractsLongUlpfecPacketMaskWithBit46ClearBit47Clear) {}
+
+TEST(FlexfecHeaderWriterTest,
+ ExpandsLongUlpfecPacketMaskWithBit46SetBit47Clear) {}
+
+TEST(FlexfecHeaderWriterTest,
+ ExpandsLongUlpfecPacketMaskWithBit46ClearBit47Set) {}
+
+TEST(FlexfecHeaderWriterTest, ExpandsLongUlpfecPacketMaskWithBit46SetBit47Set) {
+}
+
+TEST(FlexfecHeaderReaderWriterTest,
+ WriteAndReadSmallUlpfecPacketHeaderWithMaskBit15ClearSingleStream) {
+ constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02}; // Bit 15 clear.
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ constexpr uint16_t kExpectedHeaderSize = 12;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}},
+ kExpectedHeaderSize);
+}
+
+TEST(FlexfecHeaderReaderWriterTest,
+ WriteAndReadSmallUlpfecPacketHeaderWithMaskBit15SetSingleStream) {
+ constexpr uint8_t kUlpfecPacketMask[] = {0xAA, 0xFF}; // Bit 15 set.
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ constexpr uint16_t kExpectedHeaderSize = 16;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}},
+ kExpectedHeaderSize);
+}
+
+TEST(FlexfecHeaderReaderWriterTest,
+ WriteAndReadLargeUlpfecPacketHeaderWithMaskBits46And47ClearSingleStream) {
+ constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44,
+ 0x00, 0x84}; // Bits 46, 47 clear.
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ constexpr uint16_t kExpectedHeaderSize = 16;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}},
+ kExpectedHeaderSize);
+}
+
+TEST(
+ FlexfecHeaderReaderWriterTest,
+ WriteAndReadLargeUlpfecPacketHeaderWithMaskBit46SetBit47ClearSingleStream) {
+ constexpr uint8_t kUlpfecPacketMask[] = {
+ 0x91, 0x02, 0x08, 0x44, 0x00, 0x86}; // Bit 46 set, bit 47 clear.
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ constexpr uint16_t kExpectedHeaderSize = 24;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}},
+ kExpectedHeaderSize);
+}
+
+TEST(
+ FlexfecHeaderReaderWriterTest,
+ WriteAndReadLargeUlpfecPacketHeaderMaskWithBit46ClearBit47SetSingleStream) {
+ constexpr uint8_t kUlpfecPacketMask[] = {
+ 0x91, 0x02, 0x08, 0x44, 0x00, 0x85}; // Bit 46 clear, bit 47 set.
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ constexpr uint16_t kExpectedHeaderSize = 24;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}},
+ kExpectedHeaderSize);
+}
+
+TEST(FlexfecHeaderReaderWriterTest,
+ WriteAndReadLargeUlpfecPacketHeaderWithMaskBits46And47SetSingleStream) {
+ constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44,
+ 0x00, 0x87}; // Bits 46, 47 set.
+ constexpr uint16_t kMediaStartSeqNum = 1234;
+ constexpr uint16_t kExpectedHeaderSize = 24;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum,
+ .packet_mask = kUlpfecPacketMask}},
+ kExpectedHeaderSize);
+}
+
+TEST(FlexfecHeaderReaderWriterTest, WriteAndReadMultipleStreamsMultipleMasks) {
+ constexpr uint8_t kUlpfecPacketMask1[] = {0x11, 0x02};
+ constexpr uint16_t kMediaStartSeqNum1 = 1234;
+ constexpr uint8_t kUlpfecPacketMask2[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
+ constexpr uint16_t kMediaStartSeqNum2 = 2345;
+ constexpr uint8_t kUlpfecPacketMask3[] = {0xAA, 0xFF};
+ constexpr uint16_t kMediaStartSeqNum3 = 3456;
+ constexpr uint8_t kUlpfecPacketMask4[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x87};
+ constexpr uint16_t kMediaStartSeqNum4 = 4567;
+ constexpr uint16_t kExpectedHeaderSize = 44;
+
+ VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
+ .seq_num_base = kMediaStartSeqNum1,
+ .packet_mask = kUlpfecPacketMask1},
+ {.ssrc = 0x02,
+ .seq_num_base = kMediaStartSeqNum2,
+ .packet_mask = kUlpfecPacketMask2},
+ {.ssrc = 0x03,
+ .seq_num_base = kMediaStartSeqNum3,
+ .packet_mask = kUlpfecPacketMask3},
+ {.ssrc = 0x04,
+ .seq_num_base = kMediaStartSeqNum4,
+ .packet_mask = kUlpfecPacketMask4}},
+ kExpectedHeaderSize);
+}
+
+} // namespace webrtc