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 opcr_base: Option<u64>,
pub opcr_extension: Option<u16>,
pub splice_countdown: Option<i8>,
pub transport_private_data: Option<&'a [u8]>,
pub adaptation_field_extension: Option<AdaptationFieldExtension>,
pub raw: &'a [u8],
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct AdaptationFieldExtension {
pub length: u8,
pub ltw_flag: bool,
pub piecewise_rate_flag: bool,
pub seamless_splice_flag: bool,
pub ltw_valid_flag: Option<bool>,
pub ltw_offset: Option<u16>,
pub piecewise_rate: Option<u32>,
pub splice_type: Option<u8>,
pub dts_next_au: Option<u64>,
}
#[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,
opcr_base: None,
opcr_extension: None,
splice_countdown: None,
transport_private_data: None,
adaptation_field_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 (pcr_base, pcr_extension) = if pcr_flag {
let (b, e) = parse_clock_reference(raw, cursor, "TS adaptation_field PCR")?;
cursor += 6;
(Some(b), Some(e))
} else {
(None, None)
};
let (opcr_base, opcr_extension) = if opcr_flag {
let (b, e) = parse_clock_reference(raw, cursor, "TS adaptation_field OPCR")?;
cursor += 6;
(Some(b), Some(e))
} else {
(None, None)
};
let splice_countdown = if splicing_point_flag {
if cursor + 1 > raw.len() {
return Err(TsError::Truncated {
what: "TS adaptation_field splice_countdown",
have: raw.len() - cursor,
need: 1,
});
}
let v = raw[cursor] as i8;
cursor += 1;
Some(v)
} else {
None
};
let transport_private_data = if transport_private_data_flag {
if cursor + 1 > raw.len() {
return Err(TsError::Truncated {
what: "TS adaptation_field transport_private_data_length",
have: raw.len() - cursor,
need: 1,
});
}
let n = raw[cursor] as usize;
cursor += 1;
let end = cursor.checked_add(n).ok_or(TsError::Truncated {
what: "TS adaptation_field private_data",
have: raw.len() - cursor,
need: n,
})?;
if end > raw.len() {
return Err(TsError::Truncated {
what: "TS adaptation_field private_data",
have: raw.len() - cursor,
need: n,
});
}
let slice = &raw[cursor..end];
cursor = end;
Some(slice)
} else {
None
};
let adaptation_field_extension = if adaptation_field_extension_flag {
Some(parse_extension(raw, &mut cursor)?)
} else {
None
};
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,
opcr_base,
opcr_extension,
splice_countdown,
transport_private_data,
adaptation_field_extension,
raw,
})
}
fn parse_clock_reference(
raw: &[u8],
cursor: usize,
what: &'static str,
) -> Result<(u64, u16), TsError> {
if cursor + 6 > raw.len() {
return Err(TsError::Truncated {
what,
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);
Ok((base, ext))
}
fn parse_extension(raw: &[u8], cursor: &mut usize) -> Result<AdaptationFieldExtension, TsError> {
if *cursor + 1 > raw.len() {
return Err(TsError::Truncated {
what: "TS adaptation_field_extension_length",
have: raw.len() - *cursor,
need: 1,
});
}
let ext_len = raw[*cursor] as usize;
*cursor += 1;
let ext_end = cursor.checked_add(ext_len).ok_or(TsError::Truncated {
what: "TS adaptation_field_extension body",
have: raw.len() - *cursor,
need: ext_len,
})?;
if ext_end > raw.len() {
return Err(TsError::Truncated {
what: "TS adaptation_field_extension body",
have: raw.len() - *cursor,
need: ext_len,
});
}
if ext_len < 1 {
return Ok(AdaptationFieldExtension {
length: 0,
..Default::default()
});
}
let flags = raw[*cursor];
*cursor += 1;
let body_end = ext_end; let ltw_flag = (flags & 0b1000_0000) != 0;
let piecewise_rate_flag = (flags & 0b0100_0000) != 0;
let seamless_splice_flag = (flags & 0b0010_0000) != 0;
let (ltw_valid_flag, ltw_offset) = if ltw_flag {
if *cursor + 2 > body_end {
return Err(TsError::Truncated {
what: "TS adaptation_field ltw",
have: body_end - *cursor,
need: 2,
});
}
let b0 = raw[*cursor];
let b1 = raw[*cursor + 1];
*cursor += 2;
let valid = (b0 & 0b1000_0000) != 0;
let offset = (((b0 & 0b0111_1111) as u16) << 8) | (b1 as u16);
(Some(valid), Some(offset))
} else {
(None, None)
};
let piecewise_rate = if piecewise_rate_flag {
if *cursor + 3 > body_end {
return Err(TsError::Truncated {
what: "TS adaptation_field piecewise_rate",
have: body_end - *cursor,
need: 3,
});
}
let p0 = raw[*cursor] & 0b0011_1111;
let p1 = raw[*cursor + 1];
let p2 = raw[*cursor + 2];
*cursor += 3;
Some(((p0 as u32) << 16) | ((p1 as u32) << 8) | (p2 as u32))
} else {
None
};
let (splice_type, dts_next_au) = if seamless_splice_flag {
if *cursor + 5 > body_end {
return Err(TsError::Truncated {
what: "TS adaptation_field seamless_splice",
have: body_end - *cursor,
need: 5,
});
}
let b0 = raw[*cursor];
let b1 = raw[*cursor + 1];
let b2 = raw[*cursor + 2];
let b3 = raw[*cursor + 3];
let b4 = raw[*cursor + 4];
*cursor += 5;
let stype = (b0 >> 4) & 0b1111;
let dts: u64 = (((b0 >> 1) & 0b0000_0111) as u64) << 30
| (b1 as u64) << 22
| (((b2 >> 1) & 0b0111_1111) as u64) << 15
| (b3 as u64) << 7
| (((b4 >> 1) & 0b0111_1111) as u64);
(Some(stype), Some(dts))
} else {
(None, None)
};
*cursor = body_end;
Ok(AdaptationFieldExtension {
length: ext_len as u8,
ltw_flag,
piecewise_rate_flag,
seamless_splice_flag,
ltw_valid_flag,
ltw_offset,
piecewise_rate,
splice_type,
dts_next_au,
})
}
#[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());
}
fn encode_clock_reference(base: u64, ext: u16) -> [u8; 6] {
let b0 = ((base >> 25) & 0xFF) as u8;
let b1 = ((base >> 17) & 0xFF) as u8;
let b2 = ((base >> 9) & 0xFF) as u8;
let b3 = ((base >> 1) & 0xFF) as u8;
let b4 = (((base & 0b1) as u8) << 7) | 0b0111_1110 | (((ext >> 8) & 0b1) as u8);
let b5 = (ext & 0xFF) as u8;
[b0, b1, b2, b3, b4, b5]
}
fn ts_packet_with_af(af_flags: u8, af_tail: &[u8]) -> Vec<u8> {
let af_len = (1 + af_tail.len()) as u8;
let header = [0x47, 0x01, 0x00, 0b0011_0000];
let mut tail = Vec::new();
tail.push(af_len);
tail.push(af_flags);
tail.extend_from_slice(af_tail);
make_packet(header, &tail)
}
#[test]
fn af_opcr_unpacked() {
let pcr_base: u64 = 0x0_0000_0001;
let pcr_ext: u16 = 0x000;
let opcr_base: u64 = 0x1_FEDC_BA98;
let opcr_ext: u16 = 0x1FE;
let mut tail = Vec::new();
tail.extend_from_slice(&encode_clock_reference(pcr_base, pcr_ext));
tail.extend_from_slice(&encode_clock_reference(opcr_base, opcr_ext));
let bytes = ts_packet_with_af(0b0001_1000, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
let af = pkt.adaptation_field.expect("af");
assert_eq!(af.pcr_base, Some(pcr_base));
assert_eq!(af.pcr_extension, Some(pcr_ext));
assert_eq!(af.opcr_base, Some(opcr_base));
assert_eq!(af.opcr_extension, Some(opcr_ext));
}
#[test]
fn af_splice_countdown_signed() {
let bytes = ts_packet_with_af(0b0000_0100, &[(-3i8) as u8]);
let pkt = TsPacket::parse(&bytes).unwrap();
let af = pkt.adaptation_field.expect("af");
assert!(af.splicing_point_flag);
assert_eq!(af.splice_countdown, Some(-3));
}
#[test]
fn af_transport_private_data() {
let payload: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
let mut tail = Vec::new();
tail.push(payload.len() as u8);
tail.extend_from_slice(payload);
let bytes = ts_packet_with_af(0b0000_0010, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
let af = pkt.adaptation_field.expect("af");
assert!(af.transport_private_data_flag);
assert_eq!(af.transport_private_data, Some(payload));
}
#[test]
fn af_extension_with_ltw_and_piecewise_rate() {
let ltw_offset: u16 = 0x1234;
let b0 = 0b1000_0000 | ((ltw_offset >> 8) & 0x7F) as u8;
let b1 = (ltw_offset & 0xFF) as u8;
let pwr: u32 = 0x2A_3B4C;
let p0 = ((pwr >> 16) & 0x3F) as u8 | 0b1100_0000; let p1 = ((pwr >> 8) & 0xFF) as u8;
let p2 = (pwr & 0xFF) as u8;
let tail = vec![
6, 0b1100_0000, b0,
b1,
p0,
p1,
p2,
];
let bytes = ts_packet_with_af(0b0000_0001, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
let af = pkt.adaptation_field.expect("af");
let ext = af.adaptation_field_extension.expect("ext");
assert_eq!(ext.length, 6);
assert!(ext.ltw_flag);
assert!(ext.piecewise_rate_flag);
assert!(!ext.seamless_splice_flag);
assert_eq!(ext.ltw_valid_flag, Some(true));
assert_eq!(ext.ltw_offset, Some(0x1234));
assert_eq!(ext.piecewise_rate, Some(0x2A_3B4C));
assert_eq!(ext.splice_type, None);
assert_eq!(ext.dts_next_au, None);
}
#[test]
fn af_extension_seamless_splice_dts() {
let dts: u64 = 0x1_2345_6789;
let splice_type: u8 = 5;
let b0 = (splice_type << 4) | (((dts >> 30) & 0b0111) as u8) << 1 | 0b1;
let b1 = ((dts >> 22) & 0xFF) as u8;
let b2 = (((dts >> 15) & 0b0111_1111) as u8) << 1 | 0b1;
let b3 = ((dts >> 7) & 0xFF) as u8;
let b4 = (((dts & 0b0111_1111) as u8) << 1) | 0b1;
let mut tail = Vec::new();
tail.push(6); tail.push(0b0010_0000); tail.extend_from_slice(&[b0, b1, b2, b3, b4]);
let bytes = ts_packet_with_af(0b0000_0001, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
let ext = pkt
.adaptation_field
.unwrap()
.adaptation_field_extension
.expect("ext");
assert!(ext.seamless_splice_flag);
assert_eq!(ext.splice_type, Some(splice_type));
assert_eq!(ext.dts_next_au, Some(dts));
}
#[test]
fn af_extension_with_reserved_padding_is_accepted() {
let ltw_offset: u16 = 0x0ABC;
let b0 = ((ltw_offset >> 8) & 0x7F) as u8; let b1 = (ltw_offset & 0xFF) as u8;
let tail = vec![
4, 0b1000_0000, b0,
b1,
0xFF, ];
let bytes = ts_packet_with_af(0b0000_0001, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
let ext = pkt
.adaptation_field
.unwrap()
.adaptation_field_extension
.expect("ext");
assert_eq!(ext.length, 4);
assert!(ext.ltw_flag);
assert_eq!(ext.ltw_valid_flag, Some(false));
assert_eq!(ext.ltw_offset, Some(0x0ABC));
}
#[test]
fn af_all_optional_subfields_together() {
let pcr_base: u64 = 100;
let pcr_ext: u16 = 50;
let opcr_base: u64 = 200;
let opcr_ext: u16 = 60;
let priv_data: &[u8] = &[0xAB, 0xCD];
let mut tail = Vec::new();
tail.extend_from_slice(&encode_clock_reference(pcr_base, pcr_ext));
tail.extend_from_slice(&encode_clock_reference(opcr_base, opcr_ext));
tail.push(7i8 as u8); tail.push(priv_data.len() as u8); tail.extend_from_slice(priv_data);
tail.push(3);
tail.push(0b1000_0000);
tail.push(0x00);
tail.push(0x00);
let bytes = ts_packet_with_af(0b0001_1111, &tail);
let pkt = TsPacket::parse(&bytes).unwrap();
let af = pkt.adaptation_field.expect("af");
assert_eq!(af.pcr_base, Some(pcr_base));
assert_eq!(af.opcr_base, Some(opcr_base));
assert_eq!(af.opcr_extension, Some(opcr_ext));
assert_eq!(af.splice_countdown, Some(7));
assert_eq!(af.transport_private_data, Some(priv_data));
let ext = af.adaptation_field_extension.expect("ext");
assert_eq!(ext.length, 3);
assert_eq!(ext.ltw_valid_flag, Some(false));
assert_eq!(ext.ltw_offset, Some(0));
}
#[test]
fn af_extension_truncated_body_errors() {
let tail = vec![
5, 0b1000_0000, 0x00, ];
let af_len = (1 + tail.len()) as u8;
let header = [0x47, 0x01, 0x00, 0b0011_0000];
let mut packet_tail = Vec::new();
packet_tail.push(af_len);
packet_tail.push(0b0000_0001);
packet_tail.extend_from_slice(&tail);
let bytes = make_packet(header, &packet_tail);
let err = TsPacket::parse(&bytes).unwrap_err();
match err {
TsError::Truncated { .. } => {}
other => panic!("expected Truncated, got {other:?}"),
}
}
}