Skip to main content

viser_encoding/
lib.rs

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/// Common encoding parameters shared across all optimization modes.
11#[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    /// Returns the actual parallelism to use.
59    /// If parallel is 0, uses num_cpus/2 with a floor of 2.
60    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
69/// Maps a generic preset name to codec-specific presets.
70pub 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}