use std::collections::BTreeSet;
use ts_fix::{PidFilter, TsFix};
fn main() {
let stream = build_multiprogram_stream();
let total_packets = stream.len() / 188;
println!("input: {total_packets} packets (synthetic multi-program stream)");
let mut engine = TsFix::builder()
.filter_pids(PidFilter::service(1))
.build()
.expect("build should not fail");
let mut output = Vec::new();
for chunk in stream.chunks(188) {
engine
.push(chunk, |pkt| output.extend_from_slice(pkt))
.expect("valid 188-byte packet");
}
engine.finish(|pkt| output.extend_from_slice(pkt));
let mut kept_pids: BTreeSet<u16> = BTreeSet::new();
for chunk in output.chunks(188) {
if chunk.len() >= 3 {
let pid = (((chunk[1] & 0x1F) as u16) << 8) | chunk[2] as u16;
kept_pids.insert(pid);
}
}
println!("kept {} packets", output.len() / 188);
println!("kept PIDs: {:#06x?}", kept_pids.iter().collect::<Vec<_>>());
let expected: BTreeSet<u16> = [0x0000, 0x0100, 0x0101, 0x0102].into();
if kept_pids == expected {
println!("result: PASS");
} else {
println!("result: FAIL (expected {expected:#06x?})");
}
}
fn build_multiprogram_stream() -> Vec<u8> {
let mut out = Vec::new();
let pat = pat_section(&[(1, 0x0100), (2, 0x0200)]);
out.extend_from_slice(§ion_packet(0x0000, &pat));
let pmt1 = pmt_section(1, 0x0101, &[(0x1B, 0x0102)]);
out.extend_from_slice(§ion_packet(0x0100, &pmt1));
let pmt2 = pmt_section(2, 0x0201, &[(0x1B, 0x0202)]);
out.extend_from_slice(§ion_packet(0x0200, &pmt2));
for cc in 0..5 {
out.extend_from_slice(&payload_packet(0x0102, cc));
}
out.extend_from_slice(&payload_packet(0x0101, 0));
for cc in 0..3 {
out.extend_from_slice(&payload_packet(0x0202, cc));
}
out
}
fn pat_section(entries: &[(u16, u16)]) -> Vec<u8> {
let mut body = Vec::new();
body.extend_from_slice(&0x0001u16.to_be_bytes());
body.push(0xC1); body.push(0x00); body.push(0x00); for &(prog, pid) in entries {
body.extend_from_slice(&prog.to_be_bytes());
body.extend_from_slice(&(pid | 0xE000).to_be_bytes());
}
let section_length = body.len() as u16 + 4;
let mut s = vec![0x00];
s.push(0xB0 | ((section_length >> 8) & 0x0F) as u8);
s.push((section_length & 0xFF) as u8);
s.extend_from_slice(&body);
let crc = calc_crc32_mpeg2(&s);
s.extend_from_slice(&crc.to_be_bytes());
s
}
fn pmt_section(program_number: u16, pcr_pid: u16, es_list: &[(u8, u16)]) -> Vec<u8> {
let mut body = Vec::new();
body.extend_from_slice(&program_number.to_be_bytes());
body.push(0xC1); body.push(0x00); body.push(0x00); body.push(0xE0 | ((pcr_pid >> 8) & 0x0F) as u8);
body.push((pcr_pid & 0xFF) as u8);
body.push(0xF0);
body.push(0x00);
for &(stype, pid) in es_list {
body.push(stype);
body.push(0xE0 | ((pid >> 8) & 0x0F) as u8);
body.push((pid & 0xFF) as u8);
body.push(0xF0);
body.push(0x00);
}
let section_length = body.len() as u16 + 4;
let mut s = vec![0x02];
s.push(0xB0 | ((section_length >> 8) & 0x0F) as u8);
s.push((section_length & 0xFF) as u8);
s.extend_from_slice(&body);
let crc = calc_crc32_mpeg2(&s);
s.extend_from_slice(&crc.to_be_bytes());
s
}
fn section_packet(pid: u16, section: &[u8]) -> [u8; 188] {
let mut pkt = [0xFFu8; 188];
pkt[0] = 0x47;
let pid_hi = ((pid >> 8) & 0x1F) as u8;
pkt[1] = pid_hi | 0x40; pkt[2] = (pid & 0xFF) as u8;
pkt[3] = 0x10; pkt[4] = 0x00; let copy_len = section.len().min(183);
pkt[5..5 + copy_len].copy_from_slice(§ion[..copy_len]);
pkt
}
fn payload_packet(pid: u16, cc: u8) -> [u8; 188] {
let mut pkt = [0xFFu8; 188];
pkt[0] = 0x47;
let pid_hi = ((pid >> 8) & 0x1F) as u8;
pkt[1] = pid_hi;
pkt[2] = (pid & 0xFF) as u8;
pkt[3] = 0x10 | (cc & 0x0F);
for i in 0..180 {
pkt[4 + i] = (i & 0xFF) as u8;
}
pkt
}
fn calc_crc32_mpeg2(data: &[u8]) -> u32 {
let poly: u32 = 0x04C11DB7;
let mut crc: u32 = 0xFFFFFFFF;
for &byte in data {
crc ^= (byte as u32) << 24;
for _ in 0..8 {
if crc & 0x80000000 != 0 {
crc = (crc << 1) ^ poly;
} else {
crc <<= 1;
}
}
}
crc
}