Skip to main content

viser_ffmpeg/
lib.rs

1mod cache;
2mod encode;
3mod path;
4mod probe;
5
6pub use cache::*;
7pub use encode::*;
8pub use path::*;
9pub use probe::*;
10
11use serde::{Deserialize, Serialize};
12use std::fmt;
13
14/// Supported video codec.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub enum Codec {
17    #[serde(rename = "libx264")]
18    X264,
19    #[serde(rename = "libx265")]
20    X265,
21    #[serde(rename = "libsvtav1")]
22    SvtAv1,
23}
24
25impl Codec {
26    pub fn as_str(&self) -> &'static str {
27        match self {
28            Codec::X264 => "libx264",
29            Codec::X265 => "libx265",
30            Codec::SvtAv1 => "libsvtav1",
31        }
32    }
33}
34
35impl fmt::Display for Codec {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.write_str(self.as_str())
38    }
39}
40
41impl std::str::FromStr for Codec {
42    type Err = anyhow::Error;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        match s {
46            "libx264" | "x264" | "h264" => Ok(Codec::X264),
47            "libx265" | "x265" | "h265" | "hevc" => Ok(Codec::X265),
48            "libsvtav1" | "svtav1" | "av1" => Ok(Codec::SvtAv1),
49            _ => Err(anyhow::anyhow!("unknown codec: {s}")),
50        }
51    }
52}
53
54/// Video resolution.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
56pub struct Resolution {
57    pub width: i32,
58    pub height: i32,
59}
60
61impl Resolution {
62    pub const fn new(width: i32, height: i32) -> Self {
63        Self { width, height }
64    }
65
66    /// Human-friendly label like "1080p", "720p", etc.
67    pub fn label(&self) -> String {
68        match self.height {
69            h if h >= 2160 => "2160p".into(),
70            h if h >= 1440 => "1440p".into(),
71            h if h >= 1080 => "1080p".into(),
72            h if h >= 720 => "720p".into(),
73            h if h >= 480 => "480p".into(),
74            h if h >= 360 => "360p".into(),
75            h if h >= 240 => "240p".into(),
76            h => format!("{h}p"),
77        }
78    }
79}
80
81impl fmt::Display for Resolution {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(f, "{}x{}", self.width, self.height)
84    }
85}
86
87impl std::str::FromStr for Resolution {
88    type Err = anyhow::Error;
89
90    fn from_str(s: &str) -> Result<Self, Self::Err> {
91        match s.to_lowercase().as_str() {
92            "2160p" | "4k" => Ok(RES_2160P),
93            "1440p" => Ok(RES_1440P),
94            "1080p" => Ok(RES_1080P),
95            "720p" => Ok(RES_720P),
96            "480p" => Ok(RES_480P),
97            "360p" => Ok(RES_360P),
98            "240p" => Ok(RES_240P),
99            other => {
100                if let Some((w, h)) = other.split_once('x') {
101                    Ok(Resolution::new(w.parse()?, h.parse()?))
102                } else {
103                    Err(anyhow::anyhow!("invalid resolution: {other}"))
104                }
105            }
106        }
107    }
108}
109
110/// Common resolutions (16:9 aspect ratio).
111pub const RES_2160P: Resolution = Resolution::new(3840, 2160);
112pub const RES_1440P: Resolution = Resolution::new(2560, 1440);
113pub const RES_1080P: Resolution = Resolution::new(1920, 1080);
114pub const RES_720P: Resolution = Resolution::new(1280, 720);
115pub const RES_480P: Resolution = Resolution::new(854, 480);
116pub const RES_360P: Resolution = Resolution::new(640, 360);
117pub const RES_240P: Resolution = Resolution::new(426, 240);
118
119/// Rate control mode for encoding.
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
121#[serde(rename_all = "lowercase")]
122pub enum RateControlMode {
123    /// Constant rate factor (default).
124    #[default]
125    Crf,
126    /// Fixed quantizer (Netflix-style, no R-D optimization).
127    Qp,
128    /// 2-pass variable bitrate (for final delivery encodes).
129    Vbr,
130}