1mod cleanup;
2mod progress;
3
4pub use cleanup::*;
5pub use progress::*;
6
7use serde::{Deserialize, Serialize};
8use viser_ffmpeg::{Codec, RES_480P, RES_720P, RES_1080P, RateControlMode, Resolution};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct Config {
13 pub resolutions: Vec<Resolution>,
14 pub crf_values: Vec<i32>,
15 pub codecs: Vec<Codec>,
16 pub preset: String,
17 pub subsample: i32,
18 pub parallel: i32,
19 pub rate_control: RateControlMode,
20}
21
22impl Default for Config {
23 fn default() -> Self {
24 Self {
25 resolutions: vec![RES_480P, RES_720P, RES_1080P],
26 crf_values: vec![18, 22, 26, 30, 34, 38, 42],
27 codecs: vec![Codec::X264],
28 preset: "veryfast".into(),
29 subsample: 5,
30 parallel: 0,
31 rate_control: RateControlMode::Crf,
32 }
33 }
34}
35
36impl Config {
37 pub fn validate(&self) -> anyhow::Result<()> {
38 if self.resolutions.is_empty() {
39 anyhow::bail!("must specify at least one resolution");
40 }
41 if self.crf_values.is_empty() {
42 anyhow::bail!("must specify at least one CRF value");
43 }
44 if self.codecs.is_empty() {
45 anyhow::bail!("must specify at least one codec");
46 }
47 for codec in &self.codecs {
48 match codec {
49 Codec::X264 | Codec::X265 | Codec::SvtAv1 => {}
50 }
51 }
52 if self.subsample < 0 {
53 anyhow::bail!("subsample must be >= 0, got {}", self.subsample);
54 }
55 Ok(())
56 }
57
58 pub fn effective_parallel(&self) -> usize {
61 if self.parallel > 0 {
62 return self.parallel as usize;
63 }
64 let p = num_cpus() / 2;
65 p.max(2)
66 }
67}
68
69pub fn preset_for_codec(codec: Codec, preset: &str) -> String {
71 if codec != Codec::SvtAv1 {
72 return preset.to_string();
73 }
74 match preset {
75 "ultrafast" => "12",
76 "superfast" => "11",
77 "veryfast" => "10",
78 "faster" => "9",
79 "fast" => "8",
80 "medium" => "6",
81 "slow" => "4",
82 "slower" => "2",
83 "veryslow" => "0",
84 other => return other.to_string(),
85 }
86 .to_string()
87}
88
89fn num_cpus() -> usize {
90 std::thread::available_parallelism().map(|n| n.get()).unwrap_or(4)
91}