Skip to main content

video_levels/
av1.rs

1use std::fmt;
2
3use crate::common::ProfileConstraint;
4use yuv::color::ChromaSampling;
5use yuv::color::Depth;
6
7#[derive(Debug)]
8pub struct LevelSelector {
9    // Constraints
10    width: u32,
11    height: u32,
12    framerate: f32,
13    tier: Tier,
14    profile: Profile,
15    min_level: Option<Level>,
16    max_level: Option<Level>,
17    max_bitrate: Option<u32>,
18}
19
20impl LevelSelector {
21    pub fn new() -> Self {
22        Self {
23            // Define default behaviour if no constraints are set
24            width: 1920,
25            height: 1080,
26            framerate: 30.0,
27            tier: Tier::Main,
28            profile: Profile::Main,
29            // Ignore if not set
30            min_level: None,
31            max_level: None,
32            max_bitrate: None,
33        }
34    }
35    pub fn width(mut self, width: u32) -> Self {
36        self.width = width;
37        self
38    }
39    pub fn height(mut self, height: u32) -> Self {
40        self.height = height;
41        self
42    }
43    pub fn framerate(mut self, framerate: f32) -> Self {
44        self.framerate = framerate;
45        self
46    }
47    pub fn clamp(mut self, min: Level, max: Level) -> Self {
48        self.min_level = Some(min);
49        self.max_level = Some(max);
50        self
51    }
52    pub fn max_bitrate(mut self, max_bitrate: u32) -> Self {
53        self.max_bitrate = Some(max_bitrate);
54        self
55    }
56    pub fn tier(mut self, tier: Tier) -> Self {
57        self.tier = tier;
58        self
59    }
60    pub fn profile(mut self, profile: Profile) -> Self {
61        self.profile = profile;
62        self
63    }
64
65    pub fn select(self) -> Option<LevelSpecification> {
66        let samples = self.width * self.height;
67        let display_rate = (samples as f64 * self.framerate as f64) as u64;
68
69        for level in LEVEL_DETAILS.iter() {
70            if samples as u64 <= level.max_picture_size()
71                && display_rate <= level.max_display_rate()
72                && self.width <= level.max_width()
73                && self.height <= level.max_height()
74                && self.framerate as u32 <= level.max_header_rate()
75            {
76                let selected = match (self.max_bitrate, level.max_bit_rate(self.tier)) {
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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
109pub enum Profile {
110    Main,
111    High,
112    Professional,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
116pub enum Tier {
117    Main,
118    High,
119}
120
121impl From<&Profile> for ProfileConstraint {
122    fn from(profile: &Profile) -> Self {
123        match profile {
124            Profile::Main => ProfileConstraint {
125                max_bit_depth: Depth::Depth10,
126                chroma_formats: vec![ChromaSampling::Monochrome, ChromaSampling::Cs420],
127            },
128            Profile::High => ProfileConstraint {
129                max_bit_depth: Depth::Depth10,
130                chroma_formats: vec![
131                    ChromaSampling::Monochrome,
132                    ChromaSampling::Cs420,
133                    ChromaSampling::Cs444,
134                ],
135            },
136            Profile::Professional => ProfileConstraint {
137                max_bit_depth: Depth::Depth12,
138                chroma_formats: vec![
139                    ChromaSampling::Monochrome,
140                    ChromaSampling::Cs420,
141                    ChromaSampling::Cs422,
142                    ChromaSampling::Cs444,
143                ],
144            },
145        }
146    }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
150pub enum Level {
151    L2,
152    L2_1,
153    L3,
154    L3_1,
155    L4,
156    L4_1,
157    L5,
158    L5_1,
159    L5_2,
160    L5_3,
161    L6,
162    L6_1,
163    L6_2,
164    L6_3,
165    Reserved,
166    Max,
167}
168
169impl From<usize> for Level {
170    fn from(value: usize) -> Self {
171        match value {
172            0 => Level::L2,
173            1 => Level::L2_1,
174            4 => Level::L3,
175            5 => Level::L3_1,
176            8 => Level::L4,
177            9 => Level::L4_1,
178            12 => Level::L5,
179            13 => Level::L5_1,
180            14 => Level::L5_2,
181            15 => Level::L5_3,
182            16 => Level::L6,
183            17 => Level::L6_1,
184            18 => Level::L6_2,
185            19 => Level::L6_3,
186            24..=30 => Level::Reserved,
187            32 => Level::Max,
188            _ => Level::Max,
189        }
190    }
191}
192
193impl Level {
194    fn usize(&self) -> usize {
195        match self {
196            Level::L2 => 0,
197            Level::L2_1 => 1,
198            Level::L3 => 4,
199            Level::L3_1 => 5,
200            Level::L4 => 8,
201            Level::L4_1 => 9,
202            Level::L5 => 12,
203            Level::L5_1 => 13,
204            Level::L5_2 => 14,
205            Level::L5_3 => 15,
206            Level::L6 => 16,
207            Level::L6_1 => 17,
208            Level::L6_2 => 18,
209            Level::L6_3 => 19,
210            Level::Reserved => 24,
211            Level::Max => 32,
212        }
213    }
214}
215
216impl fmt::Display for Level {
217    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218        let level_str = match self {
219            Level::L2 => "L2",
220            Level::L2_1 => "L2.1",
221            Level::L3 => "L3",
222            Level::L3_1 => "L3.1",
223            Level::L4 => "L4",
224            Level::L4_1 => "L4.1",
225            Level::L5 => "L5",
226            Level::L5_1 => "L5.1",
227            Level::L5_2 => "L5.2",
228            Level::L5_3 => "L5.3",
229            Level::L6 => "L6",
230            Level::L6_1 => "L6.1",
231            Level::L6_2 => "L6.2",
232            Level::L6_3 => "L6.3",
233            Level::Reserved => "Reserved",
234            Level::Max => "Max",
235        };
236        write!(f, "{}", level_str)
237    }
238}
239
240/// get returns the level specification for the given level
241pub fn get(level: Level) -> LevelSpecification {
242    for l in LEVEL_DETAILS.iter() {
243        if l.id() == level {
244            return *l;
245        }
246    }
247    LEVEL_DETAILS[LEVEL_DETAILS.len() - 1]
248}
249
250#[derive(Debug, Clone, Copy)]
251pub struct LevelSpecification {
252    id: Level,
253    max_picture_size: u64,
254    max_horizontal: u32,
255    max_vertical: u32,
256    max_display_rate: u64,
257    max_decode_rate: u64,
258    max_header_rate: u32,
259    max_bit_rate_main: u64,
260    max_bit_rate_high: Option<u32>,
261    min_comp_basis: u32,
262    max_tiles: u32,
263    max_tile_cols: u32,
264}
265
266impl LevelSpecification {
267    pub fn id(&self) -> Level {
268        self.id
269    }
270    pub fn max_picture_size(&self) -> u64 {
271        self.max_picture_size
272    }
273    pub fn max_horizontal(&self) -> u32 {
274        self.max_horizontal
275    }
276    pub fn max_vertical(&self) -> u32 {
277        self.max_vertical
278    }
279    pub fn max_width(&self) -> u32 {
280        self.max_horizontal
281    }
282    pub fn max_height(&self) -> u32 {
283        self.max_vertical
284    }
285    pub fn max_display_rate(&self) -> u64 {
286        self.max_display_rate
287    }
288    pub fn max_decode_rate(&self) -> u64 {
289        self.max_decode_rate
290    }
291    pub fn max_header_rate(&self) -> u32 {
292        self.max_header_rate
293    }
294    pub fn max_bit_rate(&self, tier: Tier) -> Option<u64> {
295        match tier {
296            Tier::Main => Some(self.max_bit_rate_main),
297            Tier::High => self.max_bit_rate_high.map(|v| v as u64),
298        }
299    }
300    pub fn max_bit_rate_main(&self) -> u64 {
301        self.max_bit_rate_main
302    }
303    pub fn max_bit_rate_high(&self) -> Option<u32> {
304        self.max_bit_rate_high
305    }
306    pub fn min_comp_basis(&self) -> u32 {
307        self.min_comp_basis
308    }
309    pub fn max_tiles(&self) -> u32 {
310        self.max_tiles
311    }
312    pub fn max_tile_cols(&self) -> u32 {
313        self.max_tile_cols
314    }
315}
316
317pub const LEVEL_DETAILS: [LevelSpecification; 14] = [
318    LevelSpecification {
319        id: Level::L2,
320        max_picture_size: 147456,
321        max_horizontal: 1152,
322        max_vertical: 2048,
323        max_display_rate: 4423680,
324        max_decode_rate: 5529600,
325        max_header_rate: 150,
326        max_bit_rate_main: 1_500_000,
327        max_bit_rate_high: None,
328        min_comp_basis: 2,
329        max_tiles: 8,
330        max_tile_cols: 4,
331    },
332    LevelSpecification {
333        id: Level::L2_1,
334        max_picture_size: 278784,
335        max_horizontal: 1584,
336        max_vertical: 2816,
337        max_display_rate: 8363520,
338        max_decode_rate: 10454400,
339        max_header_rate: 150,
340        max_bit_rate_main: 3_000_000,
341        max_bit_rate_high: None,
342        min_comp_basis: 2,
343        max_tiles: 8,
344        max_tile_cols: 4,
345    },
346    LevelSpecification {
347        id: Level::L3,
348        max_picture_size: 665856,
349        max_horizontal: 2448,
350        max_vertical: 4352,
351        max_display_rate: 19975680,
352        max_decode_rate: 24969600,
353        max_header_rate: 150,
354        max_bit_rate_main: 6_000_000,
355        max_bit_rate_high: None,
356        min_comp_basis: 2,
357        max_tiles: 16,
358        max_tile_cols: 6,
359    },
360    LevelSpecification {
361        id: Level::L3_1,
362        max_picture_size: 1065024,
363        max_horizontal: 3096,
364        max_vertical: 5504,
365        max_display_rate: 31950720,
366        max_decode_rate: 39938400,
367        max_header_rate: 150,
368        max_bit_rate_main: 10_000_000,
369        max_bit_rate_high: None,
370        min_comp_basis: 2,
371        max_tiles: 16,
372        max_tile_cols: 6,
373    },
374    LevelSpecification {
375        id: Level::L4,
376        max_picture_size: 2359296,
377        max_horizontal: 3456,
378        max_vertical: 6144,
379        max_display_rate: 70778880,
380        max_decode_rate: 77856768,
381        max_header_rate: 300,
382        max_bit_rate_main: 12_000_000,
383        max_bit_rate_high: Some(30_000_000),
384        min_comp_basis: 4,
385        max_tiles: 32,
386        max_tile_cols: 8,
387    },
388    LevelSpecification {
389        id: Level::L4_1,
390        max_picture_size: 2359296,
391        max_horizontal: 3456,
392        max_vertical: 6144,
393        max_display_rate: 141557760,
394        max_decode_rate: 155713536,
395        max_header_rate: 300,
396        max_bit_rate_main: 20_000_000,
397        max_bit_rate_high: Some(50_000_000),
398        min_comp_basis: 4,
399        max_tiles: 32,
400        max_tile_cols: 8,
401    },
402    LevelSpecification {
403        id: Level::L5,
404        max_picture_size: 8912896,
405        max_horizontal: 4352,
406        max_vertical: 8192,
407        max_display_rate: 267386880,
408        max_decode_rate: 273715200,
409        max_header_rate: 300,
410        max_bit_rate_main: 30_000_000,
411        max_bit_rate_high: Some(100_000_000),
412        min_comp_basis: 6,
413        max_tiles: 64,
414        max_tile_cols: 8,
415    },
416    LevelSpecification {
417        id: Level::L5_1,
418        max_picture_size: 8912896,
419        max_horizontal: 4352,
420        max_vertical: 8192,
421        max_display_rate: 534773760,
422        max_decode_rate: 547430400,
423        max_header_rate: 300,
424        max_bit_rate_main: 40_000_000,
425        max_bit_rate_high: Some(160_000_000),
426        min_comp_basis: 8,
427        max_tiles: 64,
428        max_tile_cols: 8,
429    },
430    LevelSpecification {
431        id: Level::L5_2,
432        max_picture_size: 8912896,
433        max_horizontal: 4352,
434        max_vertical: 8192,
435        max_display_rate: 1069547520,
436        max_decode_rate: 1094860800,
437        max_header_rate: 300,
438        max_bit_rate_main: 60_000_000,
439        max_bit_rate_high: Some(240_000_000),
440        min_comp_basis: 8,
441        max_tiles: 64,
442        max_tile_cols: 8,
443    },
444    LevelSpecification {
445        id: Level::L5_3,
446        max_picture_size: 8912896,
447        max_horizontal: 4352,
448        max_vertical: 8192,
449        max_display_rate: 1069547520,
450        max_decode_rate: 1176502272,
451        max_header_rate: 300,
452        max_bit_rate_main: 60_000_000,
453        max_bit_rate_high: Some(240_000_000),
454        min_comp_basis: 8,
455        max_tiles: 64,
456        max_tile_cols: 8,
457    },
458    LevelSpecification {
459        id: Level::L6,
460        max_picture_size: 35651584,
461        max_horizontal: 8704,
462        max_vertical: 16384,
463        max_display_rate: 1069547520,
464        max_decode_rate: 1176502272,
465        max_header_rate: 300,
466        max_bit_rate_main: 60_000_000,
467        max_bit_rate_high: Some(240_000_000),
468        min_comp_basis: 8,
469        max_tiles: 128,
470        max_tile_cols: 16,
471    },
472    LevelSpecification {
473        id: Level::L6_1,
474        max_picture_size: 35651584,
475        max_horizontal: 8704,
476        max_vertical: 16384,
477        max_display_rate: 2139095040,
478        max_decode_rate: 2189721600,
479        max_header_rate: 300,
480        max_bit_rate_main: 100_000_000,
481        max_bit_rate_high: Some(480_000_000),
482        min_comp_basis: 8,
483        max_tiles: 128,
484        max_tile_cols: 16,
485    },
486    LevelSpecification {
487        id: Level::L6_2,
488        max_picture_size: 35651584,
489        max_horizontal: 8704,
490        max_vertical: 16384,
491        max_display_rate: 4278190080,
492        max_decode_rate: 4379443200,
493        max_header_rate: 300,
494        max_bit_rate_main: 160_000_000,
495        max_bit_rate_high: Some(800_000_000),
496        min_comp_basis: 8,
497        max_tiles: 128,
498        max_tile_cols: 16,
499    },
500    LevelSpecification {
501        id: Level::L6_3,
502        max_picture_size: 35651584,
503        max_horizontal: 8704,
504        max_vertical: 16384,
505        max_display_rate: 4278190080,
506        max_decode_rate: 4706009088,
507        max_header_rate: 300,
508        max_bit_rate_main: 160_000_000,
509        max_bit_rate_high: Some(800_000_000),
510        min_comp_basis: 8,
511        max_tiles: 128,
512        max_tile_cols: 16,
513    },
514];
515#[cfg(test)]
516mod tests {
517    #[test]
518    fn level_mult() {
519        use crate::av1::Level;
520        assert_eq!(Level::L3, Level::from(4));
521    }
522
523    #[test]
524    fn max_bitrate() {
525        use crate::av1::{self, Level, Tier};
526
527        let l = av1::get(Level::L3);
528        assert_eq!(l.max_bit_rate(Tier::Main), Some(6_000_000));
529        assert_eq!(l.max_bit_rate(Tier::High), None);
530
531        let l = av1::get(Level::L5_2);
532        assert_eq!(l.max_bit_rate(Tier::Main), Some(60_000_000));
533        assert_eq!(l.max_bit_rate(Tier::High), Some(240_000_000));
534    }
535
536    #[test]
537    fn select_base_cases() {
538        use crate::av1::{Level, LevelSelector, Profile, Tier};
539        assert_eq!(
540            LevelSelector::new()
541                .width(1920)
542                .height(1080)
543                .framerate(30.0)
544                .tier(Tier::Main)
545                .profile(Profile::Main)
546                .select()
547                .unwrap()
548                .id(),
549            Level::L4
550        );
551
552        assert_eq!(
553            LevelSelector::new()
554                .width(4380)
555                .height(1080)
556                .framerate(30.0)
557                .tier(Tier::Main)
558                .profile(Profile::Main)
559                .select()
560                .unwrap()
561                .id(),
562            Level::L6
563        );
564
565        assert_eq!(
566            LevelSelector::new()
567                .width(3840)
568                .height(2160)
569                .framerate(200.0)
570                .tier(Tier::Main)
571                .profile(Profile::Main)
572                .select()
573                .unwrap()
574                .id(),
575            Level::L6_1
576        );
577    }
578}