mux: fix DVD subtitle/audio track collision, PGS/VobSub flush, unify TS codec table
Subtitle/DVD output-corruption + stream-mapping coverage fixes. 1. DVD subtitle/audio track-mapping collision (CRITICAL). The PS path routed 0xBD private-stream packets to a track via (sub_id & 0x1F)+1, so VobSub subtitle sub-id 0x20+j aliased audio track j+1: subtitle PES was fed to the AC-3 parser and the real subtitle track got nothing. Route by the canonical DVD PID instead via a new PsPacket::dvd_pid() that mirrors scan_dvd_titles' PID assignment (video 0xE0, audio 0xBD00+i, subtitle 0x20+j), then look up the track in pid_to_track. Fixed identically at all three sites (pipelined_stream consume_ps, disc.rs live feed, disc.rs EOF flush). Unmappable/unmapped packets now WARN instead of silently dropping. 2. PGS flush() missing. PgsParser inherited the no-op default flush, so the last subtitle of every PGS track (emitted only when a following PCS arrives) was dropped at EOF. Implemented flush() to drain the pending display set (duration_ns: None for the trailing block). 3. DVD VobSub multi-PES SPU not reassembled. A subpicture unit larger than one PES spans multiple PES (only the head carries a PTS). DvdSubParser is now stateful: it buffers per sub-stream until the leading 2-byte SPU_size is satisfied, inherits the head PTS, and emits one Frame. flush() drains a truncated trailing SPU at EOF. 4. One-table hygiene. scan_streams had a duplicate stream_type->Codec table that had drifted from Codec::from_coding_type (missing 0x80 LPCM, 0x85 mapped to DTS-HD MA vs HR, etc.). scan_streams now uses from_coding_type plus a new Codec::kind()/CodecKind category split, so the two mappings can never diverge. Silent drops in scan_streams and bluray STN parsing now WARN with PID + type. Tests: dvd_pid mapping + subtitle/audio collision regression, PGS final-subtitle flush, VobSub multi-PES reassembly + EOF flush, scan_streams 0x80 LPCM via from_coding_type.
This commit is contained in:
+24
-17
@@ -124,24 +124,31 @@ impl PipelinedPesStream {
|
||||
|
||||
fn consume_ps(&mut self, packets: Vec<super::ps::PsPacket>) {
|
||||
for ps in packets {
|
||||
let track = match ps.stream_id {
|
||||
0xE0..=0xEF => 0,
|
||||
0xC0..=0xDF => 1,
|
||||
0xBD => ps
|
||||
.sub_stream_id
|
||||
.map(|s| (s & 0x1F) as usize + 1)
|
||||
.unwrap_or(1),
|
||||
_ => continue,
|
||||
};
|
||||
if track >= self.title.streams.len() {
|
||||
// Route by the REAL DVD PID (matching the PIDs that
|
||||
// `scan_dvd_titles` assigns) rather than a synthetic track
|
||||
// index. The old `(sub_id & 0x1F) + 1` heuristic collided
|
||||
// subtitle sub-id 0x20+j with audio track j+1, feeding
|
||||
// VobSub PES into the AC-3 parser.
|
||||
let Some(pid) = ps.dvd_pid() else {
|
||||
tracing::warn!(
|
||||
target: "mux",
|
||||
"dropping unmappable PS packet (stream_id={:#04x}, sub_stream_id={:?})",
|
||||
ps.stream_id,
|
||||
ps.sub_stream_id,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let pid = self
|
||||
.pid_to_track
|
||||
.iter()
|
||||
.find(|(_, idx)| *idx == track)
|
||||
.map(|(p, _)| *p)
|
||||
.unwrap_or(0);
|
||||
};
|
||||
let Some((_, track)) = self.pid_to_track.iter().find(|(p, _)| *p == pid).copied()
|
||||
else {
|
||||
tracing::warn!(
|
||||
target: "mux",
|
||||
"dropping PS packet for unmapped PID {:#06x} (stream_id={:#04x}, sub_stream_id={:?})",
|
||||
pid,
|
||||
ps.stream_id,
|
||||
ps.sub_stream_id,
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let pes = PesPacket {
|
||||
pid,
|
||||
pts: ps.pts.map(|p| p as i64),
|
||||
|
||||
Reference in New Issue
Block a user