1#![allow(dead_code)]
2use std::fmt;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub enum CodecFamily {
13 Av1,
15 H264,
17 H265,
19 Vp9,
21}
22
23impl fmt::Display for CodecFamily {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 match self {
26 Self::Av1 => write!(f, "AV1"),
27 Self::H264 => write!(f, "H.264"),
28 Self::H265 => write!(f, "H.265"),
29 Self::Vp9 => write!(f, "VP9"),
30 }
31 }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36pub enum Profile {
37 Main,
39 Main10,
41 High,
43 Professional,
45 Baseline,
47}
48
49impl fmt::Display for Profile {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Self::Main => write!(f, "Main"),
53 Self::Main10 => write!(f, "Main10"),
54 Self::High => write!(f, "High"),
55 Self::Professional => write!(f, "Professional"),
56 Self::Baseline => write!(f, "Baseline"),
57 }
58 }
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
65pub struct Level(pub u16);
66
67impl Level {
68 pub const L3_0: Self = Self(30);
70 pub const L3_1: Self = Self(31);
72 pub const L4_0: Self = Self(40);
74 pub const L4_1: Self = Self(41);
76 pub const L5_0: Self = Self(50);
78 pub const L5_1: Self = Self(51);
80 pub const L5_2: Self = Self(52);
82 pub const L6_0: Self = Self(60);
84 pub const L6_1: Self = Self(61);
86}
87
88impl fmt::Display for Level {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "{}.{}", self.0 / 10, self.0 % 10)
91 }
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
96pub enum Tier {
97 Main,
99 High,
101}
102
103impl Default for Tier {
104 fn default() -> Self {
105 Self::Main
106 }
107}
108
109#[derive(Clone, Debug)]
111pub struct LevelConstraints {
112 pub max_width: u32,
114 pub max_height: u32,
116 pub max_sample_rate: u64,
118 pub max_bitrate_main: u64,
120 pub max_bitrate_high: u64,
122 pub max_ref_frames: u8,
124 pub max_tile_cols: u8,
126 pub max_tile_rows: u8,
128}
129
130impl LevelConstraints {
131 pub fn max_bitrate(&self, tier: Tier) -> u64 {
133 match tier {
134 Tier::Main => self.max_bitrate_main,
135 Tier::High => self.max_bitrate_high,
136 }
137 }
138
139 pub fn max_pixels(&self) -> u64 {
141 u64::from(self.max_width) * u64::from(self.max_height)
142 }
143}
144
145pub fn get_constraints(codec: CodecFamily, level: Level) -> LevelConstraints {
147 match codec {
148 CodecFamily::Av1 => av1_constraints(level),
149 CodecFamily::H264 => h264_constraints(level),
150 CodecFamily::H265 => h265_constraints(level),
151 CodecFamily::Vp9 => vp9_constraints(level),
152 }
153}
154
155fn av1_constraints(level: Level) -> LevelConstraints {
156 match level.0 {
157 50 => LevelConstraints {
158 max_width: 4096,
159 max_height: 2304,
160 max_sample_rate: 66_846_720,
161 max_bitrate_main: 30_000_000,
162 max_bitrate_high: 60_000_000,
163 max_ref_frames: 7,
164 max_tile_cols: 8,
165 max_tile_rows: 8,
166 },
167 51 => LevelConstraints {
168 max_width: 4096,
169 max_height: 2304,
170 max_sample_rate: 133_693_440,
171 max_bitrate_main: 40_000_000,
172 max_bitrate_high: 100_000_000,
173 max_ref_frames: 7,
174 max_tile_cols: 8,
175 max_tile_rows: 8,
176 },
177 60 => LevelConstraints {
178 max_width: 8192,
179 max_height: 4352,
180 max_sample_rate: 267_386_880,
181 max_bitrate_main: 60_000_000,
182 max_bitrate_high: 180_000_000,
183 max_ref_frames: 7,
184 max_tile_cols: 16,
185 max_tile_rows: 16,
186 },
187 _ => default_constraints(),
188 }
189}
190
191fn h264_constraints(level: Level) -> LevelConstraints {
192 match level.0 {
193 40 => LevelConstraints {
194 max_width: 2048,
195 max_height: 1024,
196 max_sample_rate: 62_914_560,
197 max_bitrate_main: 20_000_000,
198 max_bitrate_high: 25_000_000,
199 max_ref_frames: 4,
200 max_tile_cols: 1,
201 max_tile_rows: 1,
202 },
203 51 => LevelConstraints {
204 max_width: 4096,
205 max_height: 2304,
206 max_sample_rate: 251_658_240,
207 max_bitrate_main: 135_000_000,
208 max_bitrate_high: 240_000_000,
209 max_ref_frames: 16,
210 max_tile_cols: 1,
211 max_tile_rows: 1,
212 },
213 _ => default_constraints(),
214 }
215}
216
217fn h265_constraints(level: Level) -> LevelConstraints {
218 match level.0 {
219 40 => LevelConstraints {
220 max_width: 2048,
221 max_height: 1080,
222 max_sample_rate: 62_914_560,
223 max_bitrate_main: 12_000_000,
224 max_bitrate_high: 30_000_000,
225 max_ref_frames: 6,
226 max_tile_cols: 5,
227 max_tile_rows: 5,
228 },
229 51 => LevelConstraints {
230 max_width: 4096,
231 max_height: 2160,
232 max_sample_rate: 267_386_880,
233 max_bitrate_main: 40_000_000,
234 max_bitrate_high: 160_000_000,
235 max_ref_frames: 6,
236 max_tile_cols: 10,
237 max_tile_rows: 10,
238 },
239 _ => default_constraints(),
240 }
241}
242
243fn vp9_constraints(level: Level) -> LevelConstraints {
244 match level.0 {
245 40 => LevelConstraints {
246 max_width: 2048,
247 max_height: 1080,
248 max_sample_rate: 62_914_560,
249 max_bitrate_main: 18_000_000,
250 max_bitrate_high: 36_000_000,
251 max_ref_frames: 3,
252 max_tile_cols: 4,
253 max_tile_rows: 4,
254 },
255 51 => LevelConstraints {
256 max_width: 4096,
257 max_height: 2160,
258 max_sample_rate: 267_386_880,
259 max_bitrate_main: 36_000_000,
260 max_bitrate_high: 120_000_000,
261 max_ref_frames: 3,
262 max_tile_cols: 8,
263 max_tile_rows: 8,
264 },
265 _ => default_constraints(),
266 }
267}
268
269fn default_constraints() -> LevelConstraints {
270 LevelConstraints {
271 max_width: 1920,
272 max_height: 1080,
273 max_sample_rate: 31_457_280,
274 max_bitrate_main: 10_000_000,
275 max_bitrate_high: 20_000_000,
276 max_ref_frames: 4,
277 max_tile_cols: 1,
278 max_tile_rows: 1,
279 }
280}
281
282#[derive(Clone, Debug, PartialEq, Eq)]
284pub struct ComplianceResult {
285 pub passed: bool,
287 pub violations: Vec<String>,
289}
290
291impl ComplianceResult {
292 pub fn pass() -> Self {
294 Self {
295 passed: true,
296 violations: Vec::new(),
297 }
298 }
299
300 pub fn fail(violations: Vec<String>) -> Self {
302 Self {
303 passed: false,
304 violations,
305 }
306 }
307}
308
309#[allow(clippy::cast_precision_loss)]
311pub fn check_compliance(
312 codec: CodecFamily,
313 level: Level,
314 tier: Tier,
315 width: u32,
316 height: u32,
317 framerate: f64,
318 bitrate: u64,
319) -> ComplianceResult {
320 let constraints = get_constraints(codec, level);
321 let mut violations = Vec::new();
322
323 if width > constraints.max_width {
324 violations.push(format!(
325 "Width {width} exceeds max {max}",
326 max = constraints.max_width
327 ));
328 }
329 if height > constraints.max_height {
330 violations.push(format!(
331 "Height {height} exceeds max {max}",
332 max = constraints.max_height
333 ));
334 }
335
336 let sample_rate = (u64::from(width) * u64::from(height)) as f64 * framerate;
337 if sample_rate > constraints.max_sample_rate as f64 {
338 violations.push(format!(
339 "Sample rate {sample_rate:.0} exceeds max {}",
340 constraints.max_sample_rate
341 ));
342 }
343
344 let max_br = constraints.max_bitrate(tier);
345 if bitrate > max_br {
346 violations.push(format!(
347 "Bitrate {bitrate} exceeds max {max_br} for tier {tier:?}"
348 ));
349 }
350
351 if violations.is_empty() {
352 ComplianceResult::pass()
353 } else {
354 ComplianceResult::fail(violations)
355 }
356}
357
358#[allow(clippy::cast_precision_loss)]
360pub fn find_minimum_level(
361 codec: CodecFamily,
362 tier: Tier,
363 width: u32,
364 height: u32,
365 framerate: f64,
366 bitrate: u64,
367) -> Option<Level> {
368 let candidates = [
369 Level::L3_0,
370 Level::L3_1,
371 Level::L4_0,
372 Level::L4_1,
373 Level::L5_0,
374 Level::L5_1,
375 Level::L5_2,
376 Level::L6_0,
377 Level::L6_1,
378 ];
379 for lvl in &candidates {
380 let result = check_compliance(codec, *lvl, tier, width, height, framerate, bitrate);
381 if result.passed {
382 return Some(*lvl);
383 }
384 }
385 None
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_codec_family_display() {
394 assert_eq!(CodecFamily::Av1.to_string(), "AV1");
395 assert_eq!(CodecFamily::H264.to_string(), "H.264");
396 assert_eq!(CodecFamily::H265.to_string(), "H.265");
397 assert_eq!(CodecFamily::Vp9.to_string(), "VP9");
398 }
399
400 #[test]
401 fn test_profile_display() {
402 assert_eq!(Profile::Main.to_string(), "Main");
403 assert_eq!(Profile::Main10.to_string(), "Main10");
404 assert_eq!(Profile::Baseline.to_string(), "Baseline");
405 }
406
407 #[test]
408 fn test_level_display() {
409 assert_eq!(Level::L5_1.to_string(), "5.1");
410 assert_eq!(Level::L4_0.to_string(), "4.0");
411 assert_eq!(Level::L6_1.to_string(), "6.1");
412 }
413
414 #[test]
415 fn test_level_ordering() {
416 assert!(Level::L3_0 < Level::L4_0);
417 assert!(Level::L5_0 < Level::L5_1);
418 assert!(Level::L5_1 < Level::L6_0);
419 }
420
421 #[test]
422 fn test_tier_default() {
423 assert_eq!(Tier::default(), Tier::Main);
424 }
425
426 #[test]
427 fn test_av1_level_50_constraints() {
428 let c = get_constraints(CodecFamily::Av1, Level::L5_0);
429 assert_eq!(c.max_width, 4096);
430 assert_eq!(c.max_height, 2304);
431 assert_eq!(c.max_ref_frames, 7);
432 }
433
434 #[test]
435 fn test_max_bitrate_tier() {
436 let c = get_constraints(CodecFamily::Av1, Level::L5_0);
437 assert_eq!(c.max_bitrate(Tier::Main), 30_000_000);
438 assert_eq!(c.max_bitrate(Tier::High), 60_000_000);
439 }
440
441 #[test]
442 fn test_max_pixels() {
443 let c = get_constraints(CodecFamily::Av1, Level::L5_0);
444 assert_eq!(c.max_pixels(), 4096 * 2304);
445 }
446
447 #[test]
448 fn test_compliance_pass() {
449 let result = check_compliance(
450 CodecFamily::Av1,
451 Level::L5_1,
452 Tier::Main,
453 1920,
454 1080,
455 60.0,
456 10_000_000,
457 );
458 assert!(result.passed);
459 assert!(result.violations.is_empty());
460 }
461
462 #[test]
463 fn test_compliance_fail_width() {
464 let result = check_compliance(
465 CodecFamily::Av1,
466 Level::L5_0,
467 Tier::Main,
468 8192,
469 4320,
470 30.0,
471 10_000_000,
472 );
473 assert!(!result.passed);
474 assert!(!result.violations.is_empty());
475 }
476
477 #[test]
478 fn test_compliance_fail_bitrate() {
479 let result = check_compliance(
480 CodecFamily::Av1,
481 Level::L5_0,
482 Tier::Main,
483 1920,
484 1080,
485 30.0,
486 999_000_000,
487 );
488 assert!(!result.passed);
489 assert!(result.violations.iter().any(|v| v.contains("Bitrate")));
490 }
491
492 #[test]
493 fn test_find_minimum_level() {
494 let lvl = find_minimum_level(CodecFamily::Av1, Tier::Main, 1920, 1080, 30.0, 8_000_000);
495 assert!(lvl.is_some());
496 }
497
498 #[test]
499 fn test_find_minimum_level_none() {
500 let lvl = find_minimum_level(
502 CodecFamily::Av1,
503 Tier::Main,
504 8192,
505 4320,
506 120.0,
507 999_999_999_999,
508 );
509 assert!(lvl.is_none());
510 }
511
512 #[test]
513 fn test_compliance_result_constructors() {
514 let pass = ComplianceResult::pass();
515 assert!(pass.passed);
516 assert!(pass.violations.is_empty());
517
518 let fail = ComplianceResult::fail(vec!["bad".to_string()]);
519 assert!(!fail.passed);
520 assert_eq!(fail.violations.len(), 1);
521 }
522}