Change flexfec header reader to parse according to updated RFC.

This change changes the flexfec header reader ReadFecHeader function to parse the FEC header according the the updated RFC. The fec_packet argument is expected to have the protected ssrcs list already populated, as they should be retrieved from the RTP header.
Updated and added Reader unittests. Unittests that are relevant for the Writer, were put inside a comment. In the next change set, when the header writer will be updated, we will update the unittests accordingly.

Bug: webrtc:15002
Change-Id: I118303e31c15c356ffeb2c0aafe503cf293bcad6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/303260
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40172}
This commit is contained in:
Yosef Twaik 2023-05-15 09:56:11 -07:00 committed by WebRTC LUCI CQ
parent e4a9a6dd6b
commit 4c1e9598a3
4 changed files with 682 additions and 121 deletions

View File

@ -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",

View File

@ -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<uint8_t>::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<uint32_t>::ReadBigEndian(&data[12]);
uint16_t seq_num_base = ByteReader<uint16_t>::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<uint16_t>::ReadBigEndian(&packet_mask[0]);
// Shift away K-bit 0, implicitly clearing the last bit.
mask_part0 <<= 1;
ByteWriter<uint16_t>::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<uint32_t>::ReadBigEndian(&packet_mask[2]);
// Shift away K-bit 1 and bit 15, implicitly clearing the last two bits.
mask_part1 <<= 2;
ByteWriter<uint32_t>::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<uint16_t>::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<uint16_t>::ReadBigEndian(&data[byte_index]);
// Shift away K-bit 0, implicitly clearing the last bit.
mask_part0 <<= 1;
ByteWriter<uint16_t>::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<uint32_t>::ReadBigEndian(&data[byte_index]);
// Shift away K-bit 1 and bit 15, implicitly clearing the last two bits.
mask_part1 <<= 2;
ByteWriter<uint32_t>::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<uint64_t>::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<uint64_t>::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<uint64_t>::ReadBigEndian(&packet_mask[6]);
// Shift away K-bit 2, bit 46, and bit 47, implicitly clearing the last
// three bits.
mask_part2 <<= 3;
ByteWriter<uint64_t>::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;

View File

@ -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:

View File

@ -10,27 +10,596 @@
#include "modules/rtp_rtcp/source/flexfec_header_reader_writer2.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 {
// 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<const uint8_t> mask;
};
void VerifyReadHeaders(size_t expected_fec_header_size,
const ReceivedFecPacket& read_packet,
std::vector<FecPacketStreamProperties> 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<Packet>();
read_packet.pkt->data.SetData(kPacketData);
read_packet.protected_streams = {{.ssrc = 0x01}};
FlexfecHeaderReader2 reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
std::vector<FecPacketStreamProperties> 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<Packet>();
read_packet.pkt->data.SetData(kPacketData);
read_packet.protected_streams = {{.ssrc = 0x01}};
FlexfecHeaderReader2 reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
std::vector<FecPacketStreamProperties> 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<Packet>();
read_packet.pkt->data.SetData(kPacketData);
read_packet.protected_streams = {{.ssrc = 0x01}};
FlexfecHeaderReader2 reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
std::vector<FecPacketStreamProperties> 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<Packet>();
read_packet.pkt->data.SetData(kPacketData);
read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};
FlexfecHeaderReader2 reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
std::vector<FecPacketStreamProperties> 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<Packet>();
read_packet.pkt->data.SetData(kPacketData);
read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};
FlexfecHeaderReader2 reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
std::vector<FecPacketStreamProperties> 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<Packet>();
read_packet.pkt->data.SetData(kPacketData);
read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};
FlexfecHeaderReader2 reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
std::vector<FecPacketStreamProperties> 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<Packet>();
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<FecPacketStreamProperties> 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<Packet>();
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<Packet>();
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<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}};
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<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}};
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<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}};
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<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}};
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) {}