webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer_unittest.cc
Yosef Twaik f3de65aebe Change ReceivedFecPacket to have list of ssrcs, seq nums and masks.
This change replaces ReceivedFecPacket FEC header fields with vectors (for protected ssrcs, sequence numbers and masks), which is needed to support protection of multiple ssrcs in the same FEC packet (as part of the flexfec RFC - https://datatracker.ietf.org/doc/html/rfc8627).

Bug: webrtc:15002
Change-Id: I82c54203fcfec10c760f34f805cc6308562e3df1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/303200
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40075}
2023-05-16 06:07:48 +00:00

250 lines
10 KiB
C++

/*
* Copyright (c) 2016 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/ulpfec_header_reader_writer.h"
#include <string.h>
#include <memory>
#include <utility>
#include "api/scoped_refptr.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 ReceivedFecPacket = ForwardErrorCorrection::ReceivedFecPacket;
using ::testing::SizeIs;
constexpr uint32_t kMediaSsrc = 1254983;
constexpr uint16_t kMediaStartSeqNum = 825;
constexpr size_t kMediaPacketLength = 1234;
constexpr size_t kUlpfecHeaderSizeLBitClear = 14;
constexpr size_t kUlpfecHeaderSizeLBitSet = 18;
constexpr size_t kUlpfecPacketMaskOffset = 12;
std::unique_ptr<uint8_t[]> GeneratePacketMask(size_t packet_mask_size,
uint64_t seed) {
Random random(seed);
std::unique_ptr<uint8_t[]> packet_mask(new uint8_t[packet_mask_size]);
for (size_t i = 0; i < packet_mask_size; ++i) {
packet_mask[i] = random.Rand<uint8_t>();
}
return packet_mask;
}
std::unique_ptr<Packet> WriteHeader(const uint8_t* packet_mask,
size_t packet_mask_size) {
UlpfecHeaderWriter writer;
std::unique_ptr<Packet> written_packet(new 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; // Actual content doesn't matter.
}
writer.FinalizeFecHeader(kMediaSsrc, kMediaStartSeqNum, packet_mask,
packet_mask_size, written_packet.get());
return written_packet;
}
std::unique_ptr<ReceivedFecPacket> ReadHeader(const Packet& written_packet) {
UlpfecHeaderReader reader;
std::unique_ptr<ReceivedFecPacket> read_packet(new ReceivedFecPacket());
read_packet->ssrc = kMediaSsrc;
read_packet->pkt = rtc::scoped_refptr<Packet>(new Packet());
read_packet->pkt->data = written_packet.data;
EXPECT_TRUE(reader.ReadFecHeader(read_packet.get()));
return read_packet;
}
void VerifyHeaders(size_t expected_fec_header_size,
const uint8_t* expected_packet_mask,
size_t expected_packet_mask_size,
const Packet& written_packet,
const ReceivedFecPacket& read_packet) {
EXPECT_EQ(kMediaSsrc, read_packet.ssrc);
EXPECT_EQ(expected_fec_header_size, read_packet.fec_header_size);
ASSERT_THAT(read_packet.protected_streams, SizeIs(1));
EXPECT_EQ(read_packet.protected_streams[0].ssrc, kMediaSsrc);
EXPECT_EQ(read_packet.protected_streams[0].seq_num_base, kMediaStartSeqNum);
EXPECT_EQ(read_packet.protected_streams[0].packet_mask_offset,
kUlpfecPacketMaskOffset);
ASSERT_EQ(read_packet.protected_streams[0].packet_mask_size,
expected_packet_mask_size);
EXPECT_EQ(written_packet.data.size() - expected_fec_header_size,
read_packet.protection_length);
EXPECT_EQ(0, memcmp(expected_packet_mask,
read_packet.pkt->data.MutableData() +
read_packet.protected_streams[0].packet_mask_offset,
read_packet.protected_streams[0].packet_mask_size));
// Verify that the call to ReadFecHeader did not tamper with the payload.
EXPECT_EQ(0, memcmp(written_packet.data.data() + expected_fec_header_size,
read_packet.pkt->data.cdata() + expected_fec_header_size,
written_packet.data.size() - expected_fec_header_size));
}
} // namespace
TEST(UlpfecHeaderReaderTest, ReadsSmallHeader) {
const uint8_t packet[] = {
0x00, 0x12, 0xab, 0xcd, // L bit clear, "random" payload type and SN base
0x12, 0x34, 0x56, 0x78, // "random" TS recovery
0xab, 0xcd, 0x11, 0x22, // "random" length recovery and protection length
0x33, 0x44, // "random" packet mask
0x00, 0x00, 0x00, 0x00 // payload
};
const size_t packet_length = sizeof(packet);
ReceivedFecPacket read_packet;
read_packet.pkt = rtc::scoped_refptr<Packet>(new Packet());
read_packet.pkt->data.SetData(packet, packet_length);
UlpfecHeaderReader reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
EXPECT_EQ(14U, read_packet.fec_header_size);
EXPECT_EQ(0xabcdU, read_packet.protected_streams[0].seq_num_base);
EXPECT_EQ(12U, read_packet.protected_streams[0].packet_mask_offset);
EXPECT_EQ(2U, read_packet.protected_streams[0].packet_mask_size);
EXPECT_EQ(0x1122U, read_packet.protection_length);
}
TEST(UlpfecHeaderReaderTest, ReadsLargeHeader) {
const uint8_t packet[] = {
0x40, 0x12, 0xab, 0xcd, // L bit set, "random" payload type and SN base
0x12, 0x34, 0x56, 0x78, // "random" TS recovery
0xab, 0xcd, 0x11, 0x22, // "random" length recovery and protection length
0x33, 0x44, 0x55, 0x66, // "random" packet mask
0x77, 0x88, //
0x00, 0x00, 0x00, 0x00 // payload
};
const size_t packet_length = sizeof(packet);
ReceivedFecPacket read_packet;
read_packet.pkt = rtc::scoped_refptr<Packet>(new Packet());
read_packet.pkt->data.SetData(packet, packet_length);
UlpfecHeaderReader reader;
EXPECT_TRUE(reader.ReadFecHeader(&read_packet));
EXPECT_EQ(18U, read_packet.fec_header_size);
EXPECT_EQ(0xabcdU, read_packet.protected_streams[0].seq_num_base);
EXPECT_EQ(12U, read_packet.protected_streams[0].packet_mask_offset);
EXPECT_EQ(6U, read_packet.protected_streams[0].packet_mask_size);
EXPECT_EQ(0x1122U, read_packet.protection_length);
}
TEST(UlpfecHeaderWriterTest, FinalizesSmallHeader) {
const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitClear;
auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd);
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;
}
UlpfecHeaderWriter writer;
writer.FinalizeFecHeader(kMediaSsrc, kMediaStartSeqNum, packet_mask.get(),
packet_mask_size, &written_packet);
const uint8_t* packet = written_packet.data.cdata();
EXPECT_EQ(0x00, packet[0] & 0x80); // E bit.
EXPECT_EQ(0x00, packet[0] & 0x40); // L bit.
EXPECT_EQ(kMediaStartSeqNum, ByteReader<uint16_t>::ReadBigEndian(packet + 2));
EXPECT_EQ(
static_cast<uint16_t>(kMediaPacketLength - kUlpfecHeaderSizeLBitClear),
ByteReader<uint16_t>::ReadBigEndian(packet + 10));
EXPECT_EQ(0, memcmp(packet + kUlpfecPacketMaskOffset, packet_mask.get(),
packet_mask_size));
}
TEST(UlpfecHeaderWriterTest, FinalizesLargeHeader) {
const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitSet;
auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd);
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;
}
UlpfecHeaderWriter writer;
writer.FinalizeFecHeader(kMediaSsrc, kMediaStartSeqNum, packet_mask.get(),
packet_mask_size, &written_packet);
const uint8_t* packet = written_packet.data.cdata();
EXPECT_EQ(0x00, packet[0] & 0x80); // E bit.
EXPECT_EQ(0x40, packet[0] & 0x40); // L bit.
EXPECT_EQ(kMediaStartSeqNum, ByteReader<uint16_t>::ReadBigEndian(packet + 2));
EXPECT_EQ(
static_cast<uint16_t>(kMediaPacketLength - kUlpfecHeaderSizeLBitSet),
ByteReader<uint16_t>::ReadBigEndian(packet + 10));
EXPECT_EQ(0, memcmp(packet + kUlpfecPacketMaskOffset, packet_mask.get(),
packet_mask_size));
}
TEST(UlpfecHeaderWriterTest, CalculateSmallHeaderSize) {
const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitClear;
auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd);
UlpfecHeaderWriter writer;
size_t min_packet_mask_size =
writer.MinPacketMaskSize(packet_mask.get(), packet_mask_size);
EXPECT_EQ(kUlpfecPacketMaskSizeLBitClear, min_packet_mask_size);
EXPECT_EQ(kUlpfecHeaderSizeLBitClear,
writer.FecHeaderSize(min_packet_mask_size));
}
TEST(UlpfecHeaderWriterTest, CalculateLargeHeaderSize) {
const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitSet;
auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd);
UlpfecHeaderWriter writer;
size_t min_packet_mask_size =
writer.MinPacketMaskSize(packet_mask.get(), packet_mask_size);
EXPECT_EQ(kUlpfecPacketMaskSizeLBitSet, min_packet_mask_size);
EXPECT_EQ(kUlpfecHeaderSizeLBitSet,
writer.FecHeaderSize(min_packet_mask_size));
}
TEST(UlpfecHeaderReaderWriterTest, WriteAndReadSmallHeader) {
const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitClear;
auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd);
auto written_packet = WriteHeader(packet_mask.get(), packet_mask_size);
auto read_packet = ReadHeader(*written_packet);
VerifyHeaders(kUlpfecHeaderSizeLBitClear, packet_mask.get(), packet_mask_size,
*written_packet, *read_packet);
}
TEST(UlpfecHeaderReaderWriterTest, WriteAndReadLargeHeader) {
const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitSet;
auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd);
auto written_packet = WriteHeader(packet_mask.get(), packet_mask_size);
auto read_packet = ReadHeader(*written_packet);
VerifyHeaders(kUlpfecHeaderSizeLBitSet, packet_mask.get(), packet_mask_size,
*written_packet, *read_packet);
}
} // namespace webrtc