use broadcast_common::{Parse, Serialize};
use dvb_subtitle::{AnySegment, PesDataField};
use mpeg_pes::{PesAssembler, PesPacket, StreamId};
const M6: &[u8] = include_bytes!("../../dvb-si/tests/fixtures/m6-single.ts");
const PKT: usize = 188;
const SUBTITLE_PID: u16 = 0x008C; const PRIVATE_STREAM_1: StreamId = StreamId(0xBD);
const DATA_IDENTIFIER_DVB_SUBTITLE: u8 = 0x20;
fn ts_payload(p: &[u8]) -> Option<(u16, bool, &[u8])> {
if p.len() != PKT || p[0] != 0x47 {
return None;
}
let pid = (u16::from(p[1] & 0x1F) << 8) | u16::from(p[2]);
let pusi = p[1] & 0x40 != 0;
let start = match (p[3] >> 4) & 0x03 {
1 => 4,
3 => 5 + usize::from(p[4]),
_ => return None,
};
(start < PKT).then(|| (pid, pusi, &p[start..]))
}
fn subtitle_pes(ts: &[u8], pid: u16) -> Vec<Vec<u8>> {
let mut asm = PesAssembler::new();
let mut out = Vec::new();
for pkt in ts.chunks(PKT) {
if let Some((p, pusi, payload)) = ts_payload(pkt) {
if p == pid {
if let Some(v) = asm.feed(pusi, payload) {
out.push(v);
}
}
}
}
if let Some(v) = asm.flush() {
out.push(v);
}
out
}
#[test]
fn parses_real_subtitle_pes_from_m6() {
let pes = subtitle_pes(M6, SUBTITLE_PID);
assert!(!pes.is_empty(), "no subtitle PES reassembled");
let mut fields = 0;
let mut total_segments = 0;
let mut known_segments = 0;
let mut clut_segments = 0;
for raw in &pes {
let pkt = PesPacket::parse(raw).expect("subtitle PES parses");
if pkt.stream_id != PRIVATE_STREAM_1
|| pkt.payload.first() != Some(&DATA_IDENTIFIER_DVB_SUBTITLE)
{
continue;
}
let field = PesDataField::parse(pkt.payload).expect("PES_data_field parses");
fields += 1;
total_segments += field.segments.len();
for seg in &field.segments {
if !matches!(seg, AnySegment::Unknown { .. }) {
known_segments += 1;
}
if matches!(seg, AnySegment::ClutDefinition(_)) {
clut_segments += 1;
}
}
assert_eq!(
field.to_bytes(),
pkt.payload,
"subtitle PES_data_field must round-trip byte-identically"
);
}
assert!(fields >= 1, "expected at least one PES_data_field");
assert!(
total_segments >= 1,
"expected at least one subtitling segment, got {total_segments}"
);
assert!(
known_segments >= 1,
"expected at least one recognised segment type, got {known_segments}"
);
assert!(
clut_segments >= 1,
"expected a typed CLUT_definition segment, got {clut_segments} (degraded to Unknown?)"
);
eprintln!(
"fixture_subtitle: {fields} PES data fields, {total_segments} segments ({known_segments} typed, {clut_segments} CLUT)"
);
}