Expand description
RTP fixed header + CSRC list + generic header extension — RFC 3550 §5.1 / §5.3.1, spec-complete (not just a happy-path subset).
This crate implements exactly the wire structures described in the curated
spec transcription at rtp-packet/docs/rtp-header.md (fetched directly
from RFC 3550) — cite that
file, not this doc comment, as the field-semantics oracle.
RtpPacket— the §5.1 fixed header (version/padding/extension bit/ CSRC-count/marker/payload-type/sequence-number/timestamp/SSRC), the CSRC identifier list (0–15 entries), the optional §5.3.1 header extension, an optional trailing padding region, and the payload.HeaderExtension— the §5.3.1 generic header extension: a 16-bit profile-specific identifier + opaque profile-specific data.
version/P/X/CC are never stored as independent fields that could
disagree with the typed data: version is fixed at 2 by the spec (checked
on parse, always written on serialize), and P/X/CC are derived from
padding.is_some() / extension.is_some() / csrc.len() respectively —
see RtpPacket’s doc for the reasoning.
Depends only on broadcast-common. #![no_std] (+ alloc) when the
std feature is disabled.
The optional rfc8285 feature adds rfc8285, a decoder for RFC
8285’s one-byte/two-byte
multiplexed extension elements that a profile may pack into
HeaderExtension::data — see rtp-packet/docs/rfc8285_header_ext.md
for the curated transcription. It is additive and off by default: most
RTP consumers only need the RFC 3550 fixed header.
§Examples
Build a simple packet (no padding/CSRC/extension) and round-trip it:
use broadcast_common::{Parse, Serialize};
use rtp_packet::RtpPacket;
let pkt = RtpPacket {
marker: true,
payload_type: 96,
sequence_number: 1,
timestamp: 3600,
ssrc: 0x1234_5678,
csrc: vec![],
extension: None,
padding: None,
payload: &[0xDE, 0xAD, 0xBE, 0xEF],
};
let mut bytes = vec![0u8; pkt.serialized_len()];
pkt.serialize_into(&mut bytes).unwrap();
assert_eq!(RtpPacket::parse(&bytes).unwrap(), pkt);§Runnable examples
Run with cargo run -p rtp-packet --example <name>.
§build_packet
//! Build an RTP packet from typed fields (fixed header + CSRC list + header
//! extension) and serialize it to wire bytes — RFC 3550 §5.1 / §5.3.1.
//!
//! Run with `cargo run -p rtp-packet --example build_packet`.
use broadcast_common::Serialize;
use rtp_packet::{HeaderExtension, RtpPacket};
fn main() {
let extension_data = [0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02];
let pkt = RtpPacket {
marker: true,
payload_type: 96, // dynamic payload type, e.g. H.264 (RFC 6184)
sequence_number: 42,
timestamp: 90_000,
ssrc: 0xCAFEBABE,
csrc: vec![0x1111_1111, 0x2222_2222],
extension: Some(HeaderExtension {
profile_id: 0xBEDE, // example one-byte header extension profile id
data: &extension_data,
}),
padding: None,
payload: b"example RTP payload bytes",
};
let mut bytes = vec![0u8; pkt.serialized_len()];
pkt.serialize_into(&mut bytes).expect("serialize");
println!("serialized {} bytes:", bytes.len());
println!(
"{}",
bytes
.iter()
.map(|b| format!("{b:02x}"))
.collect::<Vec<_>>()
.join(" ")
);
println!(
"V={} P={} X={} CC={}",
bytes[0] >> 6,
(bytes[0] >> 5) & 1,
(bytes[0] >> 4) & 1,
bytes[0] & 0x0F
);
}§parse_packet
//! Parse the committed real-capture fixture (`tests/fixtures/rtp_simple.bin`)
//! and print its decoded RTP fields, then round-trip it back to bytes and
//! confirm the output is byte-identical — RFC 3550 §5.1.
//!
//! Run with `cargo run -p rtp-packet --example parse_packet`.
use broadcast_common::{Parse, Serialize};
use rtp_packet::RtpPacket;
fn main() {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/rtp_simple.bin");
let bytes = std::fs::read(path).expect("fixture must exist");
let pkt = RtpPacket::parse(&bytes).expect("parse RTP fixed header");
println!("marker: {}", pkt.marker);
println!("payload_type: {}", pkt.payload_type);
println!("sequence_number: {}", pkt.sequence_number);
println!("timestamp: {}", pkt.timestamp);
println!("ssrc: {:#010x}", pkt.ssrc);
println!("csrc count: {}", pkt.csrc_count());
println!("extension: {}", pkt.extension.is_some());
println!("padding: {}", pkt.padding.is_some());
println!("payload len: {}", pkt.payload.len());
let mut out = vec![0u8; pkt.serialized_len()];
pkt.serialize_into(&mut out).expect("serialize");
assert_eq!(out, bytes, "byte-identical round trip");
println!("round trip byte-identical: OK ({} bytes)", out.len());
}§rfc8285_extensions (requires --features rfc8285)
//! Build an RTP packet carrying RFC 8285 one-byte-form multiplexed header-
//! extension elements, serialize it, then parse it back and decode the
//! extension elements — RFC 8285 §4.1.2 / §4.2.
//!
//! Run with `cargo run -p rtp-packet --example rfc8285_extensions --features rfc8285`.
use broadcast_common::{Parse, Serialize};
use rtp_packet::rfc8285::{
ExtensionElements, OneByteElement, OneByteElements, OneByteId, parse_extensions,
};
use rtp_packet::{HeaderExtension, RtpPacket};
fn main() {
// Two named extension elements: a 1-byte "audio level" (id=1) and a
// 3-byte "MID" identifier (id=2) — the kind of thing WebRTC streams
// multiplex via RFC 8285 in practice.
let elements = OneByteElements(vec![
OneByteElement {
id: OneByteId::new(1).expect("1 is in the valid 1..=14 range"),
data: &[0x2A], // audio level, one byte
},
OneByteElement {
id: OneByteId::new(2).expect("2 is in the valid 1..=14 range"),
data: b"mid", // 3-byte MID value
},
]);
// Serialize the elements to the padded byte sequence that becomes the
// RFC 3550 §5.3.1 HeaderExtension's `data`.
let mut ext_data = vec![0u8; elements.serialized_len()];
elements
.serialize_into(&mut ext_data)
.expect("serialize extension elements");
println!(
"encoded {} extension-data bytes (word-aligned): {}",
ext_data.len(),
ext_data
.iter()
.map(|b| format!("{b:02x}"))
.collect::<Vec<_>>()
.join(" ")
);
let pkt = RtpPacket {
marker: true,
payload_type: 96,
sequence_number: 1,
timestamp: 3600,
ssrc: 0x1234_5678,
csrc: vec![],
extension: Some(HeaderExtension {
profile_id: rtp_packet::rfc8285::ONE_BYTE_PROFILE_ID,
data: &ext_data,
}),
padding: None,
payload: b"example payload",
};
let mut wire = vec![0u8; pkt.serialized_len()];
pkt.serialize_into(&mut wire).expect("serialize RTP packet");
println!("serialized RTP packet: {} bytes", wire.len());
// Parse it back and decode the RFC 8285 elements from the extension.
let reparsed = RtpPacket::parse(&wire).expect("parse RTP packet");
let ext = reparsed.extension.expect("X=1");
let decoded = parse_extensions(&ext).expect("recognized RFC 8285 profile_id");
let ExtensionElements::OneByte(one_byte) = decoded else {
panic!("expected the one-byte form (profile_id 0xBEDE)");
};
println!("decoded {} extension elements:", one_byte.elements().len());
for e in one_byte.elements() {
println!(" id={:>2} data={:02x?}", e.id.get(), e.data);
}
// Re-serializing the decoded elements reproduces the exact original
// extension data byte-for-byte, including the zero-padding tail.
let mut re_ext_data = vec![0u8; one_byte.serialized_len()];
one_byte
.serialize_into(&mut re_ext_data)
.expect("serialize decoded elements");
assert_eq!(re_ext_data, ext_data);
println!(
"round trip byte-identical: OK ({} bytes)",
re_ext_data.len()
);
}Modules§
- rfc8285
rfc8285 - RFC 8285 one-byte/two-byte RTP header-extension element multiplexing.
Structs§
- Header
Extension - The RTP generic header extension (§5.3.1): a 16-bit profile-specific identifier + opaque profile-specific data.
- RtpPacket
- A parsed (or to-be-serialized) RTP packet: the §5.1 fixed header, the CSRC list, the optional §5.3.1 header extension, an optional padding region, and the payload.
Enums§
- Error
- An RTP header parse / serialize error.
Constants§
- FIXED_
HEADER_ LEN - Length of the fixed header (12 bytes: byte0 + byte1 + seq(2) + ts(4) + ssrc(4)) before any CSRC identifiers, per the §5.1 bit diagram.
- MAX_
CSRC_ COUNT - Maximum CSRC count — the
CCfield is 4 bits (§5.1). - MAX_
PADDING_ COUNT - Maximum padding-octet count — the trailing count byte is 8 bits (§5.1: “the last octet of the padding contains a count of how many padding octets should be ignored, including itself”).
- MAX_
PAYLOAD_ TYPE - Maximum
payload typevalue —PTis a 7-bit field (§5.1). - RTP_
VERSION - RTP version — “the version defined by this specification is two (2)”
(docs/rtp-header.md §5.1,
version (V), 2 bits).
Type Aliases§
- Result
- Result alias for
rtp-packetparsing/serialization.