Skip to main content

deepshrink_core/
options.rs

1//! Encoding option enums shared between the CLI and the engine.
2//!
3//! These are the decoded, engine-facing forms of the user's flags (the CLI maps
4//! its clap types onto these). Keeping them here lets `plan` stay a pure
5//! function over well-typed inputs.
6
7/// Video codec choice.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum VideoCodec {
10    H264,
11    H265,
12}
13
14impl VideoCodec {
15    /// The libav encoder name passed to `ffmpeg -c:v`.
16    pub fn encoder(self) -> &'static str {
17        match self {
18            VideoCodec::H264 => "libx264",
19            VideoCodec::H265 => "libx265",
20        }
21    }
22
23    /// The `-tag:v` value needed for MP4 compatibility, if any.
24    pub fn mp4_tag(self) -> Option<&'static str> {
25        match self {
26            VideoCodec::H264 => None,
27            // Without hvc1, HEVC in MP4 won't play in QuickTime/Safari.
28            VideoCodec::H265 => Some("hvc1"),
29        }
30    }
31
32    /// Human-readable label for output.
33    pub fn label(self) -> &'static str {
34        match self {
35            VideoCodec::H264 => "H.264",
36            VideoCodec::H265 => "H.265",
37        }
38    }
39
40    /// Inclusive CRF range to search when targeting a VMAF score, best quality
41    /// (lowest CRF) first. x265's CRF scale is shifted ~+6 vs x264 for the same
42    /// perceptual quality, so the bounds differ per codec.
43    pub fn crf_search_bounds(self) -> (u8, u8) {
44        match self {
45            VideoCodec::H264 => (18, 32),
46            VideoCodec::H265 => (22, 36),
47        }
48    }
49}
50
51/// Audio codec choice (also used for pure-audio in session 003).
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum AudioCodec {
54    Aac,
55    Opus,
56    Mp3,
57}
58
59impl AudioCodec {
60    pub fn encoder(self) -> &'static str {
61        match self {
62            AudioCodec::Aac => "aac",
63            AudioCodec::Opus => "libopus",
64            AudioCodec::Mp3 => "libmp3lame",
65        }
66    }
67
68    /// Output file extension for a pure-audio result.
69    pub fn extension(self) -> &'static str {
70        match self {
71            AudioCodec::Aac => "m4a",
72            AudioCodec::Opus => "opus",
73            AudioCodec::Mp3 => "mp3",
74        }
75    }
76
77    /// Human-readable label for output.
78    pub fn label(self) -> &'static str {
79        match self {
80            AudioCodec::Aac => "AAC",
81            AudioCodec::Opus => "Opus",
82            AudioCodec::Mp3 => "MP3",
83        }
84    }
85}
86
87/// What to do with the audio track inside a video.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum AudioChoice {
90    /// Keep the track, re-encoding at a sensible (budget-aware) bitrate.
91    Keep,
92    /// Keep the track at an explicit bitrate in bits/s.
93    Bitrate(u64),
94    /// Drop the audio entirely.
95    Drop,
96}
97
98/// Target resolution.
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum ResolutionOpt {
101    /// Let the engine pick (downscale only if bitrate is too low).
102    Auto,
103    /// Force a target height (width follows aspect ratio).
104    Height(u32),
105}
106
107/// Frame-rate cap.
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum FpsOpt {
110    Auto,
111    Cap(u32),
112}
113
114/// Speed/quality trade-off, mapped to the encoder preset.
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum QualityPreset {
117    Fast,
118    Balanced,
119    Max,
120}
121
122impl QualityPreset {
123    /// x264/x265 `-preset` value.
124    pub fn encoder_preset(self) -> &'static str {
125        match self {
126            QualityPreset::Fast => "veryfast",
127            QualityPreset::Balanced => "medium",
128            QualityPreset::Max => "slow",
129        }
130    }
131
132    /// Default quality-mode CRF for a codec. x265 needs a higher CRF than x264
133    /// for comparable quality, so the numbers are codec-specific. These defaults
134    /// aim for roughly VMAF ~93 (visually near-transparent) on typical content.
135    pub fn default_crf(self, codec: VideoCodec) -> u8 {
136        match codec {
137            VideoCodec::H264 => match self {
138                QualityPreset::Fast => 25,
139                QualityPreset::Balanced => 23,
140                QualityPreset::Max => 20,
141            },
142            VideoCodec::H265 => match self {
143                QualityPreset::Fast => 30,
144                QualityPreset::Balanced => 28,
145                QualityPreset::Max => 24,
146            },
147        }
148    }
149}