use crate::TsError;
pub const TS_PACKET_LEN: usize = 188;
pub const TS_SYNC_BYTE: u8 = 0x47;
#[derive(Debug, Clone, Copy)]
pub struct AdaptationField<'a> {
pub length: u8,
pub discontinuity_indicator: bool,
pub random_access_indicator: bool,
pub elementary_stream_priority_indicator: bool,
pub pcr_flag: bool,
pub opcr_flag: bool,
pub splicing_point_flag: bool,
pub transport_private_data_flag: bool,
pub adaptation_field_extension_flag: bool,
pub pcr_base: Option<u64>,
pub pcr_extension: Option<u16>,
pub raw: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct TsPacket<'a> {
pub pid: u16,
pub payload_unit_start: bool,
pub transport_error: bool,
pub transport_priority: bool,
pub transport_scrambling_control: u8,
pub continuity_counter: u8,
pub adaptation_field: Option<AdaptationField<'a>>,
pub payload: &'a [u8],
pub bytes: &'a [u8],
}
impl<'a> TsPacket<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, TsError> {
if bytes.len() < TS_PACKET_LEN {
return Err(TsError::Truncated {
what: "TS packet",
have: bytes.len(),
need: TS_PACKET_LEN,
});
}
let bytes = &bytes[..TS_PACKET_LEN];
if bytes[0] != TS_SYNC_BYTE {
return Err(TsError::BadSyncByte(bytes[0]));
}
let b1 = bytes[1];
let b2 = bytes[2];
let b3 = bytes[3];
let transport_error = (b1 & 0b1000_0000) != 0;
let payload_unit_start = (b1 & 0b0100_0000) != 0;
let transport_priority = (b1 & 0b0010_0000) != 0;
let pid = (((b1 & 0b0001_1111) as u16) << 8) | (b2 as u16);
let transport_scrambling_control = (b3 >> 6) & 0b11;
let adaptation_field_control = (b3 >> 4) & 0b11;
let continuity_counter = b3 & 0b1111;
let has_af = (adaptation_field_control & 0b10) != 0;
let has_payload = (adaptation_field_control & 0b01) != 0;
let mut cursor: usize = 4;
let mut adaptation_field = None;
if has_af {
let af_len = bytes[cursor] as usize;
let af_total = af_len + 1;
let af_end = cursor.checked_add(af_total).ok_or(TsError::Truncated {
what: "TS adaptation_field",
have: bytes.len() - cursor,
need: af_total,
})?;
if af_end > TS_PACKET_LEN {
return Err(TsError::Truncated {
what: "TS adaptation_field",
have: TS_PACKET_LEN - cursor,
need: af_total,
});
}
let af_raw = &bytes[cursor..af_end];
adaptation_field = Some(parse_adaptation_field(af_raw)?);
cursor = af_end;
}
let payload = if has_payload {
&bytes[cursor..]
} else {
&[][..]
};
Ok(Self {
pid,
payload_unit_start,
transport_error,
transport_priority,
transport_scrambling_control,
continuity_counter,
adaptation_field,
payload,
bytes,
})
}
}
fn parse_adaptation_field(raw: &[u8]) -> Result<AdaptationField<'_>, TsError> {
let length = raw[0];
if length == 0 {
return Ok(AdaptationField {
length: 0,
discontinuity_indicator: false,
random_access_indicator: false,
elementary_stream_priority_indicator: false,
pcr_flag: false,
opcr_flag: false,
splicing_point_flag: false,
transport_private_data_flag: false,
adaptation_field_extension_flag: false,
pcr_base: None,
pcr_extension: None,
raw,
});
}
if raw.len() < 2 {
return Err(TsError::Truncated {
what: "TS adaptation_field flags",
have: raw.len(),
need: 2,
});
}
let flags = raw[1];
let discontinuity_indicator = (flags & 0b1000_0000) != 0;
let random_access_indicator = (flags & 0b0100_0000) != 0;
let elementary_stream_priority_indicator = (flags & 0b0010_0000) != 0;
let pcr_flag = (flags & 0b0001_0000) != 0;
let opcr_flag = (flags & 0b0000_1000) != 0;
let splicing_point_flag = (flags & 0b0000_0100) != 0;
let transport_private_data_flag = (flags & 0b0000_0010) != 0;
let adaptation_field_extension_flag = (flags & 0b0000_0001) != 0;
let mut cursor = 2usize;
let mut pcr_base = None;
let mut pcr_extension = None;
if pcr_flag {
if cursor + 6 > raw.len() {
return Err(TsError::Truncated {
what: "TS adaptation_field PCR",
have: raw.len() - cursor,
need: 6,
});
}
let p = &raw[cursor..cursor + 6];
let base: u64 = ((p[0] as u64) << 25)
| ((p[1] as u64) << 17)
| ((p[2] as u64) << 9)
| ((p[3] as u64) << 1)
| (((p[4] >> 7) & 0b1) as u64);
let ext: u16 = (((p[4] & 0b0000_0001) as u16) << 8) | (p[5] as u16);
pcr_base = Some(base);
pcr_extension = Some(ext);
cursor += 6;
}
let _ = cursor;
Ok(AdaptationField {
length,
discontinuity_indicator,
random_access_indicator,
elementary_stream_priority_indicator,
pcr_flag,
opcr_flag,
splicing_point_flag,
transport_private_data_flag,
adaptation_field_extension_flag,
pcr_base,
pcr_extension,
raw,
})
}
#[derive(Debug)]
pub struct TsPacketIter<'a> {
rest: &'a [u8],
halted: bool,
}
impl<'a> Iterator for TsPacketIter<'a> {
type Item = Result<TsPacket<'a>, TsError>;
fn next(&mut self) -> Option<Self::Item> {
if self.halted || self.rest.len() < TS_PACKET_LEN {
return None;
}
let (head, tail) = self.rest.split_at(TS_PACKET_LEN);
self.rest = tail;
match TsPacket::parse(head) {
Ok(pkt) => Some(Ok(pkt)),
Err(e) => {
self.halted = true;
Some(Err(e))
}
}
}
}
pub fn iter_packets(bytes: &[u8]) -> TsPacketIter<'_> {
TsPacketIter {
rest: bytes,
halted: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_packet(header: [u8; 4], tail: &[u8]) -> Vec<u8> {
assert!(tail.len() <= 184);
let mut v = Vec::with_capacity(TS_PACKET_LEN);
v.extend_from_slice(&header);
v.extend_from_slice(tail);
v.resize(TS_PACKET_LEN, 0xFF);
v
}
#[test]
fn bare_audio_packet_payload_only() {
let header = [
0x47,
0b0100_0001, 0x00, 0b0001_0101, ];
let payload = [0xAA, 0xBB, 0xCC, 0xDD];
let bytes = make_packet(header, &payload);
let pkt = TsPacket::parse(&bytes).unwrap();
assert_eq!(pkt.pid, 0x0100);
assert!(pkt.payload_unit_start);
assert!(!pkt.transport_error);
assert_eq!(pkt.continuity_counter, 5);
assert!(pkt.adaptation_field.is_none());
assert_eq!(pkt.payload.len(), 184);
assert_eq!(&pkt.payload[..4], &payload);
}
#[test]
fn packet_with_adaptation_field_and_pcr() {
let base: u64 = 0x1_2345_6789;
let ext: u16 = 0x0AB;
let p0 = ((base >> 25) & 0xFF) as u8;
let p1 = ((base >> 17) & 0xFF) as u8;
let p2 = ((base >> 9) & 0xFF) as u8;
let p3 = ((base >> 1) & 0xFF) as u8;
let p4 = (((base & 0b1) as u8) << 7) | 0b0111_1110 | (((ext >> 8) & 0b1) as u8);
let p5 = (ext & 0xFF) as u8;
let af_len: u8 = 1 + 6;
let af = [af_len, 0b0101_0000, p0, p1, p2, p3, p4, p5];
let header = [0x47, 0x01, 0x00, 0b0011_1010];
let mut tail = Vec::new();
tail.extend_from_slice(&af);
tail.extend_from_slice(&[0x00, 0x00, 0x01, 0xE0]);
let bytes = make_packet(header, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
assert_eq!(pkt.pid, 0x0100);
assert!(!pkt.payload_unit_start);
assert_eq!(pkt.continuity_counter, 0xA);
let af = pkt.adaptation_field.expect("af present");
assert_eq!(af.length, 7);
assert!(af.random_access_indicator);
assert!(af.pcr_flag);
assert_eq!(af.pcr_base, Some(base));
assert_eq!(af.pcr_extension, Some(ext));
assert_eq!(pkt.payload.len(), TS_PACKET_LEN - 4 - 8);
assert_eq!(&pkt.payload[..4], &[0x00, 0x00, 0x01, 0xE0]);
}
#[test]
fn af_only_packet_has_empty_payload() {
let header = [0x47, 0x01, 0x00, 0b0010_0000];
let mut tail = Vec::new();
tail.push(183);
tail.push(0);
tail.extend(std::iter::repeat(0xFF).take(182));
let bytes = make_packet(header, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
assert!(pkt.adaptation_field.is_some());
assert_eq!(pkt.adaptation_field.unwrap().length, 183);
assert!(pkt.payload.is_empty());
}
#[test]
fn wrong_sync_byte_iterator_halts() {
let good_header = [0x47, 0x01, 0x00, 0b0001_0000];
let good = make_packet(good_header, &[]);
let mut bad = good.clone();
bad[0] = 0x48; let mut buf = Vec::new();
buf.extend_from_slice(&good);
buf.extend_from_slice(&bad);
buf.extend_from_slice(&good);
let mut it = iter_packets(&buf);
let first = it.next().unwrap().unwrap();
assert_eq!(first.pid, 0x0100);
let second = it.next().unwrap();
match second {
Err(TsError::BadSyncByte(0x48)) => {}
other => panic!("expected BadSyncByte(0x48), got {other:?}"),
}
assert!(it.next().is_none());
}
#[test]
fn truncated_tail_yields_nothing_extra() {
let good_header = [0x47, 0x00, 0x00, 0b0001_0000];
let good = make_packet(good_header, &[]);
let mut buf = Vec::new();
buf.extend_from_slice(&good);
buf.extend_from_slice(&[0x47, 0x00]); let mut it = iter_packets(&buf);
assert!(it.next().unwrap().is_ok());
assert!(it.next().is_none());
}
}