///|
pub(all) enum ContinuityEvent {
First
Continuous
Duplicate
Discontinuity
IgnoredNull
TransportError
Gap(expected~ : Int, actual~ : Int)
} derive(Eq, Debug)
///|
priv struct PidState {
previous : Packet
duplicated : Bool
}
///|
/// One slot per legal PID. Packet history is bounded by the TS PID namespace.
pub struct Continuity {
priv states : FixedArray[PidState?]
}
///|
pub fn Continuity::new() -> Continuity {
{ states: FixedArray::make(8192, None), }
}
///|
fn same_duplicate(a : Packet, b : Packet, has_pcr : Bool) -> Bool {
for i = 0; i < 188; i = i + 1 {
// H.222.0 permits a fresh PCR, but not a changed OPCR, in a duplicate packet.
if has_pcr && i >= 6 && i < 12 {
continue
}
if a.raw[i] != b.raw[i] {
return false
}
}
true
}
///|
/// Check continuity independently of PSI/PES parsing. At most one consecutive duplicate is legal.
pub fn Continuity::observe(
self : Continuity,
packet : Packet,
) -> Result[ContinuityEvent, Diagnostic] {
let pid = packet.pid
if pid == 8191 {
return Ok(IgnoredNull)
}
if packet.transport_error {
self.states[pid] = None
return Ok(TransportError)
}
let adaptation = match packet.adaptation() {
Err(e) => {
self.states[pid] = None
return Err(e)
}
Ok(a) => a
}
let discontinuity = match adaptation {
Some(a) => a.discontinuity
None => false
}
let has_pcr = match adaptation {
Some(a) => a.pcr is Some(_)
None => false
}
let prev = self.states[pid]
self.states[pid] = Some({ previous: packet, duplicated: false, })
if discontinuity {
return Ok(Discontinuity)
}
match prev {
None => Ok(First)
Some(state) => {
let old = state.previous
let payload = (packet.adaptation_control & 1) != 0
let expected = if payload {
(old.continuity + 1) % 16
} else {
old.continuity
}
if payload &&
packet.continuity == old.continuity &&
same_duplicate(old, packet, has_pcr) {
self.states[pid] = Some({ previous: packet, duplicated: true, })
if state.duplicated {
Ok(Gap(expected~, actual=packet.continuity))
} else {
Ok(Duplicate)
}
} else if packet.continuity == expected {
Ok(Continuous)
} else {
Ok(Gap(expected~, actual=packet.continuity))
}
}
}
}
///|
/// Explicitly discard one PID's baseline after an external loss or seek.
pub fn Continuity::reset_pid(
self : Continuity,
pid : Int,
) -> Result[Unit, Diagnostic] {
if pid < 0 || pid > 8191 {
return Err(fault("pid_range", 0L, Some(pid)))
}
self.states[pid] = None
Ok(())
}