Chapters, DVD subtitle palette, MKV track flags, progress total_bytes

Chapters:
- MPLS PlayList marks parsed (mark_type 1 = chapter)
- Chapter struct on DiscTitle (time_secs, name)
- MKV Chapters element with EditionEntry/ChapterAtom per mark
- 3 MPLS mark tests + 2 MKV chapter tests

DVD subtitle palette:
- IFO palette extraction (PGC offset 0xA4, 16 × YCbCr colors)
- YCbCr→RGB conversion for VobSub .idx format
- DvdSubParser codec_private returns formatted palette
- codec_data field on SubtitleStream flows through pipeline
- 5 palette tests (YCbCr conversion, formatting, overflow)

MKV track flags:
- FlagDefault: primary video/audio = 1, secondary = 0
- FlagForced: forced subtitles = 1
- Language: set from stream language code
- Already implemented, verified with 4 new tests

Progress total_bytes:
- IOStream trait: total_bytes() -> Option<u64>
- DiscStream, IsoStream: from disc_title.size_bytes
- M2tsStream, MkvStream: from file metadata on open
- NetworkStream, StdioStream, NullStream: None

316 tests total, all passing.
This commit is contained in:
MattJackson
2026-04-11 17:43:47 +00:00
parent cd575b7221
commit 96a65de3ff
21 changed files with 690 additions and 12 deletions
+19
View File
@@ -128,6 +128,7 @@ impl Disc {
codec,
language: s.language.clone(),
forced: false,
codec_data: None,
}))
} else {
Some(Stream::Audio(AudioStream {
@@ -146,6 +147,7 @@ impl Disc {
codec,
language: s.language.clone(),
forced: false,
codec_data: None,
})),
// Stream type 4 = IG, unknown types -- skip
_ => None,
@@ -153,6 +155,22 @@ impl Disc {
})
.collect();
// Convert marks to chapters (filter mark_type == 1 = chapter entry)
let first_in_time = parsed.play_items.first().map(|pi| pi.in_time).unwrap_or(0);
let chapters: Vec<Chapter> = parsed
.marks
.iter()
.filter(|m| m.mark_type == 1)
.enumerate()
.map(|(i, m)| {
let time_secs = (m.timestamp as f64 - first_in_time as f64) / 45000.0;
Chapter {
time_secs: if time_secs < 0.0 { 0.0 } else { time_secs },
name: format!("Chapter {}", i + 1),
}
})
.collect();
let playlist_num = filename.trim_end_matches(".mpls").trim_end_matches(".MPLS");
let playlist_id = playlist_num.parse::<u16>().unwrap_or(0);
@@ -163,6 +181,7 @@ impl Disc {
size_bytes: total_size,
clips,
streams,
chapters,
extents,
content_format: ContentFormat::BdTs,
})