| /* | |
| * 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. | |
| */ | |
| #ifndef MODULES_VIDEO_CODING_SFRAME_PACKET_BUFFER_H_ | |
| #define MODULES_VIDEO_CODING_SFRAME_PACKET_BUFFER_H_ | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #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 "rtc_base/numerics/sequence_number_unwrapper.h" | |
| namespace webrtc { | |
| // Buffers incoming SFrame RTP packets and validates complete S->E runs. | |
| // | |
| // Implements the receiver-side validation from | |
| // draft-ietf-avtcore-rtp-sframe section 5.2: | |
| // 1. Add each packet to the set. | |
| // 2. Find the smallest consecutive S=1->E=1 run. | |
| // 3. Validate T-bits are identical, PTs are identical. | |
| // 4. Return the validated packets + metadata. | |
| // | |
| // Uses a circular buffer indexed by (seq_num % buffer_size), similar to | |
| // PacketBuffer, to avoid per-packet heap allocations. | |
| // | |
| // Descriptor stripping, payload assembly, and decryption are handled | |
| // downstream by the packet transformer. | |
| // This buffer sits before SFrame decryption and before the codec depacketizer. | |
| class SFramePacketBuffer { | |
| public: | |
| // Outcome of InsertPacket() when no frame was assembled. | |
| enum class InsertResult { | |
| // Packet was buffered but no complete frame yet. | |
| kNoFrame, | |
| // Buffer was full and had to be cleared. Caller should request a | |
| // keyframe. Any partially assembled state is discarded. | |
| kBufferCleared, | |
| }; | |
| struct AssembledFrame { | |
| // Encryption granularity from the SFrame descriptor T bit. | |
| SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame; | |
| // The RTP packets forming the validated S->E run, in sequence-number | |
| // order. | |
| std::vector<std::unique_ptr<RtpPacketReceived>> packets; | |
| }; | |
| explicit SFramePacketBuffer(size_t buffer_size = 2048); | |
| ~SFramePacketBuffer(); | |
| // Insert an SFrame-depacketized RTP packet (descriptor already parsed and | |
| // stripped). If this packet completes an S->E run, returns an | |
| // AssembledFrame. Otherwise returns an InsertResult status. | |
| std::variant<AssembledFrame, InsertResult> InsertPacket( | |
| std::unique_ptr<SframeRtpPacketReceived> packet); | |
| // Discard all packets up to and including `seq_num`. | |
| void ClearTo(uint16_t seq_num); | |
| // Clear all buffered state. | |
| void Clear(); | |
| private: | |
| // Check whether the newly inserted packet at `seq_num` completes an S->E | |
| // run. Walks backward from `seq_num` to find S=1, then forward to find E=1. | |
| std::optional<AssembledFrame> FindFrame(int64_t seq_num); | |
| // Walk backward from `seq_num` to find the S=1 packet (start of frame). | |
| std::optional<int64_t> FindFrameStart(int64_t seq_num); | |
| // Walk forward from `seq_num` to find the E=1 packet (end of frame). | |
| std::optional<int64_t> FindFrameEnd(int64_t seq_num, int64_t start); | |
| // Validate the S->E run, extract packets, and remove them from the buffer. | |
| std::optional<AssembledFrame> AssembleFrame(int64_t start, int64_t end); | |
| // Clear all packets in [start, end] on validation failure and log `reason`. | |
| void DropFrame(absl::string_view reason, | |
| uint16_t bad_seq, | |
| int64_t start, | |
| int64_t end); | |
| // Update first_seq_num_ to track the lowest sequence number in the window. | |
| // Returns false if the packet is too old to fit in the buffer. | |
| bool UpdateWindowStart(int64_t seq_num); | |
| // Outcome of ResolveSlot: found an available slot, detected a duplicate | |
| // packet, or ran out of buffer space. | |
| enum class SlotOutcome { kOk, kDuplicate, kFull }; | |
| // Resolve the buffer slot for `seq_num`. Returns the outcome and the | |
| // slot index (only valid when kOk). Does NOT clear the buffer on | |
| // overflow — the caller decides how to handle that. | |
| std::pair<SlotOutcome, size_t> ResolveSlot(int64_t seq_num); | |
| size_t ToIdx(int64_t seq_num) const { | |
| return static_cast<size_t>(seq_num) % buffer_.size(); | |
| } | |
| // Circular buffer indexed by ToIdx(unwrapped_seq_num). | |
| std::vector<std::unique_ptr<SframeRtpPacketReceived>> buffer_; | |
| std::optional<int64_t> first_seq_num_; | |
| // True after ClearTo() has been called. Prevents UpdateWindowStart from | |
| // moving first_seq_num_ backward past the cleared point. | |
| bool is_cleared_to_first_seq_num_ = false; | |
| // Unwraps uint16_t RTP sequence numbers to monotonic int64_t values. | |
| SeqNumUnwrapper<uint16_t> seq_num_unwrapper_; | |
| }; | |
| } // namespace webrtc | |
| #endif // MODULES_VIDEO_CODING_SFRAME_PACKET_BUFFER_H_ |