oximedia_proxy/generate/
encoder.rs1use super::settings::ProxyGenerationSettings;
4use crate::{ProxyError, Result};
5use std::path::Path;
6
7pub struct ProxyEncoder {
9 settings: ProxyGenerationSettings,
10}
11
12impl ProxyEncoder {
13 pub fn new(settings: ProxyGenerationSettings) -> Result<Self> {
15 settings.validate()?;
16 Ok(Self { settings })
17 }
18
19 pub async fn encode(&self, input: &Path, output: &Path) -> Result<ProxyEncodeResult> {
28 if !input.exists() {
30 return Err(ProxyError::FileNotFound(input.display().to_string()));
31 }
32
33 if let Some(parent) = output.parent() {
35 std::fs::create_dir_all(parent)?;
36 }
37
38 tracing::info!(
43 "Encoding proxy: {} -> {} (scale: {}, codec: {})",
44 input.display(),
45 output.display(),
46 self.settings.scale_factor,
47 self.settings.codec
48 );
49
50 Ok(ProxyEncodeResult {
51 output_path: output.to_path_buf(),
52 file_size: 0,
53 duration: 0.0,
54 bitrate: self.settings.bitrate,
55 codec: self.settings.codec.clone(),
56 resolution: (0, 0),
57 encoding_time: 0.0,
58 })
59 }
60
61 #[must_use]
63 pub fn settings(&self) -> &ProxyGenerationSettings {
64 &self.settings
65 }
66
67 pub fn set_settings(&mut self, settings: ProxyGenerationSettings) -> Result<()> {
69 settings.validate()?;
70 self.settings = settings;
71 Ok(())
72 }
73
74 #[must_use]
76 pub fn calculate_target_resolution(&self, input_width: u32, input_height: u32) -> (u32, u32) {
77 let target_width = ((input_width as f32) * self.settings.scale_factor) as u32;
78 let target_height = ((input_height as f32) * self.settings.scale_factor) as u32;
79
80 let target_width = target_width & !1;
82 let target_height = target_height & !1;
83
84 (target_width, target_height)
85 }
86}
87
88#[derive(Debug, Clone)]
90pub struct ProxyEncodeResult {
91 pub output_path: std::path::PathBuf,
93
94 pub file_size: u64,
96
97 pub duration: f64,
99
100 pub bitrate: u64,
102
103 pub codec: String,
105
106 pub resolution: (u32, u32),
108
109 pub encoding_time: f64,
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_encoder_creation() {
119 let settings = ProxyGenerationSettings::quarter_res_h264();
120 let encoder = ProxyEncoder::new(settings);
121 assert!(encoder.is_ok());
122 }
123
124 #[test]
125 fn test_invalid_settings() {
126 let mut settings = ProxyGenerationSettings::default();
127 settings.scale_factor = 0.0;
128 let encoder = ProxyEncoder::new(settings);
129 assert!(encoder.is_err());
130 }
131
132 #[test]
133 fn test_resolution_calculation() {
134 let settings = ProxyGenerationSettings::quarter_res_h264();
135 let encoder = ProxyEncoder::new(settings).expect("should succeed in test");
136
137 let (width, height) = encoder.calculate_target_resolution(1920, 1080);
138 assert_eq!(width, 480);
139 assert_eq!(height, 270);
140
141 let (width, height) = encoder.calculate_target_resolution(3840, 2160);
142 assert_eq!(width, 960);
143 assert_eq!(height, 540);
144 }
145
146 #[test]
147 fn test_resolution_even_alignment() {
148 let settings = ProxyGenerationSettings::default().with_scale_factor(0.3);
149 let encoder = ProxyEncoder::new(settings).expect("should succeed in test");
150
151 let (width, height) = encoder.calculate_target_resolution(1920, 1080);
152 assert_eq!(width % 2, 0);
154 assert_eq!(height % 2, 0);
155 }
156}