oximedia_proxy/generate/
optimizer.rs1use super::settings::ProxyGenerationSettings;
4use crate::Result;
5
6pub struct ProxyOptimizer {
8 target_size: Option<u64>,
10
11 target_bitrate: Option<u64>,
13
14 max_encoding_time: Option<f64>,
16}
17
18impl ProxyOptimizer {
19 #[must_use]
21 pub const fn new() -> Self {
22 Self {
23 target_size: None,
24 target_bitrate: None,
25 max_encoding_time: None,
26 }
27 }
28
29 #[must_use]
31 pub const fn with_target_size(mut self, size: u64) -> Self {
32 self.target_size = Some(size);
33 self
34 }
35
36 #[must_use]
38 pub const fn with_target_bitrate(mut self, bitrate: u64) -> Self {
39 self.target_bitrate = Some(bitrate);
40 self
41 }
42
43 #[must_use]
45 pub const fn with_max_encoding_time(mut self, time: f64) -> Self {
46 self.max_encoding_time = Some(time);
47 self
48 }
49
50 pub fn optimize(
52 &self,
53 base_settings: ProxyGenerationSettings,
54 input_duration: f64,
55 ) -> Result<ProxyGenerationSettings> {
56 let mut settings = base_settings;
57
58 if let Some(target_bitrate) = self.target_bitrate {
60 settings.bitrate = target_bitrate;
61 }
62
63 if let Some(target_size) = self.target_size {
65 let required_bitrate = (target_size as f64 * 8.0 / input_duration) as u64;
67
68 settings.bitrate = (required_bitrate as f64 * 0.9) as u64;
70 settings.audio_bitrate = (required_bitrate as f64 * 0.1) as u64;
71 }
72
73 if let Some(_max_time) = self.max_encoding_time {
75 settings.quality_preset = "ultrafast".to_string();
77 settings.threads = num_cpus();
78 }
79
80 settings.validate()?;
81 Ok(settings)
82 }
83
84 #[must_use]
86 pub fn estimate_output_size(&self, settings: &ProxyGenerationSettings, duration: f64) -> u64 {
87 let video_size = (settings.bitrate as f64 * duration / 8.0) as u64;
89
90 let audio_size = (settings.audio_bitrate as f64 * duration / 8.0) as u64;
92
93 let overhead = ((video_size + audio_size) as f64 * 0.05) as u64;
95
96 video_size + audio_size + overhead
97 }
98
99 #[must_use]
101 pub fn estimate_encoding_time(&self, settings: &ProxyGenerationSettings, duration: f64) -> f64 {
102 let base_speed = match settings.quality_preset.as_str() {
104 "ultrafast" => 10.0, "veryfast" => 5.0, "fast" => 3.0, "medium" => 1.5, "slow" => 0.5, _ => 1.0, };
111
112 let scale_adjustment = 1.0 / (settings.scale_factor as f64);
114
115 let thread_adjustment: f64 = if settings.threads == 0 {
117 num_cpus::get() as f64
118 } else {
119 settings.threads as f64
120 };
121
122 let denominator = base_speed * thread_adjustment / scale_adjustment;
124 duration / denominator
125 }
126}
127
128impl Default for ProxyOptimizer {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134#[allow(dead_code)]
135fn num_cpus() -> u32 {
136 std::thread::available_parallelism()
137 .map(|n| n.get() as u32)
138 .unwrap_or(1)
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_optimizer_creation() {
147 let optimizer = ProxyOptimizer::new()
148 .with_target_size(100_000_000)
149 .with_target_bitrate(5_000_000);
150
151 assert_eq!(optimizer.target_size, Some(100_000_000));
152 assert_eq!(optimizer.target_bitrate, Some(5_000_000));
153 }
154
155 #[test]
156 fn test_optimize_for_size() {
157 let optimizer = ProxyOptimizer::new().with_target_size(50_000_000); let base_settings = ProxyGenerationSettings::quarter_res_h264();
160 let optimized = optimizer
161 .optimize(base_settings, 60.0)
162 .expect("should succeed in test");
163
164 assert!(optimized.bitrate > 0);
166 }
167
168 #[test]
169 fn test_estimate_output_size() {
170 let optimizer = ProxyOptimizer::new();
171 let settings = ProxyGenerationSettings::quarter_res_h264();
172
173 let estimated_size = optimizer.estimate_output_size(&settings, 60.0);
174 assert!(estimated_size > 0);
175
176 assert!(estimated_size > 10_000_000);
179 assert!(estimated_size < 20_000_000);
180 }
181
182 #[test]
183 fn test_estimate_encoding_time() {
184 let optimizer = ProxyOptimizer::new();
185 let settings = ProxyGenerationSettings::quarter_res_h264();
186
187 let estimated_time = optimizer.estimate_encoding_time(&settings, 60.0);
188 assert!(estimated_time > 0.0);
189
190 assert!(estimated_time < 60.0);
192 }
193
194 #[test]
195 fn test_optimize_for_time() {
196 let optimizer = ProxyOptimizer::new().with_max_encoding_time(10.0);
197
198 let base_settings = ProxyGenerationSettings::quarter_res_h264();
199 let optimized = optimizer
200 .optimize(base_settings, 60.0)
201 .expect("should succeed in test");
202
203 assert_eq!(optimized.quality_preset, "ultrafast");
205 assert!(optimized.threads > 0);
206 }
207}