oximedia_proxy/
proxy_quality.rs1#![allow(dead_code)]
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub enum ProxyQualityTier {
11 Draft,
13 Review,
15 Delivery,
17}
18
19impl ProxyQualityTier {
20 pub fn resolution_cap(self) -> (u32, u32) {
22 match self {
23 Self::Draft => (640, 360),
24 Self::Review => (1280, 720),
25 Self::Delivery => (1920, 1080),
26 }
27 }
28
29 pub fn label(self) -> &'static str {
31 match self {
32 Self::Draft => "Draft",
33 Self::Review => "Review",
34 Self::Delivery => "Delivery",
35 }
36 }
37
38 pub fn upgrade(self) -> Option<Self> {
40 match self {
41 Self::Draft => Some(Self::Review),
42 Self::Review => Some(Self::Delivery),
43 Self::Delivery => None,
44 }
45 }
46
47 pub fn downgrade(self) -> Option<Self> {
49 match self {
50 Self::Draft => None,
51 Self::Review => Some(Self::Draft),
52 Self::Delivery => Some(Self::Review),
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct ProxyQualityConfig {
60 pub tier: ProxyQualityTier,
62 pub video_bitrate_kbps: u32,
64 pub audio_bitrate_kbps: u32,
66 pub codec_hint: String,
68 pub fps_cap: f32,
70}
71
72impl ProxyQualityConfig {
73 pub fn new(tier: ProxyQualityTier) -> Self {
75 let (video_bitrate_kbps, audio_bitrate_kbps, codec_hint) = match tier {
76 ProxyQualityTier::Draft => (500, 64, "h264"),
77 ProxyQualityTier::Review => (2000, 128, "h264"),
78 ProxyQualityTier::Delivery => (8000, 320, "h264"),
79 };
80 Self {
81 tier,
82 video_bitrate_kbps,
83 audio_bitrate_kbps,
84 codec_hint: codec_hint.to_string(),
85 fps_cap: 0.0,
86 }
87 }
88
89 pub fn bitrate_kbps(&self) -> u32 {
91 self.video_bitrate_kbps + self.audio_bitrate_kbps
92 }
93
94 pub fn fits_budget(&self, budget_kbps: u32) -> bool {
96 self.bitrate_kbps() <= budget_kbps
97 }
98
99 pub fn resolution_cap(&self) -> (u32, u32) {
101 self.tier.resolution_cap()
102 }
103}
104
105pub struct ProxyQualitySelector {
107 configs: Vec<ProxyQualityConfig>,
108}
109
110impl ProxyQualitySelector {
111 pub fn new() -> Self {
113 Self {
114 configs: vec![
115 ProxyQualityConfig::new(ProxyQualityTier::Draft),
116 ProxyQualityConfig::new(ProxyQualityTier::Review),
117 ProxyQualityConfig::new(ProxyQualityTier::Delivery),
118 ],
119 }
120 }
121
122 pub fn with_configs(configs: Vec<ProxyQualityConfig>) -> Self {
124 Self { configs }
125 }
126
127 pub fn select_for_bandwidth(
130 &self,
131 available_bandwidth_kbps: u32,
132 ) -> Option<&ProxyQualityConfig> {
133 let mut sorted: Vec<&ProxyQualityConfig> = self.configs.iter().collect();
135 sorted.sort_by(|a, b| b.tier.cmp(&a.tier));
136 sorted
137 .into_iter()
138 .find(|c| c.fits_budget(available_bandwidth_kbps))
139 }
140
141 pub fn all_tiers(&self) -> Vec<&ProxyQualityConfig> {
143 let mut v: Vec<&ProxyQualityConfig> = self.configs.iter().collect();
144 v.sort_by_key(|c| c.tier);
145 v
146 }
147}
148
149impl Default for ProxyQualitySelector {
150 fn default() -> Self {
151 Self::new()
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_draft_resolution_cap() {
161 assert_eq!(ProxyQualityTier::Draft.resolution_cap(), (640, 360));
162 }
163
164 #[test]
165 fn test_review_resolution_cap() {
166 assert_eq!(ProxyQualityTier::Review.resolution_cap(), (1280, 720));
167 }
168
169 #[test]
170 fn test_delivery_resolution_cap() {
171 assert_eq!(ProxyQualityTier::Delivery.resolution_cap(), (1920, 1080));
172 }
173
174 #[test]
175 fn test_tier_upgrade() {
176 assert_eq!(
177 ProxyQualityTier::Draft.upgrade(),
178 Some(ProxyQualityTier::Review)
179 );
180 assert_eq!(ProxyQualityTier::Delivery.upgrade(), None);
181 }
182
183 #[test]
184 fn test_tier_downgrade() {
185 assert_eq!(
186 ProxyQualityTier::Delivery.downgrade(),
187 Some(ProxyQualityTier::Review)
188 );
189 assert_eq!(ProxyQualityTier::Draft.downgrade(), None);
190 }
191
192 #[test]
193 fn test_tier_ordering() {
194 assert!(ProxyQualityTier::Draft < ProxyQualityTier::Review);
195 assert!(ProxyQualityTier::Review < ProxyQualityTier::Delivery);
196 }
197
198 #[test]
199 fn test_config_bitrate_kbps() {
200 let cfg = ProxyQualityConfig::new(ProxyQualityTier::Draft);
201 assert_eq!(cfg.bitrate_kbps(), 564); }
203
204 #[test]
205 fn test_config_fits_budget_true() {
206 let cfg = ProxyQualityConfig::new(ProxyQualityTier::Draft);
207 assert!(cfg.fits_budget(1000));
208 }
209
210 #[test]
211 fn test_config_fits_budget_false() {
212 let cfg = ProxyQualityConfig::new(ProxyQualityTier::Delivery);
213 assert!(!cfg.fits_budget(100));
214 }
215
216 #[test]
217 fn test_selector_selects_draft_for_low_bandwidth() {
218 let sel = ProxyQualitySelector::new();
219 let result = sel.select_for_bandwidth(600);
220 assert!(result.is_some());
221 assert_eq!(
222 result.expect("should succeed in test").tier,
223 ProxyQualityTier::Draft
224 );
225 }
226
227 #[test]
228 fn test_selector_selects_delivery_for_high_bandwidth() {
229 let sel = ProxyQualitySelector::new();
230 let result = sel.select_for_bandwidth(50_000);
231 assert!(result.is_some());
232 assert_eq!(
233 result.expect("should succeed in test").tier,
234 ProxyQualityTier::Delivery
235 );
236 }
237
238 #[test]
239 fn test_selector_returns_none_if_too_low() {
240 let sel = ProxyQualitySelector::new();
241 let result = sel.select_for_bandwidth(10); assert!(result.is_none());
243 }
244
245 #[test]
246 fn test_selector_all_tiers_sorted() {
247 let sel = ProxyQualitySelector::new();
248 let tiers: Vec<ProxyQualityTier> = sel.all_tiers().iter().map(|c| c.tier).collect();
249 assert_eq!(
250 tiers,
251 vec![
252 ProxyQualityTier::Draft,
253 ProxyQualityTier::Review,
254 ProxyQualityTier::Delivery
255 ]
256 );
257 }
258
259 #[test]
260 fn test_tier_label() {
261 assert_eq!(ProxyQualityTier::Draft.label(), "Draft");
262 assert_eq!(ProxyQualityTier::Review.label(), "Review");
263 assert_eq!(ProxyQualityTier::Delivery.label(), "Delivery");
264 }
265}