use crate::error::{Error, Result};
use crate::ts::{
AdaptationFieldControl, ScramblingControl, TsHeader, TS_PACKET_SIZE, TS_SYNC_BYTE,
};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OwnedTsPacket {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_raw_bytes"))]
pub raw: [u8; TS_PACKET_SIZE],
pub pid: u16,
pub pusi: bool,
pub has_adaptation: bool,
pub has_payload: bool,
pub tei: bool,
pub scrambling: u8,
pub 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 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 fn serialize_with_payload(
pid: u16,
pusi: bool,
cc: u8,
payload: &[u8],
) -> [u8; TS_PACKET_SIZE] {
let mut pkt = [0xFFu8; TS_PACKET_SIZE];
let hdr = TsHeader {
tei: false,
pusi,
pid,
scrambling: 0,
has_adaptation: false,
has_payload: true,
continuity_counter: cc & 0x0F,
};
hdr.serialize_into(&mut pkt)
.expect("serialize TsHeader into 188-byte buf");
let copy_len = payload.len().min(184);
pkt[4..4 + copy_len].copy_from_slice(&payload[..copy_len]);
pkt
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_round_trip_and_payload_mut() {
let payload = [0xAAu8; 184];
let mut pkt = OwnedTsPacket::parse(OwnedTsPacket::serialize_with_payload(
0x0100, true, 7, &payload,
))
.unwrap();
assert_eq!(pkt.pid, 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 owned_scrambling_control_accessor() {
let make = |scrambling_bits: u8| -> OwnedTsPacket {
let mut raw = OwnedTsPacket::serialize_with_payload(0x0100, false, 0, &[]);
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 = [0xFFu8; 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
);
}
}