Expand description
ETSI DVB-S2 / S2X / T2 BBFrame parser + builder.
Supports both Normal Mode (NM) and High Efficiency Mode (HEM) per EN 302 755 v1.4.1 §5.1.7.
Entry points:
header::Bbheader— the 10-byte BBHEADER with parse + serialize.packet::up_iter— user packet extraction from the data field.pump::BbframePump— per-PLP BBFrame→inner-TS pump (orchestrates header parse + carry-over extraction).crc::crc8— CRC-8 encoder (EN 302 307-1 §5.1.4 / EN 302 755 Annex F).issy— ISSY field parser (EN 302 755 Annex C).
§Quick start
use dvb_bbframe::header::{Bbheader, Matype, Mode, TsGs, BBHEADER_LEN};
let hdr = Bbheader {
matype: Matype { ts_gs: TsGs::Ts, sis: true, ccm: true, issyi: false, npd: false, ext: 0, isi: 0 },
upl: 1504, sync: 0x47, dfl: 1504, syncd: 0, mode: Mode::Normal, issy_in_header: None,
};
let bytes = hdr.serialize(); // 10-byte BBHEADER
assert_eq!(bytes.len(), BBHEADER_LEN);
assert_eq!(Bbheader::parse(&bytes).unwrap(), hdr); // byte-identical round-tripFor recovering the inner TS carried in a T2-MI stream, see
dvb_t2mi::inner_ts::InnerTsRecovery, which drives this header + the
packet extractor for you.
§Generic Stream (GSE) handoff
When Bbheader::parse yields matype.ts_gs == TsGs::Gse, the data field
carries GSE (Generic Stream Encapsulation) packets (EN 302 307-1 / EN 302 755).
GSE parsing is out of scope for this crate — hand the data field to the
third-party dvb-gse crate:
use dvb_bbframe::header::{Bbheader, TsGs, BBHEADER_LEN};
let hdr = Bbheader::parse(df_bytes).unwrap();
let data_field = &df_bytes[BBHEADER_LEN..];
match hdr.matype.ts_gs {
TsGs::Ts => {
/* TS user packets: dvb_bbframe::packet::up_iter(data_field, &hdr) */
}
TsGs::Gse => {
/* GSE packets: hand `data_field` to the `dvb-gse` crate */
}
other => {
/* GFPS / GCS — generic continuous or packetized */
}
}§RFU policy
BBFrame reserved_future_use bits are emitted as 1 and
reserved_zero_future_use bits as 0, following the DVB convention.
Parsers accept any value (no rejection on non-zero RFU) for forward
compatibility.
§Examples
Two runnable examples ship with this crate (cargo run -p dvb-bbframe --example <name>).
§parse_bbheader
//! Basic: parse a single DVB-S2 BBHEADER from raw bytes.
//!
//! Run with: `cargo run -p dvb-bbframe --example parse_bbheader`
use dvb_bbframe::crc::crc8;
use dvb_bbframe::header::Bbheader;
fn main() {
// A 10-byte Normal-Mode BBHEADER: MATYPE-1/2, UPL, DFL, SYNC, SYNCD, CRC-8.
// (UPL=1520 bits = a 188-byte TS packet, SYNC=0x47, TS/CCM stream.)
#[rustfmt::skip]
let mut header = [
0xD8, 0x00, // MATYPE-1, MATYPE-2
0x05, 0xF0, // UPL = 1520 bits
0xE0, 0x30, // DFL = 57392 bits
0x47, // SYNC byte
0x00, 0x00, // SYNCD
0x00, // CRC-8 — filled below
];
// In Normal Mode the MODE field is folded into the CRC-8: a stored byte
// equal to crc8(header[..9]) decodes back to Mode::Normal.
header[9] = crc8(&header[..9]);
let hdr = Bbheader::parse(&header).expect("valid BBHEADER");
println!("mode : {:?}", hdr.mode);
println!("ts_gs : {:?}", hdr.matype.ts_gs);
println!("ccm : {}", hdr.matype.ccm);
println!("issyi : {}", hdr.matype.issyi);
println!("UPL : {} bits", hdr.upl);
println!("DFL : {} bits", hdr.dfl);
println!("SYNC : {:#04X}", hdr.sync);
println!("SYNCD : {}", hdr.syncd);
}§walk_capture
//! Advanced: walk a real DVB-S2 capture, reassemble BBFrames from TS private
//! sections, and parse every BBHEADER.
//!
//! Run with: `cargo run -p dvb-bbframe --example walk_capture`
//!
//! Reads the committed `tnt-5w-12732v-bbframe.ts` fixture at runtime, so the
//! example compiles even when the fixture is absent.
use dvb_bbframe::crc::crc8;
use dvb_bbframe::header::{Bbheader, Mode};
const BBFRAME_PID: u16 = 0x010E;
const NEW_FRAME_COUNT: u8 = 0xB8; // section count byte marking a new BBFrame
/// Reassemble complete BBFrames carried in TS private sections on `pid`.
fn extract_bbframes(data: &[u8], pid: u16) -> Vec<Vec<u8>> {
let mut frames = Vec::new();
let mut current = Vec::with_capacity(8192);
let mut started = false;
for pkt in data.chunks(188) {
if pkt.len() < 188 || pkt[0] != 0x47 {
continue;
}
let ts_pid = ((u16::from(pkt[1]) & 0x1F) << 8) | u16::from(pkt[2]);
if ts_pid != pid || pkt[3] & 0x30 != 0x10 {
continue;
}
if pkt[4] != 0x00 || pkt[5] != 0x80 || pkt[6] != 0x00 {
continue; // section header: 00 80 00 [slen] [count]
}
let slen = usize::from(pkt[7]);
if slen == 0 || slen > 0xB4 {
continue;
}
let data_end = 9 + (slen - 1);
if data_end > 188 {
continue;
}
if pkt[8] == NEW_FRAME_COUNT {
if started && !current.is_empty() {
frames.push(core::mem::take(&mut current));
}
started = true;
current.clear();
}
if started {
current.extend_from_slice(&pkt[9..data_end]);
}
}
frames
}
fn main() {
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/tnt-5w-12732v-bbframe.ts"
);
let data = match std::fs::read(path) {
Ok(b) => b,
Err(e) => {
eprintln!("fixture not available ({e}); nothing to do");
return;
}
};
let frames = extract_bbframes(&data, BBFRAME_PID);
println!(
"PID {BBFRAME_PID:#06X}: {} BBFrames reassembled",
frames.len()
);
let mut normal = 0;
let mut crc_ok = 0;
for frame in &frames {
if frame.len() < 10 {
continue;
}
let hdr = Bbheader::parse(frame).expect("BBHEADER parses");
if hdr.mode == Mode::Normal {
normal += 1;
}
// NM integrity: stored CRC-8 equals computed over the first 9 bytes.
if crc8(&frame[..9]) == frame[9] {
crc_ok += 1;
}
}
println!("Normal-Mode headers : {normal}");
println!("CRC-8 intact : {crc_ok}/{}", frames.len());
if let Some(first) = frames.first() {
let hdr = Bbheader::parse(first).unwrap();
println!(
"first frame : UPL={} bits, DFL={} bits, SYNC={:#04X}",
hdr.upl, hdr.dfl, hdr.sync
);
}
}Re-exports§
Modules§
- crc
- CRC-8 encoder per EN 302 755 Annex F / EN 302 307-1 §5.1.4.
- error
- Error type for BBFrame parsing and serialization.
- header
- BBHEADER (Base-Band Header) parser and builder.
- issy
- ISSY (Input Stream SYnchronizer) field decoding per EN 302 755 §5.1.7 / Annex C Table C.1 (DVB-T2) and EN 302 307-1 Annex D Table D.1 (DVB-S2 BUFSTAT).
- packet
- User packet extraction from BBFrame data fields.
- pump
BbframePump— per-PLP BBFrame→inner-TS pump.