From 8ec71834dd64a7f4257e047864ff540db9c855a1 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:01:39 -0700 Subject: [PATCH] Stamp the last frames that were still leaving without provenance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A real rip caught these. The warning added for a track placed from timestamps under a seam plan fired once, on one subtitle track of a 23-clip title, eighteen minutes in — a display set that arrives as a lone non-PCS segment is emitted straight through rather than accumulated, and that path still built its frame with source: None. Auditing the rest the same way found four more: flac, lpcm, mpegaudio and the passthrough parser. All are one-PES-one-frame, so the packet's own facts are the unit's facts. Every Frame construction in every codec parser now carries a source, checked by walking balanced braces rather than by eye — the earlier count was taken with a regex that cannot see a block containing nested braces, which is how these survived the first pass. --- src/mux/codec/flac.rs | 2 +- src/mux/codec/lpcm.rs | 2 +- src/mux/codec/mod.rs | 2 +- src/mux/codec/mpegaudio.rs | 2 +- src/mux/codec/pgs.rs | 28 +++++++++++++++++++++++++++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/mux/codec/flac.rs b/src/mux/codec/flac.rs index b903859..43c1f3b 100644 --- a/src/mux/codec/flac.rs +++ b/src/mux/codec/flac.rs @@ -118,7 +118,7 @@ impl CodecParser for FlacParser { vec![Frame { discontinuity: pes.discontinuity, coding: None, - source: None, + source: super::pesbuf::PesFacts::of(pes).source, pts_ns, keyframe: true, data: pes.data.clone(), diff --git a/src/mux/codec/lpcm.rs b/src/mux/codec/lpcm.rs index fa53638..e1dfc38 100644 --- a/src/mux/codec/lpcm.rs +++ b/src/mux/codec/lpcm.rs @@ -74,7 +74,7 @@ impl CodecParser for LpcmParser { vec![Frame { discontinuity: false, coding: None, - source: None, + source: super::pesbuf::PesFacts::of(pes).source, pts_ns, keyframe: true, data: pes.data[offset..].to_vec(), diff --git a/src/mux/codec/mod.rs b/src/mux/codec/mod.rs index 1b9a3c6..5784352 100644 --- a/src/mux/codec/mod.rs +++ b/src/mux/codec/mod.rs @@ -154,7 +154,7 @@ impl CodecParser for PassthroughParser { // parsers must instead defer the flag to the next emitted frame.) vec![Frame { coding: None, - source: None, + source: pesbuf::PesFacts::of(pes).source, pts_ns, keyframe: self.keyframe, discontinuity: pes.discontinuity, diff --git a/src/mux/codec/mpegaudio.rs b/src/mux/codec/mpegaudio.rs index dfe429b..cab0677 100644 --- a/src/mux/codec/mpegaudio.rs +++ b/src/mux/codec/mpegaudio.rs @@ -116,7 +116,7 @@ impl CodecParser for MpegAudioParser { vec![Frame { discontinuity: pes.discontinuity, coding: None, - source: None, + source: super::pesbuf::PesFacts::of(pes).source, pts_ns, keyframe: true, data: pes.data.clone(), diff --git a/src/mux/codec/pgs.rs b/src/mux/codec/pgs.rs index 6e95d9b..880e2de 100644 --- a/src/mux/codec/pgs.rs +++ b/src/mux/codec/pgs.rs @@ -366,7 +366,10 @@ impl CodecParser for PgsParser { out.push(Frame { discontinuity: false, coding: None, - source: None, + // Emitted straight from THIS packet, so its facts are + // this packet's -- the same rule as a pending set, + // which takes the facts of the packet that opened it. + source: super::pesbuf::PesFacts::of(pes).source, pts_ns: pts.unwrap_or(0), keyframe: true, data: pes.data.clone(), @@ -914,4 +917,27 @@ mod tests { assert_eq!(t.facts().displays, u32::MAX); assert_eq!(t.facts().forced_displays, u32::MAX); } + + /// A lone non-PCS segment with a PTS is emitted straight through rather + /// than accumulated, and it must still carry provenance. This path was + /// missed on the first pass and showed up on a real disc as a subtitle + /// track with no source offset -- the one track out of forty that could + /// not be placed by byte. + #[test] + fn a_lone_segment_emitted_directly_still_carries_provenance() { + let mut parser = PgsParser::new(); + // A non-PCS segment (type 0x15 = ODS) with a PTS and no pending set. + let mut p = make_pes(vec![0x15, 0x00, 0x00, 0x00, 0x04, 1, 2, 3, 4], Some(90_000)); + p.source = Some(crate::pes::SourcePos::at_byte(7_777)); + let frames = parser.parse(&p); + assert!( + !frames.is_empty(), + "a lone segment with a PTS is passed through" + ); + assert_eq!( + frames[0].source.map(|s| s.byte), + Some(7_777), + "emitted straight from this packet, so it carries this packet's offset" + ); + } }