| /* | |
| * Copyright 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/rtp_rtcp/source/rtp_depacketizer_sframe.h" | |
| #include <bitset> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #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/gmock.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| namespace { | |
| using ::testing::ElementsAreArray; | |
| // Compose a wire-format SFrame descriptor byte from S/E/T flags using the | |
| // bit indices declared on `SframeDescriptor`. | |
| uint8_t MakeDescriptorByte(bool s, bool e, bool t) { | |
| std::bitset<SframeDescriptor::kNumBits> bits; | |
| bits.set(SframeDescriptor::kSBit, s); | |
| bits.set(SframeDescriptor::kEBit, e); | |
| bits.set(SframeDescriptor::kTBit, t); | |
| return static_cast<uint8_t>(bits.to_ulong()); | |
| } | |
| RtpPacketReceived MakeReceivedPacket(uint8_t descriptor, | |
| std::vector<uint8_t> inner_payload) { | |
| RtpPacketReceived packet; | |
| packet.SetSequenceNumber(0x4242); | |
| packet.SetTimestamp(0xDEADBEEF); | |
| packet.SetPayloadType(96); | |
| std::vector<uint8_t> payload; | |
| payload.reserve(1 + inner_payload.size()); | |
| payload.push_back(descriptor); | |
| payload.insert(payload.end(), inner_payload.begin(), inner_payload.end()); | |
| packet.SetPayload(payload); | |
| return packet; | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, FailsOnEmptyPayload) { | |
| RtpPacketReceived packet; | |
| packet.SetSequenceNumber(1); | |
| packet.SetTimestamp(2); | |
| packet.SetPayloadType(96); | |
| packet.SetPayload({}); | |
| EXPECT_FALSE(ParseSframeRtpPacketOrError(packet).ok()); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, ParsesTBitAsPacketEncryptionLevel) { | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/true), {0xAA}); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_TRUE(parsed->descriptor().start); | |
| EXPECT_TRUE(parsed->descriptor().end); | |
| EXPECT_EQ(parsed->descriptor().encryption_level, | |
| SframeEncryptionLevel::kPacket); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, StripsDescriptorByteFromPayload) { | |
| const std::vector<uint8_t> inner = {0x01, 0x02, 0x03, 0x04, 0x05}; | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), inner); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_THAT(parsed->packet().payload(), ElementsAreArray(inner)); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, AcceptsDescriptorOnlyPayload) { | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), {}); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_EQ(parsed->packet().payload().size(), 0u); | |
| EXPECT_TRUE(parsed->descriptor().start); | |
| EXPECT_TRUE(parsed->descriptor().end); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, IgnoresReservedBits) { | |
| // Set all five reserved low bits in addition to S=1, E=1, T=0. | |
| const uint8_t descriptor = | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false) | 0x1F; | |
| RtpPacketReceived packet = MakeReceivedPacket(descriptor, {0xFF}); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_TRUE(parsed->descriptor().start); | |
| EXPECT_TRUE(parsed->descriptor().end); | |
| EXPECT_EQ(parsed->descriptor().encryption_level, | |
| SframeEncryptionLevel::kFrame); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, PreservesRtpHeaderFields) { | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), {0xAB, 0xCD}); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_EQ(parsed->SequenceNumber(), 0x4242); | |
| EXPECT_EQ(parsed->Timestamp(), 0xDEADBEEFu); | |
| EXPECT_EQ(parsed->PayloadType(), 96); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, ParsesAllEightSETCombinations) { | |
| // Exhaustively walk the 2^3 = 8 (S, E, T) descriptor combinations. | |
| for (uint8_t bits = 0; bits < 8; ++bits) { | |
| const bool expect_s = (bits & 0b100) != 0; | |
| const bool expect_e = (bits & 0b010) != 0; | |
| const bool expect_t = (bits & 0b001) != 0; | |
| const uint8_t descriptor = MakeDescriptorByte(expect_s, expect_e, expect_t); | |
| SCOPED_TRACE(static_cast<int>(descriptor)); | |
| RtpPacketReceived packet = MakeReceivedPacket(descriptor, {0x55}); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_EQ(parsed->descriptor().start, expect_s); | |
| EXPECT_EQ(parsed->descriptor().end, expect_e); | |
| EXPECT_EQ(parsed->descriptor().encryption_level, | |
| expect_t ? SframeEncryptionLevel::kPacket | |
| : SframeEncryptionLevel::kFrame); | |
| } | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, PreservesLargePayloadBytes) { | |
| std::vector<uint8_t> inner_payload(1000); | |
| for (size_t i = 0; i < inner_payload.size(); ++i) { | |
| inner_payload[i] = static_cast<uint8_t>((i * 17) & 0xFF); | |
| } | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), inner_payload); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_THAT(parsed->packet().payload(), ElementsAreArray(inner_payload)); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, PreservesMarkerBit) { | |
| for (bool marker : {false, true}) { | |
| SCOPED_TRACE(marker); | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), {0xAA}); | |
| packet.SetMarker(marker); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_EQ(parsed->packet().Marker(), marker); | |
| } | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, PreservesSsrc) { | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), {0xAA}); | |
| packet.SetSsrc(0xCAFEBABE); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_EQ(parsed->packet().Ssrc(), 0xCAFEBABEu); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, PreservesPadding) { | |
| // RTP packets with padding must not crash inside the depacketizer and the | |
| // padding must round-trip through the rebuilt wire buffer. | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), {0xAB, 0xCD}); | |
| ASSERT_TRUE(packet.SetPadding(4)); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| EXPECT_EQ(parsed->packet().padding_size(), 4u); | |
| EXPECT_THAT(parsed->packet().payload(), ElementsAreArray({0xAB, 0xCD})); | |
| } | |
| TEST(ParseSframeRtpPacketOrErrorTest, ReturnedPacketIsIndependentCopy) { | |
| // The returned object must own a copy independent of the source packet. | |
| RtpPacketReceived packet = MakeReceivedPacket( | |
| MakeDescriptorByte(/*s=*/true, /*e=*/true, /*t=*/false), {0x11, 0x22}); | |
| auto result = ParseSframeRtpPacketOrError(packet); | |
| ASSERT_TRUE(result.ok()); | |
| auto parsed = result.MoveValue(); | |
| const uint16_t original_seq = parsed->packet().SequenceNumber(); | |
| const uint32_t original_ts = parsed->packet().Timestamp(); | |
| packet.SetSequenceNumber(0xFFFF); | |
| packet.SetTimestamp(0xFFFFFFFF); | |
| packet.SetPayload(std::vector<uint8_t>{0x00}); | |
| EXPECT_EQ(parsed->packet().SequenceNumber(), original_seq); | |
| EXPECT_EQ(parsed->packet().Timestamp(), original_ts); | |
| EXPECT_THAT(parsed->packet().payload(), ElementsAreArray({0x11, 0x22})); | |
| } | |
| } // namespace | |
| } // namespace webrtc |