blob: 14254c129804cdc667a0e46fb7f1e3a44c9f1409 [file] [edit]
/*
* Copyright (c) 2026 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/video_coding/sframe_packet_buffer.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <variant>
#include <vector>
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/sframe_descriptor.h"
#include "modules/rtp_rtcp/source/sframe_rtp_packet_received.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
constexpr size_t kBufferSize = 16;
class SFramePacketBufferTest : public ::testing::Test {
protected:
SFramePacketBufferTest() : buffer_(kBufferSize) {}
enum IsStart { kNotStart, kStart };
enum IsEnd { kNotEnd, kEnd };
std::unique_ptr<SframeRtpPacketReceived> MakePacket(
uint16_t seq_num,
IsStart start,
IsEnd end,
uint32_t timestamp = 1000,
uint8_t payload_type = 96,
SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame) {
auto rtp = std::make_unique<RtpPacketReceived>();
rtp->SetSequenceNumber(seq_num);
rtp->SetTimestamp(timestamp);
rtp->SetPayloadType(payload_type);
SFrameDescriptor desc;
desc.start = (start == kStart);
desc.end = (end == kEnd);
desc.encryption_level = encryption_level;
return std::make_unique<SframeRtpPacketReceived>(std::move(rtp), desc);
}
struct TestResult {
SFramePacketBuffer::InsertResult status =
SFramePacketBuffer::InsertResult::kNoFrame;
SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame;
std::vector<std::unique_ptr<RtpPacketReceived>> packets;
};
bool BufferCleared(SFramePacketBuffer::InsertResult r) {
return r == SFramePacketBuffer::InsertResult::kBufferCleared;
}
TestResult InsertInto(SFramePacketBuffer& buf,
std::unique_ptr<SframeRtpPacketReceived> packet) {
auto insert_result = buf.InsertPacket(std::move(packet));
return std::visit(
overloaded{
[](SFramePacketBuffer::AssembledFrame& frame) -> TestResult {
return {.encryption_level = frame.encryption_level,
.packets = std::move(frame.packets)};
},
[](SFramePacketBuffer::InsertResult& status) -> TestResult {
return {.status = status};
},
},
insert_result);
}
TestResult Insert(
uint16_t seq_num,
IsStart start,
IsEnd end,
uint32_t timestamp = 1000,
uint8_t payload_type = 96,
SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame) {
return InsertInto(buffer_, MakePacket(seq_num, start, end, timestamp,
payload_type, encryption_level));
}
SFramePacketBuffer buffer_;
};
TEST_F(SFramePacketBufferTest, SinglePacketFrame) {
auto result = Insert(100, kStart, kEnd);
ASSERT_EQ(result.packets.size(), 1u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 100);
}
TEST_F(SFramePacketBufferTest, TwoPacketFrame) {
EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
auto result = Insert(101, kNotStart, kEnd);
ASSERT_EQ(result.packets.size(), 2u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 100);
EXPECT_EQ(result.packets[1]->SequenceNumber(), 101);
}
TEST_F(SFramePacketBufferTest, ThreePacketFrame) {
EXPECT_TRUE(Insert(200, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(201, kNotStart, kNotEnd).packets.empty());
auto result = Insert(202, kNotStart, kEnd);
ASSERT_EQ(result.packets.size(), 3u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 200);
EXPECT_EQ(result.packets[1]->SequenceNumber(), 201);
EXPECT_EQ(result.packets[2]->SequenceNumber(), 202);
}
TEST_F(SFramePacketBufferTest, TwoPacketFrameReversed) {
EXPECT_TRUE(Insert(101, kNotStart, kEnd).packets.empty());
auto result = Insert(100, kStart, kNotEnd);
ASSERT_EQ(result.packets.size(), 2u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 100);
EXPECT_EQ(result.packets[1]->SequenceNumber(), 101);
}
TEST_F(SFramePacketBufferTest, ThreePacketFrameMiddleLast) {
EXPECT_TRUE(Insert(300, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(302, kNotStart, kEnd).packets.empty());
auto result = Insert(301, kNotStart, kNotEnd);
ASSERT_EQ(result.packets.size(), 3u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 300);
EXPECT_EQ(result.packets[1]->SequenceNumber(), 301);
EXPECT_EQ(result.packets[2]->SequenceNumber(), 302);
}
TEST_F(SFramePacketBufferTest, TwoConsecutiveSinglePacketFrames) {
auto r1 = Insert(50, kStart, kEnd, /*timestamp=*/1000);
ASSERT_EQ(r1.packets.size(), 1u);
auto r2 = Insert(51, kStart, kEnd, /*timestamp=*/2000);
ASSERT_EQ(r2.packets.size(), 1u);
EXPECT_EQ(r2.packets[0]->SequenceNumber(), 51);
}
TEST_F(SFramePacketBufferTest, TwoFramesBackToBack) {
// Frame 1: seq 10-11
EXPECT_TRUE(Insert(10, kStart, kNotEnd, /*timestamp=*/100).packets.empty());
auto r1 = Insert(11, kNotStart, kEnd, /*timestamp=*/100);
ASSERT_EQ(r1.packets.size(), 2u);
// Frame 2: seq 12-13
EXPECT_TRUE(Insert(12, kStart, kNotEnd, /*timestamp=*/200).packets.empty());
auto r2 = Insert(13, kNotStart, kEnd, /*timestamp=*/200);
ASSERT_EQ(r2.packets.size(), 2u);
}
TEST_F(SFramePacketBufferTest, IncompleteFrameMissingEnd) {
EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(101, kNotStart, kNotEnd).packets.empty());
// Neither packet completes a frame, but completing the frame later works.
auto result = Insert(102, kNotStart, kEnd);
EXPECT_EQ(result.packets.size(), 3u);
}
TEST_F(SFramePacketBufferTest, IncompleteFrameMissingStart) {
EXPECT_TRUE(Insert(101, kNotStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(102, kNotStart, kEnd).packets.empty());
// Packets are buffered but no S-bit found, so adding it completes the frame.
auto result = Insert(100, kStart, kNotEnd);
EXPECT_EQ(result.packets.size(), 3u);
}
TEST_F(SFramePacketBufferTest, IncompleteFrameGap) {
// S-bit at 100, gap at 101, E-bit at 102.
EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(102, kNotStart, kEnd).packets.empty());
// Filling the gap completes the frame.
auto result = Insert(101, kNotStart, kNotEnd);
EXPECT_EQ(result.packets.size(), 3u);
}
TEST_F(SFramePacketBufferTest, DuplicatePacketDropped) {
EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
// Original packet still in buffer -- completing the frame proves it.
auto result = Insert(101, kNotStart, kEnd);
EXPECT_EQ(result.packets.size(), 2u);
}
TEST_F(SFramePacketBufferTest, TBitMismatchDropsFrame) {
EXPECT_TRUE(
Insert(100, kStart, kNotEnd, 1000, 96, SframeEncryptionLevel::kFrame)
.packets.empty());
auto result =
Insert(101, kNotStart, kEnd, 1000, 96, SframeEncryptionLevel::kPacket);
EXPECT_TRUE(result.packets.empty());
// Dropped packets don't block subsequent frames.
auto r2 = Insert(102, kStart, kEnd, /*timestamp=*/2000);
EXPECT_FALSE(r2.packets.empty());
}
TEST_F(SFramePacketBufferTest, PayloadTypeMismatchDropsFrame) {
EXPECT_TRUE(
Insert(100, kStart, kNotEnd, 1000, /*payload_type=*/96).packets.empty());
auto result = Insert(101, kNotStart, kEnd, 1000, /*payload_type=*/97);
EXPECT_TRUE(result.packets.empty());
auto r2 = Insert(102, kStart, kEnd, /*timestamp=*/2000);
EXPECT_FALSE(r2.packets.empty());
}
TEST_F(SFramePacketBufferTest, TimestampMismatchDropsFrame) {
EXPECT_TRUE(Insert(100, kStart, kNotEnd, /*timestamp=*/1000).packets.empty());
auto result = Insert(101, kNotStart, kEnd, /*timestamp=*/2000);
EXPECT_TRUE(result.packets.empty());
auto r2 = Insert(102, kStart, kEnd, /*timestamp=*/3000);
EXPECT_FALSE(r2.packets.empty());
}
TEST_F(SFramePacketBufferTest, PacketizedBitPropagated) {
auto result =
Insert(100, kStart, kEnd, 1000, 96, SframeEncryptionLevel::kPacket);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.encryption_level, SframeEncryptionLevel::kPacket);
}
TEST_F(SFramePacketBufferTest, NonPacketizedBitPropagated) {
auto result =
Insert(100, kStart, kEnd, 1000, 96, SframeEncryptionLevel::kFrame);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.encryption_level, SframeEncryptionLevel::kFrame);
}
TEST_F(SFramePacketBufferTest, AssembledPacketsRemovedFromBuffer) {
auto r1 = Insert(100, kStart, kEnd);
ASSERT_FALSE(r1.packets.empty());
// Slot is freed after assembly. Re-inserting assembles a new frame.
auto r2 = Insert(100, kStart, kEnd);
ASSERT_FALSE(r2.packets.empty());
EXPECT_EQ(r2.packets.size(), 1u);
}
TEST_F(SFramePacketBufferTest, MultiPacketFrameRemovedFromBuffer) {
Insert(100, kStart, kNotEnd);
Insert(101, kNotStart, kNotEnd);
auto result = Insert(102, kNotStart, kEnd);
ASSERT_EQ(result.packets.size(), 3u);
// Slots freed -- a new frame at same seq range can be inserted.
auto r2 = Insert(100, kStart, kEnd, /*timestamp=*/9000);
EXPECT_FALSE(r2.packets.empty());
}
TEST_F(SFramePacketBufferTest, ClearRemovesAll) {
Insert(100, kStart, kNotEnd);
Insert(101, kNotStart, kNotEnd);
buffer_.Clear();
// After clear, inserting a new single-packet frame works (no stale data).
auto result = Insert(200, kStart, kEnd, /*timestamp=*/5000);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->SequenceNumber(), 200);
}
TEST_F(SFramePacketBufferTest, ClearToRemovesOldPackets) {
Insert(100, kStart, kNotEnd, /*timestamp=*/1000);
Insert(101, kNotStart, kNotEnd, /*timestamp=*/1000);
Insert(102, kNotStart, kNotEnd, /*timestamp=*/1000);
buffer_.ClearTo(101);
// Packets 100-101 cleared, packet 102 remains.
// Completing a frame starting from 102 should work.
auto result = Insert(103, kNotStart, kEnd, /*timestamp=*/1000);
// 102 has no S-bit and 100-101 are gone, so this can't assemble.
EXPECT_TRUE(result.packets.empty());
}
TEST_F(SFramePacketBufferTest, ClearToIsInclusive) {
Insert(100, kStart, kEnd, /*timestamp=*/1000);
// Frame was assembled, buffer is empty. Insert new incomplete frame.
Insert(101, kStart, kNotEnd, /*timestamp=*/2000);
Insert(102, kNotStart, kNotEnd, /*timestamp=*/2000);
buffer_.ClearTo(102);
// Both 101 and 102 should be cleared. A new frame after works.
auto result = Insert(103, kStart, kEnd, /*timestamp=*/3000);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->SequenceNumber(), 103);
}
TEST_F(SFramePacketBufferTest, ClearToBeforeFirstPacketIsNoop) {
Insert(100, kStart, kNotEnd);
// ClearTo a seq_num before the first received packet should be a no-op.
buffer_.ClearTo(50);
// Packet 100 is still there -- completing the frame proves it.
auto result = Insert(101, kNotStart, kEnd);
ASSERT_EQ(result.packets.size(), 2u);
}
TEST_F(SFramePacketBufferTest, SeqNumWraparound) {
uint16_t seq = 0xFFFE;
EXPECT_TRUE(Insert(seq, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(seq + 1, kNotStart, kNotEnd).packets.empty());
// seq + 2 wraps to 0.
auto result = Insert(static_cast<uint16_t>(seq + 2), kNotStart, kEnd);
ASSERT_EQ(result.packets.size(), 3u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 0xFFFE);
EXPECT_EQ(result.packets[1]->SequenceNumber(), 0xFFFF);
EXPECT_EQ(result.packets[2]->SequenceNumber(), 0x0000);
}
TEST_F(SFramePacketBufferTest, SinglePacketAtWraparound) {
auto result = Insert(0xFFFF, kStart, kEnd);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->SequenceNumber(), 0xFFFF);
}
TEST_F(SFramePacketBufferTest, ReorderedPacketUnwrapsToNegative) {
// First packet at seq 2 unwraps to 2. Then a reordered packet at raw seq
// 65535 unwraps to -1 (backward by 3 is shorter than forward by 65533).
// ToIdx must handle the negative unwrapped value correctly.
EXPECT_TRUE(Insert(2, kNotStart, kEnd).packets.empty());
auto result = Insert(0xFFFF, kStart, kNotEnd);
EXPECT_TRUE(result.packets.empty());
// Both packets are buffered — insert the middle to complete the frame.
auto r2 = Insert(0, kNotStart, kNotEnd);
EXPECT_TRUE(r2.packets.empty());
auto r3 = Insert(1, kNotStart, kNotEnd);
ASSERT_EQ(r3.packets.size(), 4u);
EXPECT_EQ(r3.packets[0]->SequenceNumber(), 0xFFFF);
EXPECT_EQ(r3.packets[3]->SequenceNumber(), 2);
}
TEST_F(SFramePacketBufferTest, CollisionClearsBuffer) {
// seq 0 and seq 16 both map to slot 0 in a size-16 buffer.
EXPECT_TRUE(Insert(0, kStart, kNotEnd, /*timestamp=*/100).packets.empty());
// Insert 16 which collides with slot 0. Should clear the buffer.
auto result = Insert(16, kStart, kNotEnd, /*timestamp=*/200);
EXPECT_TRUE(BufferCleared(result.status));
}
TEST_F(SFramePacketBufferTest, BufferClearedOnCollision) {
// Create a small buffer so collisions happen quickly.
SFramePacketBuffer small_buffer(4);
// Fill all 4 slots with non-colliding packets.
small_buffer.InsertPacket(MakePacket(0, kStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(1, kNotStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(2, kNotStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(3, kNotStart, kNotEnd, 100));
// Insert packet that collides (seq 4 maps to slot 0 in size-4 buffer).
// Should clear and re-insert.
auto result = small_buffer.InsertPacket(MakePacket(4, kStart, kEnd, 200));
// Buffer cleared — caller should request a keyframe.
ASSERT_TRUE(std::holds_alternative<SFramePacketBuffer::InsertResult>(result));
EXPECT_EQ(std::get<SFramePacketBuffer::InsertResult>(result),
SFramePacketBuffer::InsertResult::kBufferCleared);
}
TEST_F(SFramePacketBufferTest, VeryOldPacketDropped) {
// Insert a packet to establish the window.
Insert(1000, kStart, kEnd);
// Try to insert a packet far before the window. With buffer_size=16,
// any packet >= 16 positions back is dropped.
auto result = Insert(900, kStart, kEnd);
// Packet is dropped (too old), no assembly.
EXPECT_TRUE(result.packets.empty());
}
TEST_F(SFramePacketBufferTest, TimestampPreservedInResult) {
auto result = Insert(100, kStart, kEnd, /*timestamp=*/42000);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->Timestamp(), 42000u);
}
TEST_F(SFramePacketBufferTest, PayloadTypePreservedInResult) {
auto result = Insert(100, kStart, kEnd, 1000, /*payload_type=*/111);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->PayloadType(), 111);
}
TEST_F(SFramePacketBufferTest, InterleavedFramesAssembleIndependently) {
// Frame A: seq 10-11 (ts 1000), Frame B: seq 12-13 (ts 2000).
// Insert them interleaved: A-start, B-start, A-end, B-end.
EXPECT_TRUE(Insert(10, kStart, kNotEnd, /*timestamp=*/1000).packets.empty());
EXPECT_TRUE(Insert(12, kStart, kNotEnd, /*timestamp=*/2000).packets.empty());
// Completing frame A.
auto r1 = Insert(11, kNotStart, kEnd, /*timestamp=*/1000);
ASSERT_EQ(r1.packets.size(), 2u);
EXPECT_EQ(r1.packets[0]->SequenceNumber(), 10);
// Completing frame B.
auto r2 = Insert(13, kNotStart, kEnd, /*timestamp=*/2000);
ASSERT_EQ(r2.packets.size(), 2u);
EXPECT_EQ(r2.packets[0]->SequenceNumber(), 12);
}
TEST_F(SFramePacketBufferTest, FrameAfterDroppedFrameStillAssembles) {
// Frame 1 with timestamp mismatch -> dropped.
Insert(100, kStart, kNotEnd, /*timestamp=*/1000);
Insert(101, kNotStart, kEnd, /*timestamp=*/9999); // mismatch
// Frame 2 is valid.
auto result = Insert(102, kStart, kEnd, /*timestamp=*/2000);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->SequenceNumber(), 102);
}
TEST_F(SFramePacketBufferTest, ClearToPreventsLatePacketFromMovingWindow) {
// Insert and assemble frame at seq 100.
auto r1 = Insert(100, kStart, kEnd, /*timestamp=*/1000);
ASSERT_FALSE(r1.packets.empty());
// Advance the window past seq 105.
Insert(101, kStart, kNotEnd, /*timestamp=*/2000);
Insert(102, kNotStart, kNotEnd, /*timestamp=*/2000);
Insert(103, kNotStart, kNotEnd, /*timestamp=*/2000);
Insert(104, kNotStart, kNotEnd, /*timestamp=*/2000);
Insert(105, kNotStart, kNotEnd, /*timestamp=*/2000);
buffer_.ClearTo(105);
// A late packet from before the cleared point should be dropped.
auto late = Insert(103, kStart, kEnd, /*timestamp=*/2000);
EXPECT_TRUE(late.packets.empty());
}
TEST_F(SFramePacketBufferTest, ClearToAllowsNewPacketsAfterClearedPoint) {
Insert(100, kStart, kEnd, /*timestamp=*/1000);
buffer_.ClearTo(100);
// Packet after the cleared point should work fine.
auto result = Insert(110, kStart, kEnd, /*timestamp=*/2000);
ASSERT_FALSE(result.packets.empty());
EXPECT_EQ(result.packets[0]->SequenceNumber(), 110);
}
TEST_F(SFramePacketBufferTest, ClearToDoesNotWipeNewerPacket) {
// Insert two frames in non-adjacent slots.
auto r1 = Insert(0, kStart, kEnd, /*timestamp=*/1000);
ASSERT_FALSE(r1.packets.empty());
// Packet in a higher slot (2 * kBufferSize away to avoid aliasing).
Insert(2 * kBufferSize, kStart, kNotEnd, /*timestamp=*/3000);
// ClearTo the first frame — must not affect the second.
buffer_.ClearTo(0);
// Completing the second frame still works.
auto r2 = Insert(2 * kBufferSize + 1, kNotStart, kEnd, /*timestamp=*/3000);
ASSERT_EQ(r2.packets.size(), 2u);
EXPECT_EQ(r2.packets[0]->SequenceNumber(), 2 * kBufferSize);
}
TEST_F(SFramePacketBufferTest, FreedSlotsReuseFullBuffer) {
// Use a small buffer where we can fill it entirely.
SFramePacketBuffer small_buffer(4);
// Frame 1: 3 packets, assembles and frees slots.
small_buffer.InsertPacket(MakePacket(10, kStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(11, kNotStart, kNotEnd, 100));
auto r1 = InsertInto(small_buffer, MakePacket(12, kNotStart, kEnd, 100));
ASSERT_EQ(r1.packets.size(), 3u);
// Frame 2: fills all 4 slots using the freed space.
small_buffer.InsertPacket(MakePacket(13, kStart, kNotEnd, 200));
small_buffer.InsertPacket(MakePacket(14, kNotStart, kNotEnd, 200));
small_buffer.InsertPacket(MakePacket(15, kNotStart, kNotEnd, 200));
auto r2 = InsertInto(small_buffer, MakePacket(16, kNotStart, kEnd, 200));
ASSERT_EQ(r2.packets.size(), 4u);
EXPECT_FALSE(BufferCleared(r2.status));
}
TEST_F(SFramePacketBufferTest, FramesAfterClearToWithReordering) {
// Assemble and clear past a frame.
Insert(100, kStart, kEnd, /*timestamp=*/1000);
Insert(101, kStart, kEnd, /*timestamp=*/2000);
buffer_.ClearTo(101);
// Insert a later frame first (out of order).
auto r1 = Insert(110, kStart, kEnd, /*timestamp=*/4000);
ASSERT_FALSE(r1.packets.empty());
EXPECT_EQ(r1.packets[0]->SequenceNumber(), 110);
// Insert a frame between the cleared point and the later frame.
auto r2 = Insert(105, kStart, kEnd, /*timestamp=*/3000);
ASSERT_FALSE(r2.packets.empty());
EXPECT_EQ(r2.packets[0]->SequenceNumber(), 105);
}
TEST_F(SFramePacketBufferTest, TwoStartsWithoutEnd) {
// S(101) followed by S(102) — two starts, no complete frame.
EXPECT_TRUE(Insert(101, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(102, kStart, kNotEnd, /*timestamp=*/2000).packets.empty());
// Adding end for second "frame" (102-103) should assemble.
auto result = Insert(103, kNotStart, kEnd, /*timestamp=*/2000);
ASSERT_EQ(result.packets.size(), 2u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 102);
EXPECT_EQ(result.packets[1]->SequenceNumber(), 103);
}
TEST_F(SFramePacketBufferTest, TwoEndsWithoutStart) {
// Two E-bit packets with no preceding S-bit — no frame assembles.
EXPECT_TRUE(Insert(100, kNotStart, kEnd).packets.empty());
EXPECT_TRUE(Insert(101, kNotStart, kEnd, /*timestamp=*/2000).packets.empty());
}
TEST_F(SFramePacketBufferTest, EFollowedByE) {
// S at 100, E at 101, then another E at 102 with different timestamp.
auto r1 = Insert(100, kStart, kNotEnd, /*timestamp=*/1000);
EXPECT_TRUE(r1.packets.empty());
auto r2 = Insert(101, kNotStart, kEnd, /*timestamp=*/1000);
ASSERT_EQ(r2.packets.size(), 2u);
// Orphan E at 102 — no start found.
auto r3 = Insert(102, kNotStart, kEnd, /*timestamp=*/2000);
EXPECT_TRUE(r3.packets.empty());
}
TEST_F(SFramePacketBufferTest, TBitMismatchInMiddlePacketDropsFrame) {
// 3-packet frame where the middle packet has a mismatched T-bit.
EXPECT_TRUE(
Insert(100, kStart, kNotEnd, 1000, 96, SframeEncryptionLevel::kFrame)
.packets.empty());
EXPECT_TRUE(
Insert(101, kNotStart, kNotEnd, 1000, 96, SframeEncryptionLevel::kPacket)
.packets.empty());
auto result =
Insert(102, kNotStart, kEnd, 1000, 96, SframeEncryptionLevel::kFrame);
EXPECT_TRUE(result.packets.empty());
// All slots must be freed — re-inserting a fresh frame at same seqs works.
auto r2 = Insert(100, kStart, kEnd, /*timestamp=*/5000);
ASSERT_EQ(r2.packets.size(), 1u);
}
TEST_F(SFramePacketBufferTest, FrameWrapsAroundRingBuffer) {
// With buffer_size=16, place S near the end and E past the wrap.
// Seq 14 → slot 14, seq 15 → slot 15, seq 16 → slot 0.
EXPECT_TRUE(Insert(14, kStart, kNotEnd).packets.empty());
EXPECT_TRUE(Insert(15, kNotStart, kNotEnd).packets.empty());
auto result = Insert(16, kNotStart, kEnd);
ASSERT_EQ(result.packets.size(), 3u);
EXPECT_EQ(result.packets[0]->SequenceNumber(), 14);
EXPECT_EQ(result.packets[2]->SequenceNumber(), 16);
}
TEST_F(SFramePacketBufferTest, CollisionDuringMultiPacketFrameClearsBuffer) {
// Start a multi-packet frame.
EXPECT_TRUE(Insert(0, kStart, kNotEnd, /*timestamp=*/100).packets.empty());
EXPECT_TRUE(Insert(1, kNotStart, kNotEnd, /*timestamp=*/100).packets.empty());
// Insert a packet that collides (seq 16 → slot 0). Buffer clears.
auto result = Insert(16, kStart, kNotEnd, /*timestamp=*/200);
EXPECT_TRUE(BufferCleared(result.status));
}
TEST_F(SFramePacketBufferTest, BufferFullBeforeEndPacket) {
// Small buffer: S and middle packets fill it, E arrives too late.
SFramePacketBuffer small_buffer(4);
small_buffer.InsertPacket(MakePacket(0, kStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(1, kNotStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(2, kNotStart, kNotEnd, 100));
small_buffer.InsertPacket(MakePacket(3, kNotStart, kNotEnd, 100));
// Packet 4 (the end) collides with slot 0; buffer clears.
auto result = InsertInto(small_buffer, MakePacket(4, kNotStart, kEnd, 100));
// Buffer cleared — the frame is lost.
EXPECT_TRUE(BufferCleared(result.status));
// Only packet 4 remains; it has no S-bit so no frame assembles.
EXPECT_TRUE(result.packets.empty());
}
} // namespace
} // namespace webrtc