use crate::{
pack_bits,
psi::{
Psi,
PsiSectionError,
Sections,
psi_section_length,
},
ts::PID_NONE,
utils::crc32b,
};
pub const PAT_PID: u16 = 0x0000;
const PAT_TABLE_ID: u8 = 0x00;
const PAT_HEADER_SIZE: usize = 8;
const PAT_ITEM_SIZE: usize = 4;
const PAT_CRC_SIZE: usize = 4;
const PAT_SECTION_SIZE: usize = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PatProgram {
pub program_number: u16,
pub pid: u16,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PatConfig {
pub transport_stream_id: u16,
pub version: u8,
pub programs: Vec<PatProgram>,
}
pub struct PatProgramRef<'a>(&'a [u8]);
impl<'a> PatProgramRef<'a> {
pub fn program_number(&self) -> u16 {
u16::from_be_bytes([self.0[0], self.0[1]])
}
pub fn pid(&self) -> u16 {
u16::from_be_bytes([self.0[2], self.0[3]]) & 0x1fff
}
}
impl<'a> TryFrom<&'a [u8]> for PatProgramRef<'a> {
type Error = PsiSectionError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < PAT_ITEM_SIZE {
Err(PsiSectionError::InvalidSectionLength)
} else {
Ok(PatProgramRef(&value[0 .. PAT_ITEM_SIZE]))
}
}
}
pub struct PatProgramIter<'a> {
data: &'a [u8],
offset: usize,
}
impl<'a> Iterator for PatProgramIter<'a> {
type Item = Result<PatProgramRef<'a>, PsiSectionError>;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.data.len() {
return None;
}
let remaining = &self.data[self.offset ..];
match PatProgramRef::try_from(remaining) {
Ok(program) => {
self.offset += PAT_ITEM_SIZE;
Some(Ok(program))
}
Err(e) => {
self.offset = self.data.len();
Some(Err(e))
}
}
}
}
pub struct PatSectionRef<'a>(&'a [u8]);
impl<'a> PatSectionRef<'a> {
pub fn table_id(&self) -> u8 {
self.0[0]
}
pub fn transport_stream_id(&self) -> u16 {
u16::from_be_bytes([self.0[3], self.0[4]])
}
pub fn version(&self) -> u8 {
(self.0[5] & 0x3e) >> 1
}
pub fn programs(&self) -> PatProgramIter<'a> {
PatProgramIter {
data: &self.0[PAT_HEADER_SIZE .. self.0.len() - PAT_CRC_SIZE],
offset: 0,
}
}
pub fn crc32(&self) -> u32 {
let p = &self.0[self.0.len() - PAT_CRC_SIZE ..];
u32::from_be_bytes([p[0], p[1], p[2], p[3]])
}
}
impl<'a> TryFrom<&'a [u8]> for PatSectionRef<'a> {
type Error = PsiSectionError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < PAT_HEADER_SIZE + PAT_CRC_SIZE {
return Err(PsiSectionError::InvalidSectionLength);
}
if value[0] != PAT_TABLE_ID {
return Err(PsiSectionError::InvalidTableId);
}
let section_length = psi_section_length(value);
if section_length > value.len() {
return Err(PsiSectionError::InvalidSectionLength);
}
let pat = PatSectionRef(&value[.. section_length]);
let checksum = crc32b(&value[.. section_length - PAT_CRC_SIZE]);
if checksum != pat.crc32() {
return Err(PsiSectionError::InvalidCrc32);
}
Ok(pat)
}
}
impl<'a> TryFrom<&'a Psi> for PatSectionRef<'a> {
type Error = PsiSectionError;
fn try_from(psi: &'a Psi) -> Result<Self, Self::Error> {
match psi.payload() {
Some(payload) => PatSectionRef::try_from(payload),
None => Err(PsiSectionError::InvalidSectionLength),
}
}
}
pub struct PatBuilder {
buffer: Vec<u8>,
starts: Vec<usize>,
transport_stream_id: u16,
version: u8,
}
impl PatBuilder {
pub fn build(config: PatConfig) -> Sections {
let mut builder = Self {
buffer: Vec::with_capacity(PAT_SECTION_SIZE),
starts: Vec::new(),
transport_stream_id: config.transport_stream_id,
version: config.version & 0x1f,
};
for program in config.programs {
builder.push(program);
}
builder.finalize()
}
fn push(&mut self, program: PatProgram) {
debug_assert!(program.pid < PID_NONE);
if self.starts.is_empty() {
self.begin_section();
} else {
let last_section_start = *self.starts.last().unwrap();
let current_section_size = self.buffer.len() - last_section_start;
if current_section_size + PAT_ITEM_SIZE + PAT_CRC_SIZE > PAT_SECTION_SIZE {
self.seal_section();
self.begin_section();
}
}
self.buffer
.extend_from_slice(&program.program_number.to_be_bytes());
self.buffer.extend_from_slice(&pack_bits!(u16,
reserved: 3 => 0b111,
pid: 13 => program.pid,
));
}
fn finalize(mut self) -> Sections {
if self.starts.is_empty() {
self.begin_section();
}
self.seal_section();
let last_section_number = (self.starts.len() - 1) as u8;
for i in 0 .. self.starts.len() {
let start = self.starts[i];
let end = if i + 1 < self.starts.len() {
self.starts[i + 1]
} else {
self.buffer.len()
};
let section_length = (end - start - 3) as u16;
self.buffer[start + 1 .. start + 3].copy_from_slice(&pack_bits!(u16,
section_syntax_indicator: 1 => 1,
private_bit: 1 => 0,
reserved1: 2 => 0b11,
section_length: 12 => section_length,
));
self.buffer[start + 6] = i as u8;
self.buffer[start + 7] = last_section_number;
let crc = crc32b(&self.buffer[start .. end - PAT_CRC_SIZE]);
self.buffer[end - PAT_CRC_SIZE .. end].copy_from_slice(&crc.to_be_bytes());
}
Sections::new(self.buffer, self.starts)
}
fn begin_section(&mut self) {
self.starts.push(self.buffer.len());
self.buffer.extend_from_slice(&pack_bits!(u64,
table_id: 8 => PAT_TABLE_ID,
section_syntax_indicator: 1 => 1,
private_bit: 1 => 0,
reserved1: 2 => 0b11,
section_length: 12 => 0, transport_stream_id: 16 => self.transport_stream_id,
reserved2: 2 => 0b11,
version: 5 => self.version,
current_next_indicator: 1 => 1,
section_number: 8 => 0, last_section_number: 8 => 0, ));
}
fn seal_section(&mut self) {
self.buffer.extend_from_slice(&[0x00; PAT_CRC_SIZE]);
}
}