#pragma once #include #include #include #include #include #include #include #include namespace sc { // Minimal RTP header (RFC 3550) without extensions. struct RtpHeader { uint8_t version = 2; bool padding = false; bool extension = false; uint8_t csrc_count = 0; bool marker = false; uint8_t payload_type = 96; // dynamic uint16_t sequence_number = 0; uint32_t timestamp = 0; uint32_t ssrc = 0; bool serialize(std::span out) const noexcept; static std::optional parse(std::span in) noexcept; }; struct RtpPacket { RtpHeader header; std::vector payload; std::vector serialize() const; static std::optional parse(std::span in) noexcept; }; // Reorders RTP packets by sequence number before depacketization so that a // reordering link (Wi-Fi) does not read as loss. Delivery stays in order; // only aged-out or overflowing buffers release out of order, which the // downstream gap detection still handles for genuine loss. class RtpJitterBuffer { public: explicit RtpJitterBuffer(std::size_t max_depth = 16, std::chrono::milliseconds max_delay = std::chrono::milliseconds{60}); // Insert one packet and return the packets now ready for in-order // delivery. In-order streams release immediately (zero added latency); // a straggler older than the next expected sequence is discarded. std::vector push(RtpPacket packet); // Discard everything still buffered. void clear(); private: std::size_t max_depth_; std::chrono::milliseconds max_delay_; std::mutex mutex_; std::map> buffer_; std::optional next_expected_; }; } // namespace sc