1use crate::common::ProfileConstraint;
2use std::fmt;
6use yuv::color::ChromaSampling;
7use yuv::color::Depth;
8
9#[derive(Debug)]
10pub struct LevelSelector {
11 width: u32,
13 height: u32,
14 framerate: f32,
15 tier: Tier,
16 profile: Profile,
17 min_level: Option<Level>,
18 max_level: Option<Level>,
19 max_bitrate: Option<u32>,
20}
21
22impl LevelSelector {
23 pub fn new() -> Self {
24 Self {
25 width: 1920,
27 height: 1080,
28 framerate: 30.0,
29 tier: Tier::Main,
30 profile: Profile::Main,
31 min_level: None,
33 max_level: None,
34 max_bitrate: None,
35 }
36 }
37 pub fn width(mut self, width: u32) -> Self {
38 self.width = width;
39 self
40 }
41 pub fn height(mut self, height: u32) -> Self {
42 self.height = height;
43 self
44 }
45 pub fn framerate(mut self, framerate: f32) -> Self {
46 self.framerate = framerate;
47 self
48 }
49 pub fn clamp(mut self, min: Level, max: Level) -> Self {
50 self.min_level = Some(min);
51 self.max_level = Some(max);
52 self
53 }
54 pub fn max_bitrate(mut self, max_bitrate: u32) -> Self {
55 self.max_bitrate = Some(max_bitrate);
56 self
57 }
58 pub fn tier(mut self, tier: Tier) -> Self {
59 self.tier = tier;
60 self
61 }
62 pub fn profile(mut self, profile: Profile) -> Self {
63 self.profile = profile;
64 self
65 }
66
67 pub fn select(self) -> Option<LevelSpecification> {
68 let samplerate = (self.width * self.height) as u64 * self.framerate.ceil() as u64;
69
70 for level in LEVEL_DETAILS.iter() {
71 if samplerate <= level.max_luma_sample_rate {
72 let selected = match (
74 self.max_bitrate,
75 level.max_bit_rate(self.profile, self.tier),
76 ) {
77 (Some(bitrate_constraint), Some(level_max_bitrate))
78 if level_max_bitrate >= bitrate_constraint.into() =>
79 {
80 *level
81 }
82 (None, Some(_)) => *level,
83 _ => continue,
84 };
85
86 match self.min_level {
88 Some(min) if selected.id() < min => {
89 continue;
90 }
91 _ => {}
92 }
93
94 match self.max_level {
96 Some(max) if selected.id() > max => return None,
97 _ => {}
98 }
99
100 return Some(selected);
101 }
102 }
103
104 Some(LEVEL_DETAILS[LEVEL_DETAILS.len() - 1])
105 }
106}
107
108pub fn get(level: Level) -> LevelSpecification {
110 for l in LEVEL_DETAILS.iter() {
111 if l.id() == level {
112 return *l;
113 }
114 }
115
116 LEVEL_DETAILS[LEVEL_DETAILS.len() - 1]
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
120pub enum Tier {
121 Main,
123 High,
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
128pub enum Profile {
130 Main,
131 Main10,
132 Main12,
133 Main422_10,
134 Main444,
135 Main444_16Intra,
136 Main444_16IntraHighThroughput,
137}
138
139impl Profile {
140 pub fn bitrate_multiplier(&self) -> f64 {
141 let spec = ProfileConstraint::from(self);
142 let pixel_multiplier = match spec.max_chroma_format() {
143 ChromaSampling::Cs420 => match spec.max_bit_depth {
144 Depth::Depth8 => 1.0,
145 Depth::Depth10 => 1.0,
146 Depth::Depth12 => 1.5,
147 Depth::Depth16 => 3.0,
148 },
149 ChromaSampling::Cs422 => match spec.max_bit_depth {
150 Depth::Depth8 => 2.0,
151 Depth::Depth10 => 2.0,
152 Depth::Depth12 => 3.0,
153 Depth::Depth16 => 6.0,
154 },
155 ChromaSampling::Cs444 => match spec.max_bit_depth {
156 Depth::Depth8 => 3.0,
157 Depth::Depth10 => 3.0,
158 Depth::Depth12 => 3.0,
159 Depth::Depth16 => 8.0,
160 },
161 ChromaSampling::Monochrome => match spec.max_bit_depth {
162 Depth::Depth8 => 1.0,
163 Depth::Depth10 => 1.0,
164 Depth::Depth12 => 1.5,
165 Depth::Depth16 => 3.0,
166 },
167 };
168
169 let throughput_multiplier = match self {
170 Profile::Main444_16IntraHighThroughput => 12.0,
171 _ => 1.0,
172 };
173
174 pixel_multiplier * throughput_multiplier
175 }
176}
177
178impl From<&Profile> for ProfileConstraint {
179 fn from(profile: &Profile) -> Self {
180 match profile {
181 Profile::Main => ProfileConstraint::new(
182 yuv::color::Depth::Depth8,
183 vec![ChromaSampling::Monochrome, ChromaSampling::Cs420],
184 ),
185 Profile::Main10 => ProfileConstraint::new(
186 yuv::color::Depth::Depth10,
187 vec![ChromaSampling::Monochrome, ChromaSampling::Cs420],
188 ),
189 Profile::Main12 => ProfileConstraint::new(
190 yuv::color::Depth::Depth12,
191 vec![ChromaSampling::Monochrome, ChromaSampling::Cs420],
192 ),
193 Profile::Main422_10 => ProfileConstraint::new(
194 yuv::color::Depth::Depth10,
195 vec![
196 ChromaSampling::Monochrome,
197 ChromaSampling::Cs420,
198 ChromaSampling::Cs422,
199 ],
200 ),
201 Profile::Main444 => ProfileConstraint::new(
202 yuv::color::Depth::Depth8,
203 vec![
204 ChromaSampling::Monochrome,
205 ChromaSampling::Cs420,
206 ChromaSampling::Cs422,
207 ChromaSampling::Cs444,
208 ],
209 ),
210 Profile::Main444_16Intra => ProfileConstraint::new(
211 yuv::color::Depth::Depth16,
212 vec![
213 ChromaSampling::Monochrome,
214 ChromaSampling::Cs420,
215 ChromaSampling::Cs422,
216 ChromaSampling::Cs444,
217 ],
218 ),
219 Profile::Main444_16IntraHighThroughput => ProfileConstraint::new(
220 yuv::color::Depth::Depth16,
221 vec![
222 ChromaSampling::Monochrome,
223 ChromaSampling::Cs420,
224 ChromaSampling::Cs422,
225 ChromaSampling::Cs444,
226 ],
227 ),
228 }
229 }
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
233pub enum Level {
234 L1,
235 L2,
236 L2_1,
237 L3,
238 L3_1,
239 L4,
240 L4_1,
241 L5,
242 L5_1,
243 L5_2,
244 L6,
245 L6_1,
246 L6_2,
247 L6_3,
248 L7,
249 L7_1,
250 L7_2,
251 L8_5,
253}
254
255impl From<usize> for Level {
258 fn from(value: usize) -> Self {
259 match value {
260 30 => Level::L1,
261 60 => Level::L2,
262 63 => Level::L2_1,
263 90 => Level::L3,
264 93 => Level::L3_1,
265 120 => Level::L4,
266 123 => Level::L4_1,
267 150 => Level::L5,
268 153 => Level::L5_1,
269 156 => Level::L5_2,
270 180 => Level::L6,
271 183 => Level::L6_1,
272 186 => Level::L6_2,
273 189 => Level::L6_3,
274 210 => Level::L7,
275 213 => Level::L7_1,
276 216 => Level::L7_2,
277 255 => Level::L8_5,
278 _ => Level::L8_5,
279 }
280 }
281}
282
283impl Level {
284 fn usize(&self) -> usize {
285 match self {
286 Level::L1 => 30,
287 Level::L2 => 60,
288 Level::L2_1 => 63,
289 Level::L3 => 90,
290 Level::L3_1 => 93,
291 Level::L4 => 120,
292 Level::L4_1 => 123,
293 Level::L5 => 150,
294 Level::L5_1 => 153,
295 Level::L5_2 => 156,
296 Level::L6 => 180,
297 Level::L6_1 => 183,
298 Level::L6_2 => 186,
299 Level::L6_3 => 189,
300 Level::L7 => 210,
301 Level::L7_1 => 213,
302 Level::L7_2 => 216,
303 Level::L8_5 => 255,
304 }
305 }
306}
307
308impl fmt::Display for Level {
309 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310 let level_str = match self {
311 Level::L1 => "1",
312 Level::L2 => "2",
313 Level::L2_1 => "2.1",
314 Level::L3 => "3",
315 Level::L3_1 => "3.1",
316 Level::L4 => "4",
317 Level::L4_1 => "4.1",
318 Level::L5 => "5",
319 Level::L5_1 => "5.1",
320 Level::L5_2 => "5.2",
321 Level::L6 => "6",
322 Level::L6_1 => "6.1",
323 Level::L6_2 => "6.2",
324 Level::L6_3 => "6.3",
325 Level::L7 => "7",
326 Level::L7_1 => "7.1",
327 Level::L7_2 => "7.2",
328 Level::L8_5 => "8.1",
329 };
330 write!(f, "{}", level_str)
331 }
332}
333
334#[derive(Debug, Clone, Copy)]
335pub struct LevelSpecification {
336 id: Level,
337 max_luma_sample_rate: u64,
339 max_luma_picture_size: u32,
341 max_bit_rate_main: u32,
342 max_bit_rate_high: Option<u32>,
343}
344
345impl LevelSpecification {
346 pub fn id(&self) -> Level {
347 self.id
348 }
349
350 pub fn max_luma_sample_rate(&self) -> u64 {
351 self.max_luma_sample_rate
352 }
353
354 pub fn max_luma_picture_size(&self) -> u32 {
355 self.max_luma_picture_size
356 }
357
358 pub fn max_bit_rate(&self, profile: Profile, tier: Tier) -> Option<u64> {
359 match tier {
360 Tier::Main => {
361 Some((self.max_bit_rate_main as f64 * profile.bitrate_multiplier()) as u64)
362 }
363 Tier::High => self
364 .max_bit_rate_high
365 .map(|v| (v as f64 * profile.bitrate_multiplier()) as u64),
366 }
367 }
368
369 pub fn max_decoder_picture_buffer_size(&self, width: u32, height: u32) -> u32 {
370 let luma_samples = width * height;
371 let max_dpb_pic_buf = 6;
372
373 if luma_samples <= self.max_luma_picture_size >> 2 {
374 std::cmp::min(4 * max_dpb_pic_buf, 16)
375 } else if luma_samples <= self.max_luma_picture_size >> 1 {
376 std::cmp::min(2 * max_dpb_pic_buf, 16)
377 } else if luma_samples <= (3 * self.max_luma_picture_size) >> 2 {
378 std::cmp::min((4 * max_dpb_pic_buf) / 3, 16)
379 } else {
380 max_dpb_pic_buf
381 }
382 }
383}
384
385pub const LEVEL_DETAILS: [LevelSpecification; 18] = [
386 LevelSpecification {
387 id: Level::L1,
388 max_luma_sample_rate: 552_960,
389 max_luma_picture_size: 36_864,
390 max_bit_rate_main: 128,
391 max_bit_rate_high: None,
392 },
393 LevelSpecification {
394 id: Level::L2,
395 max_luma_sample_rate: 3_686_400,
396 max_luma_picture_size: 122_880,
397 max_bit_rate_main: 1_500,
398 max_bit_rate_high: None,
399 },
400 LevelSpecification {
401 id: Level::L2_1,
402 max_luma_sample_rate: 7_372_800,
403 max_luma_picture_size: 245_760,
404 max_bit_rate_main: 3_000,
405 max_bit_rate_high: None,
406 },
407 LevelSpecification {
408 id: Level::L3,
409 max_luma_sample_rate: 16_588_800,
410 max_luma_picture_size: 552_960,
411 max_bit_rate_main: 6_000,
412 max_bit_rate_high: None,
413 },
414 LevelSpecification {
415 id: Level::L3_1,
416 max_luma_sample_rate: 33_177_600,
417 max_luma_picture_size: 983_040,
418 max_bit_rate_main: 10_000,
419 max_bit_rate_high: None,
420 },
421 LevelSpecification {
422 id: Level::L4,
423 max_luma_sample_rate: 66_846_720,
424 max_luma_picture_size: 2_228_224,
425 max_bit_rate_main: 12_000,
426 max_bit_rate_high: Some(30_000),
427 },
428 LevelSpecification {
429 id: Level::L4_1,
430 max_luma_sample_rate: 133_693_440,
431 max_luma_picture_size: 2_228_224,
432 max_bit_rate_main: 20_000,
433 max_bit_rate_high: Some(50_000),
434 },
435 LevelSpecification {
436 id: Level::L5,
437 max_luma_sample_rate: 267_386_880,
438 max_luma_picture_size: 8_912_896,
439 max_bit_rate_main: 25_000,
440 max_bit_rate_high: Some(100_000),
441 },
442 LevelSpecification {
443 id: Level::L5_1,
444 max_luma_sample_rate: 534_773_760,
445 max_luma_picture_size: 8_912_896,
446 max_bit_rate_main: 40_000,
447 max_bit_rate_high: Some(160_000),
448 },
449 LevelSpecification {
450 id: Level::L5_2,
451 max_luma_sample_rate: 1_069_547_520,
452 max_luma_picture_size: 8_912_896,
453 max_bit_rate_main: 60_000,
454 max_bit_rate_high: Some(240_000),
455 },
456 LevelSpecification {
457 id: Level::L6,
458 max_luma_sample_rate: 1_069_547_520,
459 max_luma_picture_size: 35_651_584,
460 max_bit_rate_main: 60_000,
461 max_bit_rate_high: Some(240_000),
462 },
463 LevelSpecification {
464 id: Level::L6_1,
465 max_luma_sample_rate: 2_139_095_040,
466 max_luma_picture_size: 35_651_584,
467 max_bit_rate_main: 120_000,
468 max_bit_rate_high: Some(480_000),
469 },
470 LevelSpecification {
471 id: Level::L6_2,
472 max_luma_sample_rate: 4_278_190_080,
473 max_luma_picture_size: 35_651_584,
474 max_bit_rate_main: 240_000,
475 max_bit_rate_high: Some(800_000),
476 },
477 LevelSpecification {
478 id: Level::L6_3,
479 max_luma_sample_rate: 4_812_963_840,
480 max_luma_picture_size: 35_651_584,
481 max_bit_rate_main: 240_000,
482 max_bit_rate_high: Some(1_600_000),
483 },
484 LevelSpecification {
485 id: Level::L7,
486 max_luma_sample_rate: 4_812_963_840,
487 max_luma_picture_size: 142_606_336,
488 max_bit_rate_main: 240_000,
489 max_bit_rate_high: Some(1_600_000),
490 },
491 LevelSpecification {
492 id: Level::L7_1,
493 max_luma_sample_rate: 8_556_380_160,
494 max_luma_picture_size: 142_606_336,
495 max_bit_rate_main: 480_000,
496 max_bit_rate_high: Some(3_200_000),
497 },
498 LevelSpecification {
499 id: Level::L7_2,
500 max_luma_sample_rate: 17_112_760_320,
501 max_luma_picture_size: 142_606_336,
502 max_bit_rate_main: 960_000,
503 max_bit_rate_high: Some(6_400_000),
504 },
505 LevelSpecification {
506 id: Level::L8_5,
507 max_luma_sample_rate: std::u64::MAX,
508 max_luma_picture_size: std::u32::MAX,
509 max_bit_rate_main: std::u32::MAX,
510 max_bit_rate_high: Some(std::u32::MAX),
511 },
512];
513
514#[cfg(test)]
515mod tests {
516 #[test]
517 fn level_mult() {
518 use crate::hevc::Level;
519
520 assert_eq!(Level::L6_2, Level::from(186));
521 }
522
523 #[test]
524 fn max_bitrate() {
525 use crate::hevc::{self, Level, Profile, Tier};
526
527 let l = hevc::get(Level::L5_2);
529 assert_eq!(l.id(), Level::L5_2);
530 assert_eq!(l.max_bit_rate(Profile::Main, Tier::Main), Some(60_000));
531 assert_eq!(l.max_bit_rate(Profile::Main12, Tier::Main), Some(90_000));
532 assert_eq!(l.max_bit_rate(Profile::Main444, Tier::Main), Some(180_000));
533 assert_eq!(
534 l.max_bit_rate(Profile::Main444_16Intra, Tier::Main),
535 Some(480_000)
536 );
537 assert_eq!(
538 l.max_bit_rate(Profile::Main444_16IntraHighThroughput, Tier::Main),
539 Some(5_760_000)
540 );
541 assert_eq!(l.max_bit_rate(Profile::Main444, Tier::High), Some(720_000));
542 assert_eq!(
543 l.max_bit_rate(Profile::Main444_16Intra, Tier::High),
544 Some(1_920_000)
545 );
546 assert_eq!(
547 l.max_bit_rate(Profile::Main444_16IntraHighThroughput, Tier::High),
548 Some(23_040_000)
549 );
550
551 let l = hevc::get(Level::L2);
553 assert_eq!(l.id(), Level::L2);
554 assert_eq!(l.max_bit_rate(Profile::Main, Tier::Main), Some(1_500));
555 }
556
557 #[test]
558 fn max_dpb_pic_buf() {
559 use crate::hevc::Level;
560
561 let l = crate::hevc::get(Level::L4);
562 assert_eq!(l.max_decoder_picture_buffer_size(1280, 720), 12);
563 assert_eq!(l.max_decoder_picture_buffer_size(1920, 1080), 6);
564
565 let l = crate::hevc::get(Level::L5_2);
566 assert_eq!(l.max_decoder_picture_buffer_size(1920, 1080), 16);
567 assert_eq!(l.max_decoder_picture_buffer_size(2560, 1440), 12);
568 assert_eq!(l.max_decoder_picture_buffer_size(3840, 2160), 6);
569 }
570
571 #[test]
572 fn select_base_cases() {
573 use crate::hevc::{Level, LevelSelector, Profile, Tier};
574 assert_eq!(
575 LevelSelector::new()
576 .width(1920)
577 .height(1080)
578 .framerate(30.0)
579 .tier(Tier::Main)
580 .profile(Profile::Main)
581 .select()
582 .unwrap()
583 .id(),
584 Level::L4
585 );
586
587 assert_eq!(
588 LevelSelector::new()
589 .width(3840)
590 .height(2160)
591 .framerate(30.0)
592 .tier(Tier::Main)
593 .profile(Profile::Main)
594 .select()
595 .unwrap()
596 .id(),
597 Level::L5
598 );
599
600 assert_eq!(
601 LevelSelector::new()
602 .width(3840)
603 .height(2160)
604 .framerate(60.0)
605 .tier(Tier::Main)
606 .profile(Profile::Main)
607 .select()
608 .unwrap()
609 .id(),
610 Level::L5_1
611 );
612
613 assert_eq!(
614 LevelSelector::new()
615 .width(3840)
616 .height(2160)
617 .framerate(66.0)
618 .tier(Tier::Main)
619 .profile(Profile::Main)
620 .select()
621 .unwrap()
622 .id(),
623 Level::L5_2
624 );
625 }
626
627 #[test]
628 fn select_clamp_cases() {
629 use crate::hevc::{Level, LevelSelector, Profile, Tier};
630
631 assert_eq!(
632 LevelSelector::new()
633 .width(1920)
634 .height(1080)
635 .framerate(60.0)
636 .tier(Tier::Main)
637 .profile(Profile::Main)
638 .clamp(Level::L5_2, Level::L7_1)
639 .select()
640 .unwrap()
641 .id(),
642 Level::L5_2
643 );
644
645 assert!(LevelSelector::new()
646 .width(1920)
647 .height(1080)
648 .framerate(60.0)
649 .tier(Tier::Main)
650 .profile(Profile::Main)
651 .clamp(Level::L2, Level::L2_1)
652 .select()
653 .is_none());
654 }
655
656 #[test]
657 fn select_bitrate_cases() {
658 use crate::hevc::{Level, LevelSelector, Profile, Tier};
659 assert_eq!(
660 LevelSelector::new()
661 .width(1920)
662 .height(1080)
663 .framerate(60.0)
664 .tier(Tier::Main)
665 .profile(Profile::Main)
666 .max_bitrate(80000)
667 .select()
668 .unwrap()
669 .id(),
670 Level::L6_1
671 );
672 }
673}