From 981f30b1b01d3cc5623efb4dc2c062b9ee7a6f39 Mon Sep 17 00:00:00 2001 From: Matt Jackson <1085847+MattJackson@users.noreply.github.com> Date: Sun, 19 Apr 2026 17:23:50 +0000 Subject: [PATCH] =?UTF-8?q?Move=20label=20generation=20to=20labels=20syste?= =?UTF-8?q?m=20=E2=80=94=20fill=5Fdefaults()=20for=20all=20stream=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/disc/mod.rs | 1 + src/labels/mod.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++ src/mux/mkv.rs | 61 ++----------------------------- 3 files changed, 95 insertions(+), 59 deletions(-) diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 99808a1..722949f 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1108,6 +1108,7 @@ impl Disc { // 4. Metadata + labels let meta_title = Self::read_meta_title(reader, &udf_fs); crate::labels::apply(reader, &udf_fs, &mut titles); + crate::labels::fill_defaults(&mut titles); // 5. Derive format, layers, region let format = Self::detect_format(&titles); diff --git a/src/labels/mod.rs b/src/labels/mod.rs index 50d5ea7..7b62417 100644 --- a/src/labels/mod.rs +++ b/src/labels/mod.rs @@ -138,6 +138,98 @@ pub fn apply(reader: &mut dyn SectorReader, udf: &UdfFs, titles: &mut [DiscTitle } } +/// Fill in default labels for any streams that don't have one. +/// Runs after BD-J label extraction — fills gaps with codec + channel descriptions. +/// This is the central place for all fallback label generation. +pub fn fill_defaults(titles: &mut [crate::disc::DiscTitle]) { + use crate::disc::{AudioChannels, Codec, HdrFormat, Stream}; + + for title in titles.iter_mut() { + for stream in &mut title.streams { + match stream { + Stream::Audio(a) if a.label.is_empty() => { + a.label = generate_audio_label(&a.codec, &a.channels, a.secondary); + } + Stream::Video(v) if v.label.is_empty() && v.secondary => { + // Label secondary video streams (DV EL, PiP) + v.label = "Dolby Vision EL".to_string(); + } + Stream::Video(v) if v.label.is_empty() => { + // Primary video: codec + resolution + HDR + let mut parts = vec![v.codec.name().to_string()]; + let (w, _) = v.resolution.pixels(); + if w >= 3840 { + parts.push("4K".into()); + } else if w >= 1920 { + parts.push("1080p".into()); + } else if w >= 1280 { + parts.push("720p".into()); + } + if v.hdr != HdrFormat::Sdr { + parts.push(v.hdr.name().to_string()); + } + v.label = parts.join(" "); + } + _ => {} + } + } + } +} + +fn generate_audio_label( + codec: &crate::disc::Codec, + channels: &crate::disc::AudioChannels, + secondary: bool, +) -> String { + use crate::disc::{AudioChannels, Codec}; + + let codec_name = match codec { + Codec::TrueHd => match channels { + AudioChannels::Surround71 => "Dolby TrueHD Atmos 7.1", + AudioChannels::Surround51 => "Dolby TrueHD 5.1", + _ => "Dolby TrueHD", + }, + Codec::Ac3 => "Dolby Digital", + Codec::Ac3Plus => "Dolby Digital Plus", + Codec::DtsHdMa => "DTS-HD Master Audio", + Codec::DtsHdHr => "DTS-HD High Resolution", + Codec::Dts => "DTS", + Codec::Lpcm => "LPCM", + Codec::Aac => "AAC", + Codec::Mp2 => "MPEG Audio", + Codec::Mp3 => "MP3", + Codec::Flac => "FLAC", + Codec::Opus => "Opus", + _ => return String::new(), + }; + + // TrueHD already includes channel info for 7.1/5.1 + let has_channels = matches!( + codec, + Codec::TrueHd if matches!(channels, AudioChannels::Surround71 | AudioChannels::Surround51) + ); + + let channel_str = if has_channels { + String::new() + } else { + match channels { + AudioChannels::Mono => " 1.0".into(), + AudioChannels::Stereo => " 2.0".into(), + AudioChannels::Stereo21 => " 2.1".into(), + AudioChannels::Quad => " 4.0".into(), + AudioChannels::Surround50 => " 5.0".into(), + AudioChannels::Surround51 => " 5.1".into(), + AudioChannels::Surround61 => " 6.1".into(), + AudioChannels::Surround71 => " 7.1".into(), + AudioChannels::Unknown => String::new(), + } + }; + + let suffix = if secondary { " (Secondary)" } else { "" }; + + format!("{}{}{}", codec_name, channel_str, suffix) +} + fn extract(reader: &mut dyn SectorReader, udf: &UdfFs) -> Vec { for (_name, detect, parse) in PARSERS { if detect(udf) { diff --git a/src/mux/mkv.rs b/src/mux/mkv.rs index 7eeedc1..92ccaaa 100644 --- a/src/mux/mkv.rs +++ b/src/mux/mkv.rs @@ -5,61 +5,9 @@ //! cues and seek head are finalized at the end. use super::ebml; -use crate::disc::{ - AudioChannels, AudioStream, Chapter, Codec, ColorSpace, HdrFormat, SubtitleStream, VideoStream, -}; +use crate::disc::{AudioStream, Chapter, Codec, ColorSpace, HdrFormat, SubtitleStream, VideoStream}; use std::io::{self, Seek, SeekFrom, Write}; -/// Generate a descriptive audio track label from codec and channel layout. -/// Used when the disc doesn't provide BD-J/CLPI labels. -fn generate_audio_label(codec: &Codec, channels: &AudioChannels, secondary: bool) -> String { - let codec_name = match codec { - Codec::TrueHd => match channels { - AudioChannels::Surround71 => "Dolby TrueHD Atmos 7.1", - AudioChannels::Surround51 => "Dolby TrueHD 5.1", - _ => "Dolby TrueHD", - }, - Codec::Ac3 => "Dolby Digital", - Codec::Ac3Plus => "Dolby Digital Plus", - Codec::DtsHdMa => "DTS-HD Master Audio", - Codec::DtsHdHr => "DTS-HD High Resolution", - Codec::Dts => "DTS", - Codec::Lpcm => "LPCM", - Codec::Aac => "AAC", - Codec::Mp2 => "MPEG Audio", - Codec::Mp3 => "MP3", - Codec::Flac => "FLAC", - Codec::Opus => "Opus", - _ => return String::new(), - }; - - // TrueHD already includes channel info in its name for 7.1/5.1 - let has_channels = matches!( - codec, - Codec::TrueHd if matches!(channels, AudioChannels::Surround71 | AudioChannels::Surround51) - ); - - let channel_str = if has_channels { - String::new() - } else { - match channels { - AudioChannels::Mono => " 1.0".into(), - AudioChannels::Stereo => " 2.0".into(), - AudioChannels::Stereo21 => " 2.1".into(), - AudioChannels::Quad => " 4.0".into(), - AudioChannels::Surround50 => " 5.0".into(), - AudioChannels::Surround51 => " 5.1".into(), - AudioChannels::Surround61 => " 6.1".into(), - AudioChannels::Surround71 => " 7.1".into(), - AudioChannels::Unknown => String::new(), - } - }; - - let suffix = if secondary { " (Secondary)" } else { "" }; - - format!("{}{}{}", codec_name, channel_str, suffix) -} - /// MKV track definition (built from disc stream metadata). pub struct MkvTrack { pub track_type: u64, // 1=video, 2=audio, 17=subtitle @@ -148,12 +96,7 @@ impl MkvTrack { let sr = a.sample_rate.hz(); let ch = a.channels.count(); - // Use disc label if available, otherwise generate from codec + channels - let name = if a.label.is_empty() { - generate_audio_label(&a.codec, &a.channels, a.secondary) - } else { - a.label.clone() - }; + let name = a.label.clone(); Self { track_type: ebml::TRACK_TYPE_AUDIO,