use crate::error::{Error, Result};
use crate::pid::Pid;
use crate::ts::{
ADAPTATION_FLAG, AF_PCR_FLAG, AdaptationField, AdaptationFieldControl, CC_MASK, PCR_FIELD_LEN,
Pcr, ScramblingControl, TS_PACKET_SIZE, TS_SYNC_BYTE, TsHeader,
};
const NULL_PID: Pid = crate::pid::well_known::NULL;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OwnedTsPacket {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_raw_bytes"))]
raw: [u8; TS_PACKET_SIZE],
pid: Pid,
pusi: bool,
has_adaptation: bool,
has_payload: bool,
tei: bool,
scrambling: u8,
continuity_counter: u8,
pub discontinuity: bool,
}
#[cfg(feature = "serde")]
fn serialize_raw_bytes<S: serde::Serializer>(
bytes: &[u8; TS_PACKET_SIZE],
s: S,
) -> core::result::Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq;
let mut seq = s.serialize_seq(Some(bytes.len()))?;
for b in bytes {
seq.serialize_element(b)?;
}
seq.end()
}
impl OwnedTsPacket {
pub fn raw(&self) -> &[u8; TS_PACKET_SIZE] {
&self.raw
}
pub fn into_raw(self) -> [u8; TS_PACKET_SIZE] {
self.raw
}
pub fn pid(&self) -> Pid {
self.pid
}
pub fn pusi(&self) -> bool {
self.pusi
}
pub fn has_adaptation(&self) -> bool {
self.has_adaptation
}
pub fn has_payload(&self) -> bool {
self.has_payload
}
pub fn tei(&self) -> bool {
self.tei
}
pub fn scrambling(&self) -> u8 {
self.scrambling
}
pub fn continuity_counter(&self) -> u8 {
self.continuity_counter
}
pub fn parse(raw: [u8; TS_PACKET_SIZE]) -> Result<Self> {
if raw[0] != TS_SYNC_BYTE {
return Err(Error::InvalidSyncByte { found: raw[0] });
}
let hdr = TsHeader::parse(&raw[..4])?;
Ok(Self {
raw,
pid: hdr.pid,
pusi: hdr.pusi,
has_adaptation: hdr.has_adaptation,
has_payload: hdr.has_payload,
tei: hdr.tei,
scrambling: hdr.scrambling,
continuity_counter: hdr.continuity_counter,
discontinuity: false,
})
}
pub fn scrambling_control(&self) -> ScramblingControl {
ScramblingControl::from_bits(self.scrambling)
}
pub fn adaptation_field_control(&self) -> AdaptationFieldControl {
AdaptationFieldControl::from_flags(self.has_adaptation, self.has_payload)
}
pub fn payload(&self) -> Option<&[u8]> {
if !self.has_payload {
return None;
}
let offset = self.payload_offset();
if offset < TS_PACKET_SIZE {
Some(&self.raw[offset..])
} else {
None
}
}
pub fn payload_mut(&mut self) -> Option<&mut [u8]> {
if !self.has_payload {
return None;
}
let offset = self.payload_offset();
if offset < TS_PACKET_SIZE {
Some(&mut self.raw[offset..])
} else {
None
}
}
#[inline]
fn payload_offset(&self) -> usize {
let mut offset = 4;
if self.has_adaptation {
let af_len = self.raw[4] as usize;
offset += 1 + af_len;
}
offset
}
pub const MAX_PAYLOAD_LEN: usize = TS_PACKET_SIZE - 4;
pub fn serialize_with_payload(
pid: Pid,
pusi: bool,
cc: u8,
payload: &[u8],
) -> Result<[u8; TS_PACKET_SIZE]> {
if payload.len() > Self::MAX_PAYLOAD_LEN {
return Err(Error::PayloadTooLarge {
len: payload.len(),
max: Self::MAX_PAYLOAD_LEN,
});
}
let mut pkt = [crate::ts::STUFFING_BYTE; TS_PACKET_SIZE];
let hdr = TsHeader {
tei: false,
pusi,
pid,
scrambling: 0,
has_adaptation: false,
has_payload: true,
continuity_counter: cc & CC_MASK,
};
hdr.serialize_into(&mut pkt)
.expect("serialize TsHeader into 188-byte buf");
pkt[4..4 + payload.len()].copy_from_slice(payload);
Ok(pkt)
}
#[must_use]
pub fn null_packet(cc: u8) -> [u8; TS_PACKET_SIZE] {
Self::serialize_with_payload(NULL_PID, false, cc, &[])
.expect("empty payload always fits a TS packet")
}
pub fn set_continuity_counter(packet: &mut [u8; TS_PACKET_SIZE], cc: u8) {
packet[3] = (packet[3] & !CC_MASK) | (cc & CC_MASK);
}
pub fn set_pcr(packet: &mut [u8; TS_PACKET_SIZE], pcr: Pcr) -> Result<()> {
if packet[3] & ADAPTATION_FLAG == 0 {
return Err(Error::BufferTooShort {
need: 6,
have: 0,
what: "set_pcr: no adaptation field",
});
}
let af_len = packet[4] as usize;
const REQUIRED_AF_LEN: usize = 1 + PCR_FIELD_LEN;
if af_len < REQUIRED_AF_LEN {
return Err(Error::BufferTooShort {
need: REQUIRED_AF_LEN,
have: af_len,
what: "set_pcr: adaptation field too short for flags byte + PCR",
});
}
if packet[5] & AF_PCR_FLAG == 0 {
return Err(Error::BufferTooShort {
need: 6,
have: 0,
what: "set_pcr: PCR flag not set in adaptation field",
});
}
let pcr_start = 6usize;
let pcr_end = pcr_start + PCR_FIELD_LEN;
packet[pcr_start..pcr_end].copy_from_slice(&pcr.to_field_bytes());
Ok(())
}
pub fn adaptation_field(&self) -> Option<crate::Result<AdaptationField<'_>>> {
if self.raw[3] & ADAPTATION_FLAG == 0 {
return None;
}
let af_len = self.raw[4] as usize;
if af_len == 0 || 5 + af_len > TS_PACKET_SIZE {
return None;
}
Some(AdaptationField::parse(&self.raw[5..5 + af_len]))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn getters_report_the_raw_header_fields() {
let mut raw = [crate::ts::STUFFING_BYTE; TS_PACKET_SIZE];
raw[0] = TS_SYNC_BYTE;
raw[1] = 0b1101_1010; raw[2] = 0xBC;
raw[3] = 0b1001_1100; let pkt = OwnedTsPacket::parse(raw).unwrap();
assert!(pkt.tei());
assert!(pkt.pusi());
assert_eq!(pkt.pid(), Pid::new(0x1ABC));
assert_eq!(pkt.scrambling(), 0b10);
assert!(!pkt.has_adaptation());
assert!(pkt.has_payload());
assert_eq!(pkt.continuity_counter(), 0xC);
assert_eq!(pkt.raw(), &raw);
assert_eq!(pkt.into_raw(), raw);
}
#[test]
fn owned_round_trip_and_payload_mut() {
let payload = [0xAAu8; 184];
let mut pkt = OwnedTsPacket::parse(
OwnedTsPacket::serialize_with_payload(Pid::new(0x0100), true, 7, &payload).unwrap(),
)
.unwrap();
assert_eq!(pkt.pid(), Pid::new(0x0100));
assert!(pkt.pusi);
assert_eq!(pkt.continuity_counter, 7);
assert_eq!(pkt.payload().unwrap()[..184], payload[..]);
pkt.payload_mut().unwrap()[0] = 0x55;
assert_eq!(pkt.payload().unwrap()[0], 0x55);
assert!(!pkt.discontinuity);
}
#[test]
fn oversized_payload_is_rejected_not_truncated() {
let payload = [0xAAu8; 185];
assert!(
OwnedTsPacket::serialize_with_payload(Pid::new(0x0100), true, 0, &payload).is_err()
);
}
#[test]
fn max_payload_len_round_trips() {
let payload = [0xBBu8; OwnedTsPacket::MAX_PAYLOAD_LEN];
let raw =
OwnedTsPacket::serialize_with_payload(Pid::new(0x0100), true, 0, &payload).unwrap();
let pkt = OwnedTsPacket::parse(raw).unwrap();
assert_eq!(pkt.payload().unwrap(), &payload[..]);
}
#[test]
fn owned_scrambling_control_accessor() {
let make = |scrambling_bits: u8| -> OwnedTsPacket {
let mut raw =
OwnedTsPacket::serialize_with_payload(Pid::new(0x0100), false, 0, &[]).unwrap();
raw[3] = (raw[3] & 0x3F) | (scrambling_bits << 6);
OwnedTsPacket::parse(raw).unwrap()
};
assert_eq!(
make(0b00).scrambling_control(),
ScramblingControl::NotScrambled
);
assert_eq!(make(0b01).scrambling_control(), ScramblingControl::Reserved);
assert_eq!(make(0b10).scrambling_control(), ScramblingControl::EvenKey);
assert_eq!(make(0b11).scrambling_control(), ScramblingControl::OddKey);
}
#[test]
fn owned_adaptation_field_control_accessor() {
let make = |afc_bits: u8| -> OwnedTsPacket {
let mut raw = [crate::ts::STUFFING_BYTE; TS_PACKET_SIZE];
raw[0] = TS_SYNC_BYTE;
raw[1] = 0x00;
raw[2] = 0x00;
raw[3] = (afc_bits << 4) & 0x30;
if afc_bits & 0b10 != 0 {
raw[4] = 0; }
OwnedTsPacket::parse(raw).unwrap()
};
assert_eq!(
make(0b00).adaptation_field_control(),
AdaptationFieldControl::Reserved
);
assert_eq!(
make(0b01).adaptation_field_control(),
AdaptationFieldControl::PayloadOnly
);
assert_eq!(
make(0b10).adaptation_field_control(),
AdaptationFieldControl::AdaptationOnly
);
assert_eq!(
make(0b11).adaptation_field_control(),
AdaptationFieldControl::AdaptationAndPayload
);
}
#[test]
fn set_pcr_rejects_adaptation_field_too_short_for_pcr() {
let mut raw = [crate::ts::STUFFING_BYTE; TS_PACKET_SIZE];
raw[0] = TS_SYNC_BYTE;
raw[1] = 0x00;
raw[2] = 0x00;
raw[3] = ADAPTATION_FLAG | crate::ts::PAYLOAD_FLAG;
raw[4] = 1; raw[5] = AF_PCR_FLAG; let payload_before = raw[6..].to_vec();
let pcr = Pcr {
base: 10_000,
extension: 0,
};
let err = OwnedTsPacket::set_pcr(&mut raw, pcr).unwrap_err();
assert!(matches!(err, Error::BufferTooShort { .. }));
assert_eq!(
&raw[6..],
payload_before.as_slice(),
"payload must be untouched"
);
}
}