mux: make B1 concealment decode-clean on every gap shape
Closes the three residual holes where a concealed/lost gap could still let a dangling-reference frame reach the muxer (degraded/undecryptable-disc path only; clean rips are byte-identical and untouched). Root cause: the discontinuity signal was reconstructed from the 4-bit continuity counter and applied per-PES, both of which are lossy. Three coordinated changes: 1. CC-INDEPENDENT marker. fill_null_ts_unit now tags its NULL packets with an adaptation-field discontinuity_indicator; the demuxer recognises a 0x1FFF packet carrying it as a concealed gap and forces a discontinuity on every tracked PID (the lost unit's PID is unknowable). This survives a loss that is an exact multiple of 16 packets (CC aliases to in-sequence — hole 3) and a loss at a PID's very start (no prior CC — hole 4); it also drops any open, potentially-truncated partial PES. 2. PUSI ATTRIBUTION. A gap landing on a PES boundary now flags the PES STARTING after it, not the one flushed at the boundary (hole 1) — stamping the pre-gap frame could arm-then-disarm the gate on a keyframe and admit the real post-gap inter frame. 3. PER-FRAME signal. codec::Frame gains `discontinuity`; each parser propagates it onto the first post-gap frame. MPEG-2 buffers whole GOPs asynchronously, so it associates the gap by ES OFFSET (like PTS/source), landing it on the exact post-gap picture mid-GOP (hole 2) — a per-PES flag stamped the previous picture. consume_ts (and the EOF flush drain) gate on frame.discontinuity. Tests: CC-independent marker with in-sequence CC + leading-loss; PUSI attribution flags the post-gap PES; MPEG-2 offset-mark stamps the post-gap picture through GOP reorder, not the previous one. Existing B1 gate + EOF tests still green (2270 lib tests).
This commit is contained in:
@@ -1542,20 +1542,32 @@ mod tests {
|
||||
}
|
||||
|
||||
/// `fill_null_ts_unit` round-trip: every BD source packet in the unit becomes
|
||||
/// a well-formed TS null packet, and a TS demuxer tracking a real PID sees
|
||||
/// none of them (PID 0x1FFF matches nothing) — the basis for A2 concealment.
|
||||
/// a well-formed TS null packet (PID 0x1FFF, invisible to any real PID) that
|
||||
/// carries the B1 adaptation-field discontinuity_indicator — the marker
|
||||
/// `mux::ts` reads as a concealed gap.
|
||||
#[test]
|
||||
fn null_ts_fill_is_well_formed_and_invisible_to_real_pids() {
|
||||
let mut unit = vec![0xAAu8; crate::aacs::ALIGNED_UNIT_LEN];
|
||||
crate::aacs::fill_null_ts_unit(&mut unit);
|
||||
// 32 packets, each sync 0x47, PID 0x1FFF, payload-only CC 0.
|
||||
// 32 packets, each: sync 0x47, PID 0x1FFF, adaptation-only (0b10) with a
|
||||
// discontinuity_indicator in the adaptation field.
|
||||
let mut off = 0;
|
||||
let mut pkts = 0;
|
||||
while off + 192 <= unit.len() {
|
||||
assert_eq!(unit[off + 4], 0x47);
|
||||
assert_eq!(unit[off + 4], 0x47, "sync");
|
||||
let pid = ((unit[off + 5] as u16 & 0x1F) << 8) | unit[off + 6] as u16;
|
||||
assert_eq!(pid, 0x1FFF, "null PID");
|
||||
assert_eq!(unit[off + 7] & 0x30, 0x10, "payload-only");
|
||||
assert_eq!(
|
||||
(unit[off + 7] >> 4) & 0x03,
|
||||
0x02,
|
||||
"adaptation_field_control = AF only (no payload)"
|
||||
);
|
||||
assert!(unit[off + 8] > 0, "adaptation_field_length > 0");
|
||||
assert_eq!(
|
||||
unit[off + 9] & 0x80,
|
||||
0x80,
|
||||
"adaptation-field discontinuity_indicator set (the B1 marker)"
|
||||
);
|
||||
off += 192;
|
||||
pkts += 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user