use alloc::vec::Vec;
use crate::error::Error;
use crate::ops::{BoxedOp, StreamModel};
use mpeg_ts::ts::{TS_PACKET_SIZE, TS_SYNC_BYTE};
pub(crate) struct Engine {
ops: Vec<BoxedOp>,
model: StreamModel,
}
impl Engine {
pub(crate) fn new(ops: Vec<BoxedOp>) -> Self {
Self {
ops,
model: StreamModel::default(),
}
}
fn validate(packet: &[u8]) -> Result<(), Error> {
if packet.len() != TS_PACKET_SIZE {
return Err(Error::ShortPacket { len: packet.len() });
}
if packet[0] != TS_SYNC_BYTE {
return Err(Error::NoSyncByte { found: packet[0] });
}
Ok(())
}
pub(crate) fn push(&mut self, packet: &[u8], mut out: impl FnMut(&[u8])) -> Result<(), Error> {
Self::validate(packet)?;
self.model.packet_count += 1;
if self.ops.is_empty() {
out(packet);
return Ok(());
}
if self.ops.len() == 1 {
let op = &mut self.ops[0];
let model = &mut self.model;
op.process(packet, model, &mut out);
return Ok(());
}
let mut stage_a: Vec<[u8; TS_PACKET_SIZE]> = Vec::new();
let mut stage_b: Vec<[u8; TS_PACKET_SIZE]> = Vec::new();
let mut arr = [0u8; TS_PACKET_SIZE];
arr.copy_from_slice(packet);
stage_a.push(arr);
let model = &mut self.model;
let ops = &mut self.ops;
let last = ops.len() - 1;
for (i, op) in ops.iter_mut().enumerate() {
stage_b.clear();
if i < last {
for pkt in stage_a.drain(..) {
op.process(&pkt, model, &mut |emitted: &[u8]| {
let mut buf = [0u8; TS_PACKET_SIZE];
buf.copy_from_slice(emitted);
stage_b.push(buf);
});
}
core::mem::swap(&mut stage_a, &mut stage_b);
} else {
for pkt in stage_a.drain(..) {
op.process(&pkt, model, &mut |emitted: &[u8]| {
out(emitted);
});
}
}
}
Ok(())
}
pub(crate) fn finish(&mut self, mut out: impl FnMut(&[u8])) {
if self.ops.is_empty() {
return;
}
if self.ops.len() == 1 {
let op = &mut self.ops[0];
let model = &mut self.model;
op.flush(model, &mut out);
return;
}
let model = &mut self.model;
let ops = &mut self.ops;
let last = ops.len() - 1;
let mut stage_a: Vec<[u8; TS_PACKET_SIZE]> = Vec::new();
for (i, op) in ops.iter_mut().enumerate() {
op.flush(model, &mut |emitted: &[u8]| {
let mut buf = [0u8; TS_PACKET_SIZE];
buf.copy_from_slice(emitted);
stage_a.push(buf);
});
if i == last {
for pkt in stage_a.drain(..) {
out(&pkt);
}
}
}
}
}