use alloc::vec::Vec;
use broadcast_common::bits::{BitReader, BitWriter};
use crate::anc_content::{AncContent, check_field_width};
use crate::error::{Error, Result};
pub const PACKET_START_CODE_PREFIX: [u8; 3] = [0x00, 0x00, 0x01];
pub const ANC_STREAM_ID: u8 = 0xBD;
pub const ANC_PES_HEADER_DATA_LENGTH: u8 = 0x05;
const PTS_DTS_FLAGS_PTS_ONLY: u8 = 0b10;
const PTS_PREFIX: u8 = 0b0010;
pub const STUFFING_BYTE: u8 = 0xFF;
const PTS_MASK: u64 = (1 << 33) - 1;
const W_LEADING_ZEROS: u32 = 6;
const W_C_NOT_Y: u32 = 1;
const W_LINE_NUMBER: u32 = 11;
const W_HORIZONTAL_OFFSET: u32 = 12;
const PES_PREFIX_LEN: usize = 6;
const PES_OPTIONAL_HEADER_LEN: usize = 3 + 5;
const PES_HEADER_LEN: usize = PES_PREFIX_LEN + PES_OPTIONAL_HEADER_LEN;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct AncPacket {
pub c_not_y_channel_flag: bool,
pub line_number: u16,
pub horizontal_offset: u16,
pub did: u16,
pub sdid: u16,
pub data_count: u16,
pub user_data_words: Vec<u16>,
pub checksum: u16,
}
impl AncPacket {
#[must_use]
pub fn udw_loop_count(&self) -> usize {
usize::from(self.data_count & 0xFF)
}
fn as_content(&self) -> AncContent {
AncContent {
did: self.did,
sdid: self.sdid,
data_count: self.data_count,
user_data_words: self.user_data_words.clone(),
checksum: self.checksum,
}
}
fn body_bits(&self) -> usize {
let placement =
(W_LEADING_ZEROS + W_C_NOT_Y + W_LINE_NUMBER + W_HORIZONTAL_OFFSET) as usize;
placement + self.as_content().content_bit_width()
}
fn serialized_byte_len(&self) -> usize {
self.body_bits().div_ceil(8)
}
fn write_into(&self, w: &mut BitWriter<'_>) -> Result<()> {
w.write_bits(0, W_LEADING_ZEROS)?; w.write_bits(u64::from(self.c_not_y_channel_flag), W_C_NOT_Y)?;
w.write_bits(
check_field_width("line_number", u64::from(self.line_number), W_LINE_NUMBER)?,
W_LINE_NUMBER,
)?;
w.write_bits(
check_field_width(
"horizontal_offset",
u64::from(self.horizontal_offset),
W_HORIZONTAL_OFFSET,
)?,
W_HORIZONTAL_OFFSET,
)?;
self.as_content().write_into(w)?;
while !w.is_byte_aligned() {
w.write_bits(1, 1)?;
}
Ok(())
}
fn read_from(r: &mut BitReader<'_>) -> Result<Self> {
r.skip_bits(W_LEADING_ZEROS as usize)?;
let c_not_y_channel_flag = r.read_bool()?;
let line_number = r.read_bits(W_LINE_NUMBER)? as u16;
let horizontal_offset = r.read_bits(W_HORIZONTAL_OFFSET)? as u16;
let AncContent {
did,
sdid,
data_count,
user_data_words,
checksum,
} = AncContent::read_from(r)?;
r.align_to_byte();
Ok(Self {
c_not_y_channel_flag,
line_number,
horizontal_offset,
did,
sdid,
data_count,
user_data_words,
checksum,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct AncDataPacket {
pub pes_priority: bool,
pub copyright: bool,
pub original_or_copy: bool,
pub pts: u64,
pub anc_packets: Vec<AncPacket>,
pub stuffing_bytes: usize,
}
impl AncDataPacket {
fn payload_len(&self) -> usize {
let anc: usize = self
.anc_packets
.iter()
.map(AncPacket::serialized_byte_len)
.sum();
anc + self.stuffing_bytes
}
fn pes_packet_length(&self) -> usize {
PES_OPTIONAL_HEADER_LEN + self.payload_len()
}
pub fn parse(b: &[u8]) -> Result<Self> {
if b.len() < PES_HEADER_LEN {
return Err(Error::BufferTooShort {
need: PES_HEADER_LEN,
have: b.len(),
what: "ANC PES header",
});
}
if b[0..3] != PACKET_START_CODE_PREFIX {
return Err(Error::BadStartCode(
(u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]),
));
}
if b[3] != ANC_STREAM_ID {
return Err(Error::BadStreamId(b[3]));
}
let pes_packet_length = usize::from(u16::from_be_bytes([b[4], b[5]]));
let f1 = b[6];
if (f1 & 0xB4) != 0x84 {
if (f1 >> 6) != 0b10 {
return Err(Error::BadFixedBits("PES '10' marker"));
}
if (f1 & 0x30) != 0x00 {
return Err(Error::BadFixedBits(
"PES_scrambling_control shall be '00' (ST 2038 Table 2)",
));
}
return Err(Error::BadFixedBits(
"data_alignment_indicator shall be '1' (ST 2038 Table 2)",
));
}
let pes_priority = f1 & 0x08 != 0;
let copyright = f1 & 0x02 != 0;
let original_or_copy = f1 & 0x01 != 0;
let f2 = b[7];
let pts_dts_flags = (f2 >> 6) & 0x03;
if pts_dts_flags != PTS_DTS_FLAGS_PTS_ONLY {
return Err(Error::BadPtsDtsFlags(pts_dts_flags));
}
if b[8] != ANC_PES_HEADER_DATA_LENGTH {
return Err(Error::BadHeaderDataLength(b[8]));
}
let pts = read_pts(&b[9..14])?;
let payload_start = PES_HEADER_LEN;
let payload_end = PES_PREFIX_LEN + pes_packet_length;
if payload_end < payload_start {
return Err(Error::PesLengthOverflow {
len: pes_packet_length,
available: b.len().saturating_sub(PES_PREFIX_LEN),
});
}
if b.len() < payload_end {
return Err(Error::PesLengthOverflow {
len: pes_packet_length,
available: b.len().saturating_sub(PES_PREFIX_LEN),
});
}
let payload = &b[payload_start..payload_end];
let mut anc_packets = Vec::new();
let mut pos = 0usize;
while pos < payload.len() {
if payload[pos] == STUFFING_BYTE {
break;
}
let mut r = BitReader::new(&payload[pos..]);
let rec = AncPacket::read_from(&mut r)?;
let consumed = r.bits_read() / 8;
pos += consumed;
anc_packets.push(rec);
}
let stuffing_bytes = payload.len() - pos;
for &byte in &payload[pos..] {
if byte != STUFFING_BYTE {
return Err(Error::BadFixedBits("stuffing_byte (expected 0xFF)"));
}
}
Ok(Self {
pes_priority,
copyright,
original_or_copy,
pts,
anc_packets,
stuffing_bytes,
})
}
#[must_use]
pub fn serialized_len(&self) -> usize {
PES_HEADER_LEN + self.payload_len()
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::BufferTooShort {
need: len,
have: buf.len(),
what: "ANC PES serialize output",
});
}
let pes_len = self.pes_packet_length();
if pes_len > usize::from(u16::MAX) {
return Err(Error::PesLengthTooLarge(pes_len));
}
buf[0..3].copy_from_slice(&PACKET_START_CODE_PREFIX);
buf[3] = ANC_STREAM_ID;
buf[4..6].copy_from_slice(&(pes_len as u16).to_be_bytes());
buf[6] = 0x80
| (u8::from(self.pes_priority) << 3)
| 0x04 | (u8::from(self.copyright) << 1)
| u8::from(self.original_or_copy);
buf[7] = PTS_DTS_FLAGS_PTS_ONLY << 6;
buf[8] = ANC_PES_HEADER_DATA_LENGTH;
buf[9..14].copy_from_slice(&write_pts(self.pts));
let mut pos = PES_HEADER_LEN;
for rec in &self.anc_packets {
let rec_len = rec.serialized_byte_len();
let mut w = BitWriter::new(&mut buf[pos..pos + rec_len]);
rec.write_into(&mut w)?;
pos += rec_len;
}
for byte in buf.iter_mut().skip(pos).take(self.stuffing_bytes) {
*byte = STUFFING_BYTE;
}
Ok(len)
}
}
fn read_pts(b: &[u8]) -> Result<u64> {
if b.len() < 5 {
return Err(Error::BufferTooShort {
need: 5,
have: b.len(),
what: "PTS field",
});
}
if (b[0] >> 4) != PTS_PREFIX {
return Err(Error::BadFixedBits("PTS prefix '0010'"));
}
if b[0] & 0x01 == 0 || b[2] & 0x01 == 0 || b[4] & 0x01 == 0 {
return Err(Error::BadFixedBits("PTS marker_bit"));
}
let hi = u64::from((b[0] >> 1) & 0x07); let mid = (u64::from(b[1]) << 7) | u64::from(b[2] >> 1); let lo = (u64::from(b[3]) << 7) | u64::from(b[4] >> 1); Ok((hi << 30) | (mid << 15) | lo)
}
fn write_pts(pts: u64) -> [u8; 5] {
let ts = pts & PTS_MASK;
[
(PTS_PREFIX << 4) | ((((ts >> 30) & 0x07) as u8) << 1) | 0x01,
((ts >> 22) & 0xFF) as u8,
((((ts >> 15) & 0x7F) as u8) << 1) | 0x01,
((ts >> 7) & 0xFF) as u8,
(((ts & 0x7F) as u8) << 1) | 0x01,
]
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn sample_2packet() -> AncDataPacket {
AncDataPacket {
pes_priority: false,
copyright: false,
original_or_copy: false,
pts: 0x1_2345_6789,
anc_packets: vec![
AncPacket {
c_not_y_channel_flag: false,
line_number: 9,
horizontal_offset: 0,
did: 0x161,
sdid: 0x101,
data_count: 0x102, user_data_words: vec![0x2CF, 0x101],
checksum: 0x233,
},
AncPacket {
c_not_y_channel_flag: true,
line_number: 0x2A,
horizontal_offset: 0x10,
did: 0x241,
sdid: 0x102,
data_count: 0x103, user_data_words: vec![0x111, 0x222, 0x333],
checksum: 0x1AB,
},
],
stuffing_bytes: 3,
}
}
#[test]
fn pts_round_trip() {
for ts in [0u64, 1, 90_000, 0x1_2345_6789, PTS_MASK] {
assert_eq!(read_pts(&write_pts(ts)).unwrap(), ts, "ts={ts:#x}");
}
}
#[test]
fn round_trip_two_packets() {
let p = sample_2packet();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
let reparsed = AncDataPacket::parse(&out).unwrap();
assert_eq!(reparsed, p);
}
#[test]
fn hand_computed_wire_bytes() {
let p = AncDataPacket {
pes_priority: false,
copyright: false,
original_or_copy: false,
pts: 0,
anc_packets: vec![AncPacket {
c_not_y_channel_flag: false,
line_number: 9,
horizontal_offset: 0,
did: 0x161,
sdid: 0x101,
data_count: 0x002,
user_data_words: vec![0x2CF, 0x101],
checksum: 0x233,
}],
stuffing_bytes: 0,
};
let mut expect_bits: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
let mut push = |val: u64, n: u32| {
for i in (0..n).rev() {
expect_bits.push(((val >> i) & 1) as u8);
}
};
push(0, 6);
push(0, 1); push(9, 11);
push(0, 12);
push(0x161, 10);
push(0x101, 10);
push(0x002, 10);
push(0x2CF, 10);
push(0x101, 10);
push(0x233, 10);
while !expect_bits.len().is_multiple_of(8) {
expect_bits.push(1); }
let mut expect_payload = alloc::vec::Vec::new();
for chunk in expect_bits.chunks(8) {
let mut byte = 0u8;
for &bit in chunk {
byte = (byte << 1) | bit;
}
expect_payload.push(byte);
}
let out = {
let mut o = vec![0u8; p.serialized_len()];
p.serialize_into(&mut o).unwrap();
o
};
assert_eq!(
&out[PES_HEADER_LEN..],
&expect_payload[..],
"ANC bit-packing"
);
assert_eq!(&out[0..4], &[0x00, 0x00, 0x01, 0xBD]);
let pes_len = (8 + expect_payload.len()) as u16;
assert_eq!(&out[4..6], &pes_len.to_be_bytes());
assert_eq!(out[6], 0x84); assert_eq!(out[7], 0x80); assert_eq!(out[8], 0x05);
assert_eq!(AncDataPacket::parse(&out).unwrap(), p);
}
#[test]
fn field_mutation_changes_bytes() {
let a = sample_2packet();
let mut b = a.clone();
b.anc_packets[0].user_data_words[0] = 0x000; let mut oa = vec![0u8; a.serialized_len()];
let mut ob = vec![0u8; b.serialized_len()];
a.serialize_into(&mut oa).unwrap();
b.serialize_into(&mut ob).unwrap();
assert_ne!(oa, ob, "changing a UDW must change the wire bytes");
let mut c = a.clone();
c.anc_packets[1].line_number = 0x2B; let mut oc = vec![0u8; c.serialized_len()];
c.serialize_into(&mut oc).unwrap();
assert_ne!(oa, oc, "changing line_number must change the wire bytes");
let mut d = a.clone();
d.anc_packets[0].did = 0x001; let mut od = vec![0u8; d.serialized_len()];
d.serialize_into(&mut od).unwrap();
assert_ne!(oa, od, "changing DID must change the wire bytes");
let mut e = a.clone();
e.anc_packets[0].checksum = 0x001; let mut oe = vec![0u8; e.serialized_len()];
e.serialize_into(&mut oe).unwrap();
assert_ne!(oa, oe, "changing checksum must change the wire bytes");
}
#[test]
fn serialize_rejects_inconsistent_udw_length() {
let p = AncDataPacket {
pes_priority: false,
copyright: false,
original_or_copy: false,
pts: 0,
anc_packets: vec![AncPacket {
c_not_y_channel_flag: false,
line_number: 1,
horizontal_offset: 0,
did: 0x161,
sdid: 0x101,
data_count: 0x003, user_data_words: vec![0x100], checksum: 0x001,
}],
stuffing_bytes: 0,
};
let mut buf = vec![0u8; p.serialized_len()];
assert!(
matches!(
p.serialize_into(&mut buf),
Err(Error::InconsistentUdwLength { have: 1, need: 3 })
),
"expected InconsistentUdwLength {{ have: 1, need: 3 }}"
);
}
#[test]
fn rejects_nonzero_scrambling_control() {
let p = sample_2packet();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
out[6] = (out[6] & !0x30) | 0x10;
assert!(
matches!(AncDataPacket::parse(&out), Err(Error::BadFixedBits(_))),
"expected BadFixedBits for non-zero scrambling_control"
);
}
#[test]
fn rejects_zero_data_alignment_indicator() {
let p = sample_2packet();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
out[6] &= !0x04;
assert!(
matches!(AncDataPacket::parse(&out), Err(Error::BadFixedBits(_))),
"expected BadFixedBits for data_alignment_indicator=0"
);
}
#[test]
fn data_count_upper_bits_use_low_8_for_loop() {
let p = AncDataPacket {
pes_priority: false,
copyright: false,
original_or_copy: false,
pts: 42,
anc_packets: vec![AncPacket {
c_not_y_channel_flag: true,
line_number: 20,
horizontal_offset: 100,
did: 0x241,
sdid: 0x102,
data_count: 0x302, user_data_words: vec![0x123, 0x3FF],
checksum: 0x199,
}],
stuffing_bytes: 0,
};
assert_eq!(p.anc_packets[0].udw_loop_count(), 2);
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
let rp = AncDataPacket::parse(&out).unwrap();
assert_eq!(rp, p);
assert_eq!(rp.anc_packets[0].data_count, 0x302); assert_eq!(rp.anc_packets[0].user_data_words.len(), 2);
}
#[test]
fn rejects_bad_stream_id() {
let p = sample_2packet();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
out[3] = 0xE0;
assert!(matches!(
AncDataPacket::parse(&out),
Err(Error::BadStreamId(0xE0))
));
}
#[test]
fn rejects_bad_start_code() {
let mut out = vec![0u8; sample_2packet().serialized_len()];
sample_2packet().serialize_into(&mut out).unwrap();
out[2] = 0x02;
assert!(matches!(
AncDataPacket::parse(&out),
Err(Error::BadStartCode(0x000002))
));
}
#[test]
fn no_stuffing_round_trips() {
let mut p = sample_2packet();
p.stuffing_bytes = 0;
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
assert_eq!(AncDataPacket::parse(&out).unwrap(), p);
}
}