| /* |
| * 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 |