mod timestamp;
pub use timestamp::{
PtsDts,
Timestamp,
};
use crate::ts::{
PACKET_SIZE,
TsPacketMut,
};
const PES_BASE_HEADER_SIZE: usize = 6;
const PES_OPTIONAL_HEADER_SIZE: usize = 9;
pub const STREAM_ID_VIDEO: u8 = 0xE0; pub const STREAM_ID_AUDIO: u8 = 0xC0; pub const STREAM_ID_PRIVATE_1: u8 = 0xBD; pub const STREAM_ID_PRIVATE_2: u8 = 0xBF;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PesHeaderError {
InvalidHeaderLength,
InvalidStartCode,
}
impl core::error::Error for PesHeaderError {}
impl std::fmt::Display for PesHeaderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PesHeaderError::InvalidHeaderLength => "Invalid PES header length".fmt(f),
PesHeaderError::InvalidStartCode => "Invalid PES start code".fmt(f),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PesHeaderRef<'a>(&'a [u8]);
impl<'a> PesHeaderRef<'a> {
pub fn stream_id(&self) -> u8 {
self.0[3]
}
pub fn packet_length(&self) -> u16 {
u16::from_be_bytes([self.0[4], self.0[5]])
}
pub fn header_len(&self) -> usize {
self.0.len()
}
pub fn pts_dts(&self) -> Option<PtsDts> {
if !has_optional_pes_header(self.stream_id()) {
return None;
}
let pts_dts_flags = (self.0[7] >> 6) & 0x03;
match pts_dts_flags {
0b10 => Some(PtsDts::new(Timestamp::read(&self.0[9 .. 14], 0b0010)?)),
0b11 => Some(
PtsDts::new(Timestamp::read(&self.0[9 .. 14], 0b0011)?)
.with_dts(Timestamp::read(&self.0[14 .. 19], 0b0001)?),
),
_ => None,
}
}
}
impl AsRef<[u8]> for PesHeaderRef<'_> {
fn as_ref(&self) -> &[u8] {
self.0
}
}
impl<'a> TryFrom<&'a [u8]> for PesHeaderRef<'a> {
type Error = PesHeaderError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < PES_BASE_HEADER_SIZE {
return Err(PesHeaderError::InvalidHeaderLength);
}
if !value.starts_with(&[0x00, 0x00, 0x01]) {
return Err(PesHeaderError::InvalidStartCode);
}
let stream_id = value[3];
if !has_optional_pes_header(stream_id) {
return Ok(Self(&value[.. PES_BASE_HEADER_SIZE]));
}
if value.len() < PES_OPTIONAL_HEADER_SIZE {
return Err(PesHeaderError::InvalidHeaderLength);
}
if (value[6] & 0xC0) != 0x80 {
return Err(PesHeaderError::InvalidHeaderLength);
}
let header_data_length = value[8] as usize;
let header_len = PES_OPTIONAL_HEADER_SIZE + header_data_length;
if value.len() >= header_len {
Ok(Self(&value[.. header_len]))
} else {
Err(PesHeaderError::InvalidHeaderLength)
}
}
}
#[derive(Debug, Clone)]
pub struct PesHeader {
stream_id: u8,
pts_dts: Option<PtsDts>,
data_alignment: bool,
}
impl PesHeader {
pub fn new(stream_id: u8) -> Self {
Self {
stream_id,
pts_dts: None,
data_alignment: false,
}
}
pub fn with_pts_dts(mut self, pts_dts: impl Into<PtsDts>) -> Self {
self.pts_dts = Some(pts_dts.into());
self
}
pub fn with_data_alignment(mut self, value: bool) -> Self {
self.data_alignment = value;
self
}
pub fn write(&self, buf: &mut [u8]) -> usize {
debug_assert!(buf.len() >= 32, "buffer too small for PES header");
buf[0] = 0x00;
buf[1] = 0x00;
buf[2] = 0x01;
buf[3] = self.stream_id;
buf[4] = 0x00;
buf[5] = 0x00;
let flags_1 = 0x80 | if self.data_alignment { 0x04 } else { 0x00 };
buf[6] = flags_1;
buf[7] = 0x00;
buf[8] = 0x00;
let mut offset = 9;
if let Some(pts_dts) = self.pts_dts {
let pts = pts_dts.pts;
if let Some(dts) = pts_dts.dts {
buf[7] |= 0b1100_0000; buf[8] += 10;
offset += pts.write(&mut buf[offset ..], 0b0011);
offset += dts.write(&mut buf[offset ..], 0b0001);
} else {
buf[7] |= 0b1000_0000; buf[8] += 5;
offset += pts.write(&mut buf[offset ..], 0b0010);
}
}
offset
}
}
fn has_optional_pes_header(stream_id: u8) -> bool {
!matches!(
stream_id,
0xBC | 0xBE | 0xBF | 0xF0 | 0xF1 | 0xF2 | 0xF8 | 0xFF )
}
pub struct EsFrame {
pub header: PesHeader,
pub payload: Vec<u8>,
pub rai: bool,
}
pub struct PesPacketizer {
pid: u16,
cc: u8,
pes_header: [u8; 32],
pes_header_len: usize,
offset: usize,
frame: Option<EsFrame>,
}
impl PesPacketizer {
pub fn new(pid: u16) -> Self {
Self {
pid,
cc: 0,
pes_header: [0u8; 32],
pes_header_len: 0,
offset: 0,
frame: None,
}
}
pub fn set_frame(&mut self, frame: EsFrame) {
self.pes_header_len = frame.header.write(&mut self.pes_header);
self.offset = 0;
self.frame = Some(frame);
}
pub fn build_pcr_packet(&mut self, packet: &mut [u8; PACKET_SIZE], pcr: u64) {
let mut ts = TsPacketMut::from(packet);
ts.init(self.pid, self.cc.wrapping_sub(1) & 0x0F);
ts.set_adaptation_field(PACKET_SIZE - 4);
ts.set_pcr(pcr);
}
pub fn next(&mut self, packet: &mut [u8; PACKET_SIZE]) -> bool {
let Some(frame) = &self.frame else {
return false;
};
let is_first = self.offset == 0;
let total = self.pes_header_len + frame.payload.len();
let remaining = total - self.offset;
let mut af_size: usize = 0;
let max_payload = PACKET_SIZE - 4;
if is_first && frame.rai {
af_size = 2; }
let capacity = max_payload - af_size;
if remaining < capacity {
let stuffing = capacity - remaining;
af_size += stuffing;
}
let mut ts = TsPacketMut::from(&mut *packet);
ts.init(self.pid, self.cc);
ts.set_payload();
self.cc = (self.cc + 1) & 0x0F;
if af_size > 0 {
ts.set_adaptation_field(af_size);
}
if is_first {
ts.set_pusi();
if frame.rai {
ts.set_rai();
}
}
let ts_payload = ts.payload_mut().unwrap();
let mut pos = 0;
if self.offset < self.pes_header_len {
let n = (self.pes_header_len - self.offset).min(ts_payload.len());
ts_payload[.. n].copy_from_slice(&self.pes_header[self.offset .. self.offset + n]);
self.offset += n;
pos = n;
}
if self.offset >= self.pes_header_len {
let data_offset = self.offset - self.pes_header_len;
let space = ts_payload.len() - pos;
let n = (frame.payload.len() - data_offset).min(space);
if n > 0 {
ts_payload[pos .. pos + n]
.copy_from_slice(&frame.payload[data_offset .. data_offset + n]);
self.offset += n;
}
}
if self.offset >= total {
self.frame = None;
}
true
}
}