mod data;
use libmpegts::{
psi::*,
slicer::TsSlicer,
ts::{
PACKET_SIZE,
TsPacketRef,
},
utils::crc32b,
};
fn psi_check_crc32(data: &[u8]) -> bool {
let crc32_calculated = crc32b(&data[.. data.len() - 4]);
let crc32_expected = u32::from_be_bytes([
data[data.len() - 4],
data[data.len() - 3],
data[data.len() - 2],
data[data.len() - 1],
]);
crc32_calculated == crc32_expected
}
#[test]
fn test_no_psi() {
let ts_no_payload = TsPacketRef::from(&[
0x47, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
]);
let mut psi = Psi::default();
assert!(psi.assemble(&ts_no_payload).is_none());
assert!(psi.payload().is_none());
}
#[test]
fn test_invalid_pointer_field() {
let ts_invalid = TsPacketRef::from(&[
0x47, 0x40, 0x00, 0x10, 200, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
]);
let mut psi = Psi::default();
assert!(psi.assemble(&ts_invalid).is_none());
assert!(psi.payload().is_none());
}
#[test]
fn test_one_ts_one_psi() {
let mut pat = Psi::default();
TsSlicer::new().slice(data::PAT).for_each(|p| {
pat.assemble(&p);
});
let payload = pat.payload().expect("PAT section expected");
assert_eq!(payload[0], 0x00); assert_eq!(payload.len(), 40); assert!(psi_check_crc32(payload));
}
#[test]
fn test_two_ts_one_psi() {
let mut sdt = Psi::default();
TsSlicer::new().slice(data::SDT).for_each(|p| {
sdt.assemble(&p);
});
let payload = sdt.payload().expect("SDT section expected");
assert_eq!(payload[0], 0x42); assert_eq!(payload.len(), 216); assert!(psi_check_crc32(payload));
}
#[test]
fn test_two_ts_two_psi() {
let first_ts = TsPacketRef::from(&[
0x47, 0x40, 0x14, 0x1e, 0x03, 0xff, 0xff, 0xff, 0x71, 0x70, 0xb4, 0xe3, 0xc5, 0x22, 0x16,
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa,
]);
let second_ts = TsPacketRef::from(&[
0x47, 0x40, 0x14, 0x1f, 0x03, 0x17, 0x4a, 0x1d, 0x72, 0x70, 0x09, 0xe3, 0xc5, 0x22, 0x16,
0x00, 0x23, 0xdb, 0xf3, 0xac, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]);
let mut psi = Psi::default();
assert!(psi.assemble(&first_ts).is_none());
assert!(psi.payload().is_none());
assert!(psi.assemble(&second_ts).is_some());
let first_psi = psi.payload().expect("first PSI section expected");
assert_eq!(first_psi[0], 0x71); assert_eq!(first_psi.len(), 183); assert!(psi_check_crc32(first_psi));
assert!(psi.assemble(&second_ts).is_some());
let second_psi = psi.payload().expect("second PSI section expected");
assert_eq!(second_psi[0], 0x72);
assert_eq!(second_psi.len(), 12); assert!(psi_check_crc32(second_psi));
}
#[test]
fn test_psi_assemble_small_psi() {
let first_ts = TsPacketRef::from(&[
0x47, 0x40, 0x14, 0x1e, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xb0,
]);
let second_ts = TsPacketRef::from(&[
0x47, 0x00, 0x14, 0x1f, 0x25, 0x00, 0x01, 0xc3, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x00,
0x01, 0xe4, 0x07, 0x00, 0x02, 0xe4, 0x08, 0x00, 0x03, 0xe4, 0x09, 0x00, 0x04, 0xe4, 0x0a,
0x00, 0x05, 0xe4, 0x0b, 0x00, 0x06, 0xe4, 0x0c, 0xb2, 0xd1, 0xb8, 0xde, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]);
let mut psi = Psi::default();
assert!(psi.assemble(&first_ts).is_none());
assert!(psi.assemble(&second_ts).is_some());
let payload = psi.payload().expect("PSI section expected");
assert_eq!(payload[0], 0x00); assert_eq!(payload.len(), 40); assert!(psi_check_crc32(payload));
}
#[test]
fn test_packetizer_single_packet() {
let sections = PatBuilder::build(PatConfig {
transport_stream_id: 1,
version: 1,
programs: vec![
PatProgram {
program_number: 0,
pid: 16,
},
PatProgram {
program_number: 1,
pid: 1031,
},
PatProgram {
program_number: 2,
pid: 1032,
},
PatProgram {
program_number: 3,
pid: 1033,
},
PatProgram {
program_number: 4,
pid: 1034,
},
PatProgram {
program_number: 5,
pid: 1035,
},
PatProgram {
program_number: 6,
pid: 1036,
},
],
});
let section_data = sections[0].to_vec();
assert_eq!(section_data.len(), 40);
let mut packetizer = PsiPacketizer::new(0);
packetizer.set_sections(sections);
let mut packet = [0u8; PACKET_SIZE];
assert!(!packetizer.is_empty());
assert!(packetizer.next(&mut packet));
assert!(packetizer.is_empty());
assert!(!packetizer.next(&mut packet));
assert_eq!(packet[0], 0x47); let ts = TsPacketRef::from(&packet);
assert!(ts.is_payload_start()); assert_eq!(ts.pid(), 0x0000); assert_eq!(ts.cc(), 0);
assert_eq!(packet[4], 0x00); assert_eq!(&packet[5 .. 5 + 40], §ion_data[..]);
assert!(packet[5 + 40 ..].iter().all(|&b| b == 0xFF));
}
#[test]
fn test_packetizer_multi_packet() {
let sections = PatBuilder::build(PatConfig {
transport_stream_id: 1,
version: 0,
programs: (0 .. 44)
.map(|i| PatProgram {
program_number: i,
pid: 100 + i,
})
.collect(),
});
assert_eq!(sections.len(), 1);
let section_data = sections[0].to_vec();
assert_eq!(section_data.len(), 188);
let mut packetizer = PsiPacketizer::new(0);
packetizer.set_sections(sections);
let mut p1 = [0u8; PACKET_SIZE];
let mut p2 = [0u8; PACKET_SIZE];
assert!(packetizer.next(&mut p1));
assert!(!packetizer.is_empty());
assert!(packetizer.next(&mut p2));
assert!(packetizer.is_empty());
assert!(!packetizer.next(&mut [0u8; PACKET_SIZE]));
let ts1 = TsPacketRef::from(&p1);
assert!(ts1.is_payload_start());
assert_eq!(ts1.cc(), 0);
assert_eq!(p1[4], 0x00); assert_eq!(&p1[5 ..], §ion_data[.. 183]);
let ts2 = TsPacketRef::from(&p2);
assert!(!ts2.is_payload_start());
assert_eq!(ts2.cc(), 1);
assert_eq!(&p2[4 .. 4 + 5], §ion_data[183 ..]);
assert!(p2[4 + 5 ..].iter().all(|&b| b == 0xFF));
}
#[test]
fn test_packetizer_preserves_cc() {
let sections = PatBuilder::build(PatConfig {
transport_stream_id: 1,
version: 0,
programs: vec![PatProgram {
program_number: 1,
pid: 100,
}],
});
let mut packetizer = PsiPacketizer::new(0);
packetizer.set_sections(sections);
let mut packet = [0u8; PACKET_SIZE];
assert!(packetizer.next(&mut packet));
assert_eq!(TsPacketRef::from(&packet).cc(), 0);
assert!(packetizer.is_empty());
packetizer.reset();
assert!(!packetizer.is_empty());
assert!(packetizer.next(&mut packet));
assert_eq!(TsPacketRef::from(&packet).cc(), 1);
let sections = PatBuilder::build(PatConfig {
transport_stream_id: 1,
version: 1,
programs: vec![PatProgram {
program_number: 1,
pid: 200,
}],
});
packetizer.set_sections(sections);
packetizer.next(&mut packet);
assert_eq!(TsPacketRef::from(&packet).cc(), 2);
}
#[test]
fn test_packetizer_roundtrip() {
let sections = PatBuilder::build(PatConfig {
transport_stream_id: 42,
version: 0,
programs: (0 .. 44)
.map(|i| PatProgram {
program_number: i,
pid: 200 + i,
})
.collect(),
});
let original = sections[0].to_vec();
let mut packetizer = PsiPacketizer::new(0);
packetizer.set_sections(sections);
let mut psi = Psi::default();
let mut packet = [0u8; PACKET_SIZE];
while packetizer.next(&mut packet) {
let ts = TsPacketRef::from(&packet);
psi.assemble(&ts);
}
let payload = psi.payload().expect("Reassembled PAT section");
assert_eq!(payload, &original[..]);
assert!(psi_check_crc32(payload));
let pat = PatSectionRef::try_from(payload).expect("Valid PAT");
assert_eq!(pat.transport_stream_id(), 42);
assert_eq!(pat.programs().count(), 44);
}
#[test]
fn test_packetizer_multiple_sections() {
let sections = PatBuilder::build(PatConfig {
transport_stream_id: 1,
version: 0,
programs: (0 .. 254)
.map(|i| PatProgram {
program_number: i,
pid: 100 + i,
})
.collect(),
});
assert_eq!(sections.len(), 2);
let s0 = sections[0].to_vec();
let s1 = sections[1].to_vec();
let mut packetizer = PsiPacketizer::new(0);
packetizer.set_sections(sections);
let mut packets = Vec::new();
loop {
let mut packet = [0u8; PACKET_SIZE];
if !packetizer.next(&mut packet) {
break;
}
packets.push(packet);
}
assert!(packetizer.is_empty());
for (i, pkt) in packets.iter().enumerate() {
let ts = TsPacketRef::from(pkt);
assert_eq!(ts.cc(), (i as u8) & 0x0F);
}
let ts_first = TsPacketRef::from(&packets[0]);
assert!(ts_first.is_payload_start());
let s0_remaining = s0.len() - 183;
let s0_total_packets = 1 + (s0_remaining + 183) / 184;
let ts_second_start = TsPacketRef::from(&packets[s0_total_packets]);
assert!(ts_second_start.is_payload_start());
if s0_total_packets > 1 {
let ts_cont = TsPacketRef::from(&packets[1]);
assert!(!ts_cont.is_payload_start());
}
PatSectionRef::try_from(&s0[..]).expect("Valid first section");
PatSectionRef::try_from(&s1[..]).expect("Valid second section");
}