Skip to main content

Crate dvb_bbframe

Crate dvb_bbframe 

Source
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-trip

For 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§

pub use error::Error;
pub use error::Result;

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.