feat(network): implement Phase 4 RTP framing with FU-A fragmentation
Add the sc_network library: RFC 3550 RtpHeader/RtpPacket serialize and parse (the receiver tolerates CSRC lists, extension headers, and padding by skipping/stripping them) and RFC 6184 H.264 payloading via H264Packetizer/H264Depacketizer. The packetizer splits Annex-B frames into NAL units (3- and 4-byte start codes), emitting single-NAL packets or FU-A fragments within the configured MTU, with the marker bit closing each frame and randomized SSRC/sequence by default. The depacketizer reassembles access units with 3-byte start codes, so both start-code widths round-trip byte-exactly; frames damaged by sequence gaps or missing fragments are dropped until the Phase 7 loss-recovery work. test_rtp covers header and packet round-trips, malformed-input rejections, splitter behavior, FU-A chunk bounds, full packetize -> depacketize round-trip, gap dropping, marker-only frame separation, sequence wrap, and empty inputs. meson test 3/3, valgrind clean.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "screencast/network/rtp_packet.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace sc {
|
||||
|
||||
struct EncodedFrame;
|
||||
|
||||
// Smallest usable MTU: 12-byte RTP header + 2-byte FU-A prefix + 1 data byte.
|
||||
inline constexpr std::size_t kMinimumRtpMtu = 15;
|
||||
|
||||
struct RtpPacketizerConfig {
|
||||
// Zero values ask the packetizer to choose random values, as recommended
|
||||
// by RFC 3550 for SSRC and the initial sequence number.
|
||||
std::uint32_t ssrc = 0;
|
||||
std::uint16_t initial_sequence_number = 0;
|
||||
std::uint8_t payload_type = 96; // dynamic payload type range
|
||||
std::size_t mtu = 1200; // maximum size of a serialized RTP packet
|
||||
};
|
||||
|
||||
// Splits Annex-B encoded frames into RFC 6184 RTP packets using single NAL
|
||||
// unit packets and FU-A fragmentation. STAP-A is never emitted.
|
||||
class H264Packetizer {
|
||||
public:
|
||||
explicit H264Packetizer(const RtpPacketizerConfig& config = RtpPacketizerConfig{});
|
||||
|
||||
// One encoded frame becomes one or more packets sharing the frame's RTP
|
||||
// timestamp; the final packet carries the marker bit. Returns an empty
|
||||
// vector for frames without NAL units or for an unusable MTU.
|
||||
std::vector<RtpPacket> packetize(const EncodedFrame& frame);
|
||||
|
||||
private:
|
||||
RtpPacket next_packet(uint32_t timestamp);
|
||||
void append_fu_a_packets(std::span<const std::byte> nal, uint32_t timestamp, std::vector<RtpPacket>& out);
|
||||
|
||||
RtpPacketizerConfig config_;
|
||||
std::uint16_t next_sequence_number_ = 0;
|
||||
};
|
||||
|
||||
// Reassembles RFC 6184 packet streams (single NAL unit packets and FU-A)
|
||||
// into Annex-B access units. Packets must arrive in order; frames damaged by
|
||||
// sequence gaps or missing fragments are dropped silently.
|
||||
class H264Depacketizer {
|
||||
public:
|
||||
// Feed one packet. Returns the completed access unit (Annex-B with 3-byte
|
||||
// start codes) when the packet closes a frame, nullopt otherwise.
|
||||
std::optional<std::vector<std::byte>> depacketize(const RtpPacket& packet);
|
||||
|
||||
private:
|
||||
void drop_frame();
|
||||
|
||||
std::optional<std::uint16_t> last_sequence_number_;
|
||||
bool frame_started_ = false;
|
||||
bool frame_damaged_ = false;
|
||||
std::uint32_t frame_timestamp_ = 0;
|
||||
std::vector<std::byte> access_unit_;
|
||||
bool fu_active_ = false;
|
||||
std::vector<std::byte> fu_nal_;
|
||||
};
|
||||
|
||||
} // namespace sc
|
||||
Reference in New Issue
Block a user