1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum QualityMode {
8 Low,
10 Medium,
12 High,
14 VeryHigh,
16 Custom,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22pub enum QualityPreset {
23 UltraFast,
25 SuperFast,
27 VeryFast,
29 Fast,
31 Medium,
33 Slow,
35 VerySlow,
37 Placebo,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43pub enum RateControlMode {
44 Crf(u8),
50
51 Cbr(u64),
57
58 Vbr {
63 target: u64,
65 max: u64,
67 },
68
69 ConstrainedVbr {
73 target: u64,
75 max: u64,
77 buffer_size: u64,
79 },
80
81 Abr(u64),
85}
86
87#[derive(Debug, Clone)]
89pub struct QualityConfig {
90 pub preset: QualityPreset,
92 pub rate_control: RateControlMode,
94 pub two_pass: bool,
96 pub lookahead: Option<u32>,
98 pub tune: Option<TuneMode>,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104pub enum TuneMode {
105 Film,
107 Animation,
109 Grain,
111 StillImage,
113 FastDecode,
115 ZeroLatency,
117 Psnr,
119 Ssim,
121}
122
123impl QualityMode {
124 #[must_use]
134 pub fn to_crf(self) -> u8 {
135 match self {
136 Self::Low => 28,
137 Self::Medium => 23,
138 Self::High => 20,
139 Self::VeryHigh => 18,
140 Self::Custom => 23, }
142 }
143
144 #[must_use]
146 pub fn to_preset(self) -> QualityPreset {
147 match self {
148 Self::Low => QualityPreset::VeryFast,
149 Self::Medium => QualityPreset::Medium,
150 Self::High => QualityPreset::Slow,
151 Self::VeryHigh => QualityPreset::VerySlow,
152 Self::Custom => QualityPreset::Medium,
153 }
154 }
155
156 #[must_use]
166 pub fn speed_factor(self) -> f64 {
167 match self {
168 Self::Low => 5.0,
169 Self::Medium => 1.5,
170 Self::High => 0.5,
171 Self::VeryHigh => 0.2,
172 Self::Custom => 1.0,
173 }
174 }
175}
176
177impl QualityPreset {
178 #[must_use]
180 pub fn as_str(self) -> &'static str {
181 match self {
182 Self::UltraFast => "ultrafast",
183 Self::SuperFast => "superfast",
184 Self::VeryFast => "veryfast",
185 Self::Fast => "fast",
186 Self::Medium => "medium",
187 Self::Slow => "slow",
188 Self::VerySlow => "veryslow",
189 Self::Placebo => "placebo",
190 }
191 }
192
193 #[must_use]
195 pub fn cpu_used(self) -> u8 {
196 match self {
197 Self::UltraFast => 8,
198 Self::SuperFast => 7,
199 Self::VeryFast => 6,
200 Self::Fast => 5,
201 Self::Medium => 4,
202 Self::Slow => 2,
203 Self::VerySlow => 1,
204 Self::Placebo => 0,
205 }
206 }
207}
208
209impl RateControlMode {
210 #[must_use]
212 pub fn target_bitrate(&self) -> Option<u64> {
213 match self {
214 Self::Crf(_) => None,
215 Self::Cbr(bitrate) | Self::Abr(bitrate) => Some(*bitrate),
216 Self::Vbr { target, .. } | Self::ConstrainedVbr { target, .. } => Some(*target),
217 }
218 }
219
220 #[must_use]
222 pub fn max_bitrate(&self) -> Option<u64> {
223 match self {
224 Self::Crf(_) | Self::Cbr(_) | Self::Abr(_) => None,
225 Self::Vbr { max, .. } | Self::ConstrainedVbr { max, .. } => Some(*max),
226 }
227 }
228
229 #[must_use]
231 pub fn is_constant_quality(&self) -> bool {
232 matches!(self, Self::Crf(_))
233 }
234
235 #[must_use]
237 pub fn is_bitrate_mode(&self) -> bool {
238 !self.is_constant_quality()
239 }
240}
241
242impl Default for QualityConfig {
243 fn default() -> Self {
244 Self {
245 preset: QualityPreset::Medium,
246 rate_control: RateControlMode::Crf(23),
247 two_pass: false,
248 lookahead: Some(40),
249 tune: None,
250 }
251 }
252}
253
254impl TuneMode {
255 #[must_use]
257 pub fn as_str(self) -> &'static str {
258 match self {
259 Self::Film => "film",
260 Self::Animation => "animation",
261 Self::Grain => "grain",
262 Self::StillImage => "stillimage",
263 Self::FastDecode => "fastdecode",
264 Self::ZeroLatency => "zerolatency",
265 Self::Psnr => "psnr",
266 Self::Ssim => "ssim",
267 }
268 }
269}
270
271#[allow(dead_code)]
273pub struct QualityConfigBuilder {
274 config: QualityConfig,
275}
276
277#[allow(dead_code)]
278impl QualityConfigBuilder {
279 #[must_use]
281 pub fn new() -> Self {
282 Self {
283 config: QualityConfig::default(),
284 }
285 }
286
287 #[must_use]
289 pub fn preset(mut self, preset: QualityPreset) -> Self {
290 self.config.preset = preset;
291 self
292 }
293
294 #[must_use]
296 pub fn rate_control(mut self, mode: RateControlMode) -> Self {
297 self.config.rate_control = mode;
298 self
299 }
300
301 #[must_use]
303 pub fn two_pass(mut self, enable: bool) -> Self {
304 self.config.two_pass = enable;
305 self
306 }
307
308 #[must_use]
310 pub fn lookahead(mut self, frames: u32) -> Self {
311 self.config.lookahead = Some(frames);
312 self
313 }
314
315 #[must_use]
317 pub fn tune(mut self, mode: TuneMode) -> Self {
318 self.config.tune = Some(mode);
319 self
320 }
321
322 #[must_use]
324 pub fn build(self) -> QualityConfig {
325 self.config
326 }
327}
328
329impl Default for QualityConfigBuilder {
330 fn default() -> Self {
331 Self::new()
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn test_quality_mode_crf() {
341 assert_eq!(QualityMode::Low.to_crf(), 28);
342 assert_eq!(QualityMode::Medium.to_crf(), 23);
343 assert_eq!(QualityMode::High.to_crf(), 20);
344 assert_eq!(QualityMode::VeryHigh.to_crf(), 18);
345 }
346
347 #[test]
348 fn test_quality_preset_str() {
349 assert_eq!(QualityPreset::UltraFast.as_str(), "ultrafast");
350 assert_eq!(QualityPreset::Medium.as_str(), "medium");
351 assert_eq!(QualityPreset::VerySlow.as_str(), "veryslow");
352 }
353
354 #[test]
355 fn test_quality_preset_cpu_used() {
356 assert_eq!(QualityPreset::UltraFast.cpu_used(), 8);
357 assert_eq!(QualityPreset::Medium.cpu_used(), 4);
358 assert_eq!(QualityPreset::Placebo.cpu_used(), 0);
359 }
360
361 #[test]
362 fn test_rate_control_crf() {
363 let crf = RateControlMode::Crf(23);
364 assert!(crf.is_constant_quality());
365 assert!(!crf.is_bitrate_mode());
366 assert_eq!(crf.target_bitrate(), None);
367 assert_eq!(crf.max_bitrate(), None);
368 }
369
370 #[test]
371 fn test_rate_control_cbr() {
372 let cbr = RateControlMode::Cbr(5_000_000);
373 assert!(!cbr.is_constant_quality());
374 assert!(cbr.is_bitrate_mode());
375 assert_eq!(cbr.target_bitrate(), Some(5_000_000));
376 assert_eq!(cbr.max_bitrate(), None);
377 }
378
379 #[test]
380 fn test_rate_control_vbr() {
381 let vbr = RateControlMode::Vbr {
382 target: 5_000_000,
383 max: 8_000_000,
384 };
385 assert!(!vbr.is_constant_quality());
386 assert!(vbr.is_bitrate_mode());
387 assert_eq!(vbr.target_bitrate(), Some(5_000_000));
388 assert_eq!(vbr.max_bitrate(), Some(8_000_000));
389 }
390
391 #[test]
392 fn test_rate_control_constrained_vbr() {
393 let cvbr = RateControlMode::ConstrainedVbr {
394 target: 5_000_000,
395 max: 8_000_000,
396 buffer_size: 10_000_000,
397 };
398 assert_eq!(cvbr.target_bitrate(), Some(5_000_000));
399 assert_eq!(cvbr.max_bitrate(), Some(8_000_000));
400 }
401
402 #[test]
403 fn test_tune_mode_str() {
404 assert_eq!(TuneMode::Film.as_str(), "film");
405 assert_eq!(TuneMode::Animation.as_str(), "animation");
406 assert_eq!(TuneMode::ZeroLatency.as_str(), "zerolatency");
407 }
408
409 #[test]
410 fn test_quality_config_builder() {
411 let config = QualityConfigBuilder::new()
412 .preset(QualityPreset::Slow)
413 .rate_control(RateControlMode::Crf(20))
414 .two_pass(true)
415 .lookahead(60)
416 .tune(TuneMode::Film)
417 .build();
418
419 assert_eq!(config.preset, QualityPreset::Slow);
420 assert_eq!(config.rate_control, RateControlMode::Crf(20));
421 assert!(config.two_pass);
422 assert_eq!(config.lookahead, Some(60));
423 assert_eq!(config.tune, Some(TuneMode::Film));
424 }
425
426 #[test]
427 fn test_quality_mode_speed_factor() {
428 assert_eq!(QualityMode::Low.speed_factor(), 5.0);
429 assert_eq!(QualityMode::Medium.speed_factor(), 1.5);
430 assert_eq!(QualityMode::High.speed_factor(), 0.5);
431 assert_eq!(QualityMode::VeryHigh.speed_factor(), 0.2);
432 }
433}