Skip to main content

video_levels/
hevc.rs

1use crate::common::ProfileConstraint;
2/// Implementing the HEVC spec for levels
3///
4/// https://itu.int/rec/T-REC-H.265-202309-I/en
5use std::fmt;
6use yuv::color::ChromaSampling;
7use yuv::color::Depth;
8
9#[derive(Debug)]
10pub struct LevelSelector {
11    // Constraints
12    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            // Define default behaviour if no constraints are set
26            width: 1920,
27            height: 1080,
28            framerate: 30.0,
29            tier: Tier::Main,
30            profile: Profile::Main,
31            // Ignore if not set
32            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                // Check if level fits within the max specified bitrate
73                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                // Clamp to min level
87                match self.min_level {
88                    Some(min) if selected.id() < min => {
89                        continue;
90                    }
91                    _ => {}
92                }
93
94                // Check if exceds max level
95                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
108/// get returns the level specification for the given level
109pub 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    /// The Main tier was designed for most applications
122    Main,
123    /// High tier was designed for very demanding applications
124    High,
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
128/// Not a complete list but like... feel free to commit more
129pub 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    /// Everything goes when you hit 8.5
252    L8_5,
253}
254
255/// HEVC spec states that the level is a multiple of 30
256/// "general_level_idc and sub_layer_level_idc[ i ] shall be set equal to a value of 30 times the level number specified in able A.8."
257impl 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    /// Samples (pixels) per second
338    max_luma_sample_rate: u64,
339    /// MaxLumaPs (samples) per picture
340    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        // test level 5.2
528        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        // test level 2
552        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}