use mpeg_ts::owned::OwnedTsPacket;
use mpeg_ts::ts::TS_PACKET_SIZE;
use crate::ops::{Op, StreamModel};
const NULL_PID: u16 = 0x1FFF;
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum Stuffing {
DropNulls,
PadTo {
packets_per_input: f64,
},
}
impl Stuffing {
pub fn drop_nulls() -> Self {
Self::DropNulls
}
pub fn pad_to(packets_per_input: f64) -> Self {
Self::PadTo { packets_per_input }
}
}
fn make_null_packet(continuity_counter: u8) -> [u8; TS_PACKET_SIZE] {
OwnedTsPacket::null_packet(continuity_counter)
}
pub(crate) struct StuffingOp {
mode: StuffingMode,
}
enum StuffingMode {
Drop,
Pad {
nulls_per_real: f64,
accumulated: f64,
null_cc: u8,
},
}
impl StuffingOp {
pub(crate) fn new(cfg: Stuffing) -> Self {
let mode = match cfg {
Stuffing::DropNulls => StuffingMode::Drop,
Stuffing::PadTo { packets_per_input } => {
let nulls_per_real = packets_per_input - 1.0;
StuffingMode::Pad {
nulls_per_real,
accumulated: 0.0,
null_cc: 0,
}
}
};
Self { mode }
}
fn is_null_packet(packet: &[u8]) -> bool {
if packet.len() < 3 {
return false;
}
let pid = (((packet[1] & 0x1F) as u16) << 8) | packet[2] as u16;
pid == NULL_PID
}
}
impl Op for StuffingOp {
fn process(&mut self, packet: &[u8], _model: &mut StreamModel, out: &mut dyn FnMut(&[u8])) {
match &mut self.mode {
StuffingMode::Drop => {
if !Self::is_null_packet(packet) {
out(packet);
}
}
StuffingMode::Pad {
nulls_per_real,
accumulated,
null_cc,
..
} => {
out(packet);
*accumulated += *nulls_per_real;
while *accumulated >= 1.0 {
let null = make_null_packet(*null_cc);
out(&null);
*accumulated -= 1.0;
*null_cc = (*null_cc + 1) & 0x0F;
}
}
}
}
fn flush(&mut self, _model: &mut StreamModel, _out: &mut dyn FnMut(&[u8])) {
}
}