Expand description
Multicast object-delivery wire formats: ALC / LCT / FLUTE / NORM.
This crate parses and serializes the binary headers used to deliver files and streams over IP multicast. Every format here is IETF RMT (Reliable Multicast Transport) — RFC 5651, RFC 5775, RFC 6726, RFC 5740. No broadcast-specific standard is implemented by this crate.
Several delivery systems are layered on top of these formats and are consumers of this crate rather than owners of it: DVB (DVB-IPTV and DVB-MABR / ETSI TS 103 769 file delivery), 3GPP (MBMS/eMBMS download delivery), and ATSC 3.0 (ROUTE, A/331 Annex A — written as a profile-and-delta on RFC 5651/5775/6726).
Renamed from dvb-flute at 0.4.0: the old name named one consumer of an
IETF standard rather than the standard itself, and read as a layering
error once non-DVB consumers needed to depend on it. All dvb-flute
versions are yanked; there is no shim.
Implements:
LctHeader— the Layered Coding Transport header (RFC 5651 §5). The fixed first word carriesV/C/PSI/S/O/H/A/B,HDR_LENand the Codepoint; theC,S,OandHflags then drive the byte-widths of the CCI, TSI and TOI fields (4*(C+1),4*S+2*H,4*O+2*Hbytes). The sharedHhalf-word feeds both TSI and TOI. Flag bits andHDR_LENare recomputed on serialize from the typed field lengths — there is no raw passthrough.HeaderExtension— the LCT/NORM header-extension chain (RFC 5651 §5.2): variable-length (HET0..=127, carriesHEL) and fixed-length (HET128..=255, one word) forms; withExtTime(EXT_TIME) and theLctExtTyperegistry (EXT_NOP/EXT_AUTH/EXT_TIME).AlcPacket— an Asynchronous Layered Coding packet (RFC 5775): LCT header + an opaque FEC Payload ID + the encoding-symbol payload, plusEXT_FTI(HET 64) and the Small-Block-SystematicFecPayloadId128.ExtFdt/ExtCenc— the FLUTE (RFC 6726) fixed-length LCT extensionsEXT_FDT(HET 192) andEXT_CENC(HET 193), plus the TOI = 0 FDT-Instance convention. The FDT Instance body is XML and is out of scope of this binary crate — it rides as the packet payload.NormCommonHeader+NormData/NormInfo/NormCmd/NormFeedback— the NORM (RFC 5740) common header and message types (NORM_INFO / NORM_DATA / NORM_CMD / NORM_NACK / NORM_ACK / NORM_REPORT).SourceBlockPartition— the FEC Building Block’s (RFC 5052 §9.1) scheme-agnostic Block Partitioning Algorithm: given a transport object’s Transfer-Length, Encoding-Symbol-Length and Maximum-Source-Block-Length, derive the number of source blocks and each block’s length in symbols. The common substratedvb-mabrandatsc3-routeboth need (issue #944) without hardcoding any FEC scheme’s FEC Payload ID or Scheme-specific OTI layout.
⚠ FEC Payload ID bit layouts are FEC-scheme dependent (RFC 5052 / the FEC
Scheme document) and are not defined by ALC/NORM themselves; this crate
exposes them as opaque byte slices (the caller supplies the length), with
FecPayloadId128 provided as one concrete illustrative layout.
All integer fields are big-endian. #![no_std] + alloc; depends only on
broadcast-common.
§Examples
Build an LCT header from typed fields (flag-driven CCI/TSI/TOI widths) and round-trip it:
use rmt_flute::{LctHeader, LCT_VERSION};
let cci = [0u8; 4]; // C = 0
let tsi = [0u8; 4]; // S = 1, H = 0
let hdr = LctHeader {
version: LCT_VERSION,
psi: 0,
close_session: false,
close_object: false,
codepoint: 0,
cci: &cci,
tsi: &tsi,
toi: &[],
extensions: vec![],
};
let mut buf = vec![0u8; hdr.serialized_len()];
hdr.serialize_into(&mut buf).unwrap();
let (re, used) = LctHeader::parse(&buf).unwrap();
assert_eq!(used, buf.len());
assert_eq!(re, hdr);§Runnable examples
Run with cargo run -p rmt-flute --example <name>.
§build_lct
/// Build a FLUTE/ALC packet from typed fields — an LCT header (TOI = 0) with an
/// EXT_FDT extension, a Small-Block-Systematic FEC Payload ID, and an XML FDT
/// Instance payload — then serialize it (recomputing all flags + HDR_LEN) and
/// dump the wire bytes.
///
/// ```sh
/// cargo run -p rmt-flute --example build_lct
/// ```
use rmt_flute::{
AlcPacket, ExtFdt, FLUTE_VERSION, FecPayloadId128, HET_EXT_FDT, LCT_VERSION, LctHeader,
};
fn main() {
// TOI = 0 (FDT Instance), so the TOI field is present and zero (O=0, H=1
// gives a 2-byte TOI; we use O=1 here for a clean 4-byte zero TOI).
let cci = [0u8, 0, 0, 1];
let tsi = [0u8, 0, 0, 0x2A]; // S=1, H=0 — non-zero TSI (required by ALC)
let toi = [0u8, 0, 0, 0]; // O=1, H=0 — TOI = 0 (FDT Instance)
// EXT_FDT (HET 192, fixed): FLUTE v2, FDT Instance ID 5.
let ext_fdt = ExtFdt {
version: FLUTE_VERSION,
instance_id: 5,
};
let mut fdt_scratch = [0u8; 3];
let ext = ext_fdt.to_extension(&mut fdt_scratch).unwrap();
let lct = LctHeader {
version: LCT_VERSION,
psi: 0,
close_session: false,
close_object: false,
codepoint: 0, // Compact No-Code FEC (Encoding ID 0)
cci: &cci,
tsi: &tsi,
toi: &toi,
extensions: vec![ext],
};
// FEC Payload ID (Small Block Systematic, fec_id 128/129).
let fpid = FecPayloadId128 {
source_block_number: 0,
source_block_length: 1,
encoding_symbol_id: 0,
};
let mut fpid_bytes = [0u8; 8];
fpid.serialize_into(&mut fpid_bytes).unwrap();
// A tiny (truncated) FDT Instance XML body as the packet payload. The XML
// itself is out of scope of this crate — it rides as opaque payload.
let xml = br#"<?xml version="1.0"?><FDT-Instance Expires="0"/>"#;
let pkt = AlcPacket::new(lct, &fpid_bytes, xml);
let mut bytes = vec![0u8; pkt.serialized_len()];
let n = pkt.serialize_into(&mut bytes).unwrap();
println!("ALC/FLUTE packet: {n} bytes");
println!("LCT version: {}", pkt.lct.version);
println!(
"flags C={} S={} O={} H={}",
pkt.lct.c_flag(),
pkt.lct.s_flag(),
pkt.lct.o_flag(),
pkt.lct.h_flag()
);
println!("HDR_LEN: {} words", pkt.lct.hdr_len());
println!("TOI bytes: {:02X?} (0 => FDT Instance)", pkt.lct.toi);
println!("EXT_FDT instance_id: {}", ext_fdt.instance_id);
println!("FEC Payload ID: {:02X?}", pkt.fec_payload_id);
println!("payload (XML, opaque): {} bytes", pkt.payload.len());
print!("wire bytes:");
for b in &bytes {
print!(" {b:02X}");
}
println!();
// Round-trip sanity (FEC Payload ID len = 8 for fec_id 128/129).
let re = AlcPacket::parse(&bytes, rmt_flute::FEC_PAYLOAD_ID_128_LEN).unwrap();
assert_eq!(re, pkt);
// The EXT_FDT extension decodes back.
let re_fdt = ExtFdt::parse(re.lct.extensions[0].content).unwrap();
assert_eq!(re.lct.extensions[0].het, HET_EXT_FDT);
assert_eq!(re_fdt, ext_fdt);
println!("round-trip: OK");
}§parse_flute
/// Read the committed FLUTE FDT-packet fixture, parse the ALC/LCT framing +
/// EXT_FDT extension, and report the decoded fields (the XML FDT Instance body
/// is left opaque — out of scope of this binary crate).
///
/// ```sh
/// cargo run -p rmt-flute --example parse_flute
/// ```
use std::fs;
use rmt_flute::{AlcPacket, ExtFdt, FEC_PAYLOAD_ID_128_LEN, FecPayloadId128, HET_EXT_FDT};
fn main() {
// Fixtures live in the workspace-shared `fixtures/rmt-flute/` directory,
// not under the crate. This example previously pointed at a
// `tests/fixtures/` path that has never existed, and its "nothing to do"
// fallback silently swallowed the failure — so it never actually ran.
// Missing fixture is now a hard error: an example that quietly does
// nothing is worse than one that fails.
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../fixtures/rmt-flute/flute_fdt.bin"
);
let data = fs::read(path)
.unwrap_or_else(|e| panic!("committed fixture {path} could not be read: {e}"));
// FLUTE default FEC is Compact No-Code (Encoding ID 0): a 16-bit Source
// Block Number + 16-bit Encoding Symbol ID = 4 bytes. But this fixture uses
// the Small-Block-Systematic 8-byte FEC Payload ID for a richer example.
let pkt = AlcPacket::parse(&data, FEC_PAYLOAD_ID_128_LEN).unwrap();
println!("ALC/FLUTE packet: {} bytes", data.len());
println!("LCT version: {}", pkt.lct.version);
println!(
"flags C={} S={} O={} H={} close_session={} close_object={}",
pkt.lct.c_flag(),
pkt.lct.s_flag(),
pkt.lct.o_flag(),
pkt.lct.h_flag(),
pkt.lct.close_session,
pkt.lct.close_object,
);
println!("HDR_LEN: {} words", pkt.lct.hdr_len());
println!("CCI: {:02X?}", pkt.lct.cci);
println!("TSI: {:02X?}", pkt.lct.tsi);
println!("TOI: {:02X?} (0 => FDT Instance)", pkt.lct.toi);
for ext in &pkt.lct.extensions {
if ext.het == HET_EXT_FDT {
let fdt = ExtFdt::parse(ext.content).unwrap();
println!(
"EXT_FDT: FLUTE v{} FDT Instance ID {}",
fdt.version, fdt.instance_id
);
} else {
println!("extension HET {} ({} bytes)", ext.het, ext.serialized_len());
}
}
let fpid = FecPayloadId128::parse(pkt.fec_payload_id).unwrap();
println!(
"FEC Payload ID: sbn={} sbl={} esi={}",
fpid.source_block_number, fpid.source_block_length, fpid.encoding_symbol_id
);
println!(
"FDT Instance XML payload ({} bytes, opaque): {}",
pkt.payload.len(),
core::str::from_utf8(pkt.payload).unwrap_or("<non-utf8>")
);
// Byte-exact round-trip.
let mut out = vec![0u8; pkt.serialized_len()];
let n = pkt.serialize_into(&mut out).unwrap();
assert_eq!(n, data.len());
assert_eq!(out, data, "serialize must be byte-identical to the fixture");
println!("byte-exact round-trip: OK");
}Structs§
- AlcPacket
- A parsed ALC packet (RFC 5775 §4.1): an
LctHeaderfollowed by an opaque FEC Payload ID and the encoding-symbol payload. - ExtCenc
- EXT_CENC — FDT Instance Content Encoding Header (RFC 6726 §3.4.3, HET = 193, fixed-length).
- ExtFdt
- EXT_FDT — FDT Instance Header (RFC 6726 §3.4.1, HET = 192, fixed-length).
- ExtTime
- A decoded EXT_TIME header extension (RFC 5651 §5.2.2, HET = 2).
- FecPayload
Id128 - FEC Payload ID for Small Block Systematic codes (
fec_id= 128/129), reproduced from RFC 5445 as an illustrative layout (RFC 5775 itself defines no FEC Payload ID format). 8 bytes: a 32-bitsource_block_number, a 16-bitsource_block_length, and a 16-bitencoding_symbol_id. - Header
Extension - One LCT/NORM header extension (RFC 5651 §5.2 / RFC 5740 §4.1).
- LctHeader
- A decoded LCT header (RFC 5651 §5.1).
- NormCmd
- A NORM_CMD message (RFC 5740 §4.2.3): common header + sender word + an 8-bit
sub-typeselecting the body, then the sub-type-specific content (kept opaque, plus the extension chain). - Norm
Common Header - The NORM common message header (RFC 5740 §4.1, Figure 1): 8 bytes carrying
version | type | hdr_len | sequence | source_id. - Norm
Data - NORM_DATA fixed header beyond the common header + sender word (RFC 5740
§4.2.1, Figure 4):
flags | fec_id | object_transport_id | fec_payload_id. - Norm
Feedback - A NORM feedback message — NORM_NACK (type 4) or NORM_ACK (type 5)
(RFC 5740 §4.3): common header,
server_id,instance_id, a 16-bit field that isreservedfor NACK /ack_type|ack_idfor ACK, thengrtt_response_sec/grtt_response_usec, extensions, and opaque payload. - Norm
Info - NORM_INFO fixed header beyond the common header + sender word (RFC 5740
§4.2.2, Figure 8):
flags | fec_id | object_transport_id. - Sender
Word - The shared sender word carried by NORM_DATA / NORM_INFO / NORM_CMD
(RFC 5740 §4.2):
instance_id(16) | grtt(8) | backoff(4) | gsize(4). - Source
Block Partition - The source-block structure of a transport object, per RFC 5052 §9.1’s Block Partitioning Algorithm.
Enums§
- Cenc
Algorithm - Content-encoding algorithm of an FDT Instance payload (RFC 6726 §3.4.3).
- Error
- A parse / serialize error.
- LctExt
Type - A known LCT Header Extension Type (RFC 5651 §5.2.1 / §9.2).
- Norm
AckType - NORM ack_type (RFC 5740 §4.2.3, shared by NORM_CMD(ACK_REQ) and NORM_ACK).
- Norm
CmdType - NORM_CMD sub-type (RFC 5740 §4.2.3).
- Norm
Message Type - NORM message
type(RFC 5740 §4.1).
Constants§
- ALC_
HET_ EXT_ FTI - HET for ALC’s EXT_FTI (FEC Object Transmission Information) — RFC 5775 §4.2. Variable-length form (HET 0..=127). The HEC body is FEC-scheme dependent.
- COMMON_
HEADER_ LEN - Size of the NORM common message header in bytes.
- FDT_
INSTANCE_ ID_ MAX - Maximum FDT Instance ID (20-bit field).
- FEC_
PAYLOAD_ ID_ 128_ LEN - Wire size in bytes of a
FecPayloadId128. - FEEDBACK_
FIXED_ LEN - Fixed-header byte size of a NORM feedback message before extensions: common(8) + server_id(4) + instance_id+ack/reserved(4) + 2×grtt(8) = 24.
- FIXED_
HEADER_ LEN - Size in bytes of the fixed first word (V/C/PSI/S/O/H/Res/A/B + HDR_LEN + CP).
- FIXED_
HET_ MIN - The HET boundary: values
< FIXED_HET_MINare variable-length (carry an HEL); values>= FIXED_HET_MINare fixed-length (one 32-bit word, no HEL). - FLUTE_
VERSION - FLUTE version carried in EXT_FDT’s
Vfield (RFC 6726 = 2). - HET_
EXT_ CC - HET for NORM EXT_CC (variable-length, hel = 3) — RFC 5740 §4.2.3.
- HET_
EXT_ CENC - HET for EXT_CENC (FDT Instance Content Encoding) — RFC 6726 §3.4.3. Fixed.
- HET_
EXT_ FDT - HET for EXT_FDT (FDT Instance Header) — RFC 6726 §3.4.1. Fixed-length.
- HET_
EXT_ NOP - HET for EXT_NOP (No-Operation) — RFC 5651 §5.2.1.
- HET_
EXT_ RATE - HET for NORM EXT_RATE (fixed-length) — RFC 5740 §4.2.3.
- HET_
EXT_ TIME - HET for EXT_TIME (Timing information) — RFC 5651 §5.2.2.
- LCT_
HET_ EXT_ AUTH - HET for EXT_AUTH (Packet Authentication) — RFC 5651 §5.2.1.
- LCT_
VERSION - LCT version number for RFC 5651.
- NORM_
FLAG_ EXPLICIT - Repair segment meeting a specific erasure.
- NORM_
FLAG_ FILE - Object is file-based.
- NORM_
FLAG_ INFO - NORM_INFO is available for this object.
- NORM_
FLAG_ REPAIR - Message is a repair transmission.
- NORM_
FLAG_ STREAM - Object is a NORM_OBJECT_STREAM (enables the payload_* fields).
- NORM_
FLAG_ UNRELIABLE - No repair will be supplied (one-shot best-effort).
- NORM_
HET_ EXT_ AUTH - HET for NORM EXT_AUTH (variable-length) — RFC 5740 §8.5.
- NORM_
HET_ EXT_ FTI - HET for NORM EXT_FTI (variable-length) — RFC 5740 §4.2.1.
- NORM_
INFO_ FIXED_ LEN - Fixed header size of NORM_INFO before header extensions: common(8) + sender(4) + flags-word(4) = 16 bytes.
- NORM_
NODE_ ANY - Reserved NormNodeId: wildcard / any.
- NORM_
NODE_ NONE - Reserved NormNodeId: invalid / none.
- NORM_
VERSION - NORM protocol version (RFC 5740 = 1).
- PSI_SPI
- ALC PSI bit: SPI (Source Packet Indicator) — RFC 5775 §2.1, the high PSI bit. SPI = 1 ⇒ source-data FEC Payload ID format; 0 ⇒ repair-data format.
- SENDER_
WORD_ LEN - Size of the shared sender word (instance_id/grtt/backoff/gsize) in bytes.
- TOI_FDT
- The reserved TOI value for FDT Instances (RFC 6726 §3.3). FDT Instances are carried in ALC packets with TOI = 0.
- USE_ERT
- Use-field bit: Expected Residual Time present.
- USE_
SCT_ HIGH - Use-field bit: Sender Current Time, high 32 bits present.
- USE_
SCT_ LOW - Use-field bit: Sender Current Time, low 32 bits present.
- USE_SLC
- Use-field bit: Session Last Changed time present.
- WORD
- Bytes in one 32-bit word.
Functions§
- chain_
len - Total serialized length (bytes) of a header-extension chain.
- parse_
chain - Parse a chain of header extensions occupying exactly
data. Every byte ofdatamust be consumed; a trailing partial extension is an error. - serialize_
chain - Serialize a header-extension chain into
out. Returns bytes written.
Type Aliases§
- Result
- Result alias for this crate’s parsing / serialization.