1use crate::error::{CodecError, CodecResult};
8use oximedia_io::BitReader;
9
10#[derive(Clone, Debug)]
12#[allow(clippy::struct_excessive_bools)]
13pub struct SequenceHeader {
14 pub profile: u8,
16 pub still_picture: bool,
18 pub reduced_still_picture_header: bool,
20 pub max_frame_width_minus_1: u32,
22 pub max_frame_height_minus_1: u32,
24 pub enable_order_hint: bool,
26 pub order_hint_bits: u8,
28 pub enable_superres: bool,
30 pub enable_cdef: bool,
32 pub enable_restoration: bool,
34 pub color_config: ColorConfig,
36 pub film_grain_params_present: bool,
38}
39
40impl SequenceHeader {
41 #[must_use]
43 pub const fn max_frame_width(&self) -> u32 {
44 self.max_frame_width_minus_1 + 1
45 }
46
47 #[must_use]
49 pub const fn max_frame_height(&self) -> u32 {
50 self.max_frame_height_minus_1 + 1
51 }
52
53 #[allow(clippy::too_many_lines, clippy::cast_possible_truncation)]
59 pub fn parse(data: &[u8]) -> CodecResult<Self> {
60 let mut reader = BitReader::new(data);
61
62 let profile = reader.read_bits(3).map_err(CodecError::Core)? as u8;
63 if profile > 2 {
64 return Err(CodecError::InvalidBitstream(format!(
65 "Invalid profile: {profile}"
66 )));
67 }
68
69 let still_picture = reader.read_bit().map_err(CodecError::Core)? != 0;
70 let reduced_still_picture_header = reader.read_bit().map_err(CodecError::Core)? != 0;
71
72 if reduced_still_picture_header && !still_picture {
73 return Err(CodecError::InvalidBitstream(
74 "reduced_still_picture_header requires still_picture".to_string(),
75 ));
76 }
77
78 if reduced_still_picture_header {
80 reader.read_bits(5).map_err(CodecError::Core)?; } else {
82 let timing_info_present = reader.read_bit().map_err(CodecError::Core)? != 0;
84 if timing_info_present {
85 reader.skip_bits(64); let decoder_model_info_present = reader.read_bit().map_err(CodecError::Core)? != 0;
87 if decoder_model_info_present {
88 reader.skip_bits(47); }
90 }
91 reader.read_bit().map_err(CodecError::Core)?;
93 let op_count = reader.read_bits(5).map_err(CodecError::Core)? as usize + 1;
95 for _ in 0..op_count {
96 reader.skip_bits(12); let level = reader.read_bits(5).map_err(CodecError::Core)? as u8;
98 if level > 7 {
99 reader.skip_bits(1); }
101 }
102 }
103
104 let frame_width_bits = reader.read_bits(4).map_err(CodecError::Core)? as u8 + 1;
105 let frame_height_bits = reader.read_bits(4).map_err(CodecError::Core)? as u8 + 1;
106 let max_frame_width_minus_1 = reader
107 .read_bits(frame_width_bits)
108 .map_err(CodecError::Core)? as u32;
109 let max_frame_height_minus_1 = reader
110 .read_bits(frame_height_bits)
111 .map_err(CodecError::Core)? as u32;
112
113 let enable_order_hint;
114 let order_hint_bits;
115 let enable_superres;
116 let enable_cdef;
117 let enable_restoration;
118
119 if reduced_still_picture_header {
120 enable_order_hint = false;
121 order_hint_bits = 0;
122 enable_superres = false;
123 enable_cdef = false;
124 enable_restoration = false;
125 } else {
126 let frame_id_present = reader.read_bit().map_err(CodecError::Core)? != 0;
128 if frame_id_present {
129 reader.skip_bits(7); }
131
132 reader.skip_bits(7); enable_order_hint = reader.read_bit().map_err(CodecError::Core)? != 0;
136
137 if enable_order_hint {
138 reader.skip_bits(2); }
140
141 let seq_choose_screen_content_tools = reader.read_bit().map_err(CodecError::Core)? != 0;
143 if !seq_choose_screen_content_tools {
144 reader.skip_bits(1);
145 }
146
147 let seq_choose_integer_mv = reader.read_bit().map_err(CodecError::Core)? != 0;
149 if !seq_choose_integer_mv {
150 reader.skip_bits(1);
151 }
152
153 order_hint_bits = if enable_order_hint {
154 reader.read_bits(3).map_err(CodecError::Core)? as u8 + 1
155 } else {
156 0
157 };
158
159 enable_superres = reader.read_bit().map_err(CodecError::Core)? != 0;
160 enable_cdef = reader.read_bit().map_err(CodecError::Core)? != 0;
161 enable_restoration = reader.read_bit().map_err(CodecError::Core)? != 0;
162 }
163
164 let color_config = ColorConfig::parse(&mut reader, profile)?;
165 let film_grain_params_present = reader.read_bit().map_err(CodecError::Core)? != 0;
166
167 Ok(Self {
168 profile,
169 still_picture,
170 reduced_still_picture_header,
171 max_frame_width_minus_1,
172 max_frame_height_minus_1,
173 enable_order_hint,
174 order_hint_bits,
175 enable_superres,
176 enable_cdef,
177 enable_restoration,
178 color_config,
179 film_grain_params_present,
180 })
181 }
182}
183
184#[derive(Clone, Debug, Default)]
186#[allow(clippy::struct_excessive_bools)]
187pub struct ColorConfig {
188 pub bit_depth: u8,
190 pub mono_chrome: bool,
192 pub num_planes: u8,
194 pub color_primaries: u8,
196 pub transfer_characteristics: u8,
198 pub matrix_coefficients: u8,
200 pub color_range: bool,
202 pub subsampling_x: bool,
204 pub subsampling_y: bool,
206 pub separate_uv_delta_q: bool,
208}
209
210impl ColorConfig {
211 #[must_use]
213 pub const fn is_420(&self) -> bool {
214 self.subsampling_x && self.subsampling_y
215 }
216
217 #[allow(clippy::cast_possible_truncation)]
219 fn parse(reader: &mut BitReader<'_>, profile: u8) -> CodecResult<Self> {
220 let high_bitdepth = reader.read_bit().map_err(CodecError::Core)? != 0;
221
222 let twelve_bit = if profile == 2 && high_bitdepth {
223 reader.read_bit().map_err(CodecError::Core)? != 0
224 } else {
225 false
226 };
227
228 let bit_depth = if profile == 2 && twelve_bit {
229 12
230 } else if high_bitdepth {
231 10
232 } else {
233 8
234 };
235
236 let mono_chrome = if profile == 1 {
237 false
238 } else {
239 reader.read_bit().map_err(CodecError::Core)? != 0
240 };
241
242 let num_planes = if mono_chrome { 1 } else { 3 };
243
244 let color_description_present = reader.read_bit().map_err(CodecError::Core)? != 0;
245
246 let (color_primaries, transfer_characteristics, matrix_coefficients) =
247 if color_description_present {
248 let cp = reader.read_bits(8).map_err(CodecError::Core)? as u8;
249 let tc = reader.read_bits(8).map_err(CodecError::Core)? as u8;
250 let mc = reader.read_bits(8).map_err(CodecError::Core)? as u8;
251 (cp, tc, mc)
252 } else {
253 (2, 2, 2)
254 };
255
256 let color_range;
257 let subsampling_x;
258 let subsampling_y;
259
260 if mono_chrome {
261 color_range = reader.read_bit().map_err(CodecError::Core)? != 0;
262 subsampling_x = true;
263 subsampling_y = true;
264 } else if color_primaries == 1 && transfer_characteristics == 13 && matrix_coefficients == 0
265 {
266 color_range = true;
267 subsampling_x = false;
268 subsampling_y = false;
269 } else {
270 color_range = reader.read_bit().map_err(CodecError::Core)? != 0;
271
272 if profile == 0 {
273 subsampling_x = true;
274 subsampling_y = true;
275 } else if profile == 1 {
276 subsampling_x = false;
277 subsampling_y = false;
278 } else if bit_depth == 12 {
279 subsampling_x = reader.read_bit().map_err(CodecError::Core)? != 0;
280 subsampling_y = if subsampling_x {
281 reader.read_bit().map_err(CodecError::Core)? != 0
282 } else {
283 false
284 };
285 } else {
286 subsampling_x = true;
287 subsampling_y = false;
288 }
289
290 if subsampling_x && subsampling_y {
291 reader.skip_bits(2); }
293 }
294
295 let separate_uv_delta_q = if mono_chrome {
296 false
297 } else {
298 reader.read_bit().map_err(CodecError::Core)? != 0
299 };
300
301 Ok(Self {
302 bit_depth,
303 mono_chrome,
304 num_planes,
305 color_primaries,
306 transfer_characteristics,
307 matrix_coefficients,
308 color_range,
309 subsampling_x,
310 subsampling_y,
311 separate_uv_delta_q,
312 })
313 }
314}
315
316pub const MAX_TEMPORAL_LAYERS: usize = 8;
322
323pub const MAX_SPATIAL_LAYERS: usize = 4;
325
326pub const MAX_OPERATING_POINTS: usize = MAX_TEMPORAL_LAYERS * MAX_SPATIAL_LAYERS;
328
329#[derive(Clone, Debug)]
359pub struct SvcConfig {
360 pub num_temporal_layers: u8,
362 pub num_spatial_layers: u8,
364 pub temporal_layers: Vec<TemporalLayerConfig>,
366 pub spatial_layers: Vec<SpatialLayerConfig>,
368 pub operating_points: Vec<OperatingPoint>,
370 pub inter_layer_prediction: bool,
372}
373
374impl SvcConfig {
375 #[must_use]
382 pub fn new(temporal_layers: u8, spatial_layers: u8) -> Self {
383 let num_t = temporal_layers.clamp(1, MAX_TEMPORAL_LAYERS as u8);
384 let num_s = spatial_layers.clamp(1, MAX_SPATIAL_LAYERS as u8);
385
386 let mut config = Self {
387 num_temporal_layers: num_t,
388 num_spatial_layers: num_s,
389 temporal_layers: Vec::with_capacity(num_t as usize),
390 spatial_layers: Vec::with_capacity(num_s as usize),
391 operating_points: Vec::new(),
392 inter_layer_prediction: true,
393 };
394
395 for t in 0..num_t {
397 let fraction = 1.0 / (1 << (num_t - 1 - t)) as f32;
398 let bitrate_frac = Self::default_bitrate_fraction(t, num_t);
399 config.temporal_layers.push(TemporalLayerConfig {
400 layer_id: t,
401 framerate_fraction: fraction,
402 bitrate_fraction: bitrate_frac,
403 qp_offset: t as i8 * 2,
404 reference_mode: if t == 0 {
405 SvcReferenceMode::KeyAndPrevious
406 } else {
407 SvcReferenceMode::PreviousLayer
408 },
409 });
410 }
411
412 for s in 0..num_s {
414 let scale = 1.0 / (1 << (num_s - 1 - s)) as f32;
415 config.spatial_layers.push(SpatialLayerConfig {
416 layer_id: s,
417 width_scale: scale,
418 height_scale: scale,
419 bitrate_fraction: 1.0 / num_s as f32,
420 });
421 }
422
423 config.build_operating_points();
424 config
425 }
426
427 fn default_bitrate_fraction(layer: u8, total: u8) -> f32 {
429 if total <= 1 {
430 return 1.0;
431 }
432 let weight = (1 << (total - 1 - layer)) as f32;
435 let total_weight: f32 = (0..total).map(|t| (1 << (total - 1 - t)) as f32).sum();
436 weight / total_weight
437 }
438
439 pub fn set_temporal_layer(&mut self, layer_id: u8, config: TemporalLayerConfig) {
446 let idx = layer_id as usize;
447 if idx < self.temporal_layers.len() {
448 self.temporal_layers[idx] = config;
449 self.build_operating_points();
450 }
451 }
452
453 pub fn set_spatial_layer(&mut self, layer_id: u8, config: SpatialLayerConfig) {
455 let idx = layer_id as usize;
456 if idx < self.spatial_layers.len() {
457 self.spatial_layers[idx] = config;
458 self.build_operating_points();
459 }
460 }
461
462 fn build_operating_points(&mut self) {
467 self.operating_points.clear();
468
469 for s in 0..self.num_spatial_layers {
471 for t in 0..self.num_temporal_layers {
472 let temporal_mask: u16 = (1 << (t + 1)) - 1; let spatial_mask: u16 = ((1u16 << (s + 1)) - 1) << 8;
474 let idc = temporal_mask | spatial_mask;
475
476 let cumulative_bitrate: f32 = self
477 .temporal_layers
478 .iter()
479 .take((t + 1) as usize)
480 .map(|l| l.bitrate_fraction)
481 .sum();
482
483 let framerate: f32 = self
484 .temporal_layers
485 .get(t as usize)
486 .map_or(1.0, |l| l.framerate_fraction);
487
488 self.operating_points.push(OperatingPoint {
489 idc,
490 temporal_id: t,
491 spatial_id: s,
492 level: Self::estimate_level(t, s),
493 tier: 0, cumulative_bitrate_fraction: cumulative_bitrate,
495 cumulative_framerate_fraction: framerate,
496 });
497 }
498 }
499 }
500
501 fn estimate_level(temporal_id: u8, spatial_id: u8) -> u8 {
503 let base = 0u8; base.saturating_add(temporal_id)
507 .saturating_add(spatial_id * 2)
508 }
509
510 #[must_use]
512 pub fn get_operating_point(&self, temporal_id: u8, spatial_id: u8) -> Option<&OperatingPoint> {
513 self.operating_points
514 .iter()
515 .find(|op| op.temporal_id == temporal_id && op.spatial_id == spatial_id)
516 }
517
518 #[must_use]
520 pub fn num_operating_points(&self) -> usize {
521 self.operating_points.len()
522 }
523
524 #[must_use]
531 pub fn frame_temporal_layer(&self, frame_index: u64) -> u8 {
532 if self.num_temporal_layers <= 1 {
533 return 0;
534 }
535
536 let n = self.num_temporal_layers;
537 let period = 1u64 << (n - 1);
538
539 if frame_index % period == 0 {
540 return 0; }
542
543 for layer in 1..n {
545 let step = period >> layer;
546 if step > 0 && frame_index % step == 0 {
547 return layer;
548 }
549 }
550
551 n - 1 }
553
554 #[must_use]
556 pub fn frame_qp_offset(&self, frame_index: u64) -> i8 {
557 let layer = self.frame_temporal_layer(frame_index);
558 self.temporal_layers
559 .get(layer as usize)
560 .map_or(0, |l| l.qp_offset)
561 }
562
563 #[must_use]
566 pub fn is_droppable(&self, frame_index: u64) -> bool {
567 self.frame_temporal_layer(frame_index) > 0
568 }
569
570 #[must_use]
572 pub fn frame_reference_mode(&self, frame_index: u64) -> SvcReferenceMode {
573 let layer = self.frame_temporal_layer(frame_index);
574 self.temporal_layers
575 .get(layer as usize)
576 .map_or(SvcReferenceMode::KeyAndPrevious, |l| l.reference_mode)
577 }
578
579 pub fn validate(&self) -> CodecResult<()> {
585 if self.num_temporal_layers == 0 || self.num_temporal_layers > MAX_TEMPORAL_LAYERS as u8 {
586 return Err(CodecError::InvalidParameter(format!(
587 "Invalid temporal layer count: {}",
588 self.num_temporal_layers
589 )));
590 }
591
592 if self.num_spatial_layers == 0 || self.num_spatial_layers > MAX_SPATIAL_LAYERS as u8 {
593 return Err(CodecError::InvalidParameter(format!(
594 "Invalid spatial layer count: {}",
595 self.num_spatial_layers
596 )));
597 }
598
599 let total_bitrate: f32 = self
601 .temporal_layers
602 .iter()
603 .map(|l| l.bitrate_fraction)
604 .sum();
605 if (total_bitrate - 1.0).abs() > 0.01 {
606 return Err(CodecError::InvalidParameter(format!(
607 "Temporal bitrate fractions sum to {total_bitrate}, expected ~1.0"
608 )));
609 }
610
611 for i in 1..self.temporal_layers.len() {
613 if self.temporal_layers[i].framerate_fraction
614 < self.temporal_layers[i - 1].framerate_fraction
615 {
616 return Err(CodecError::InvalidParameter(
617 "Temporal framerate fractions must be non-decreasing".to_string(),
618 ));
619 }
620 }
621
622 Ok(())
623 }
624}
625
626#[derive(Clone, Debug)]
628pub struct TemporalLayerConfig {
629 pub layer_id: u8,
631 pub framerate_fraction: f32,
633 pub bitrate_fraction: f32,
635 pub qp_offset: i8,
637 pub reference_mode: SvcReferenceMode,
639}
640
641#[derive(Clone, Debug)]
643pub struct SpatialLayerConfig {
644 pub layer_id: u8,
646 pub width_scale: f32,
648 pub height_scale: f32,
650 pub bitrate_fraction: f32,
652}
653
654#[derive(Clone, Copy, Debug, PartialEq, Eq)]
656pub enum SvcReferenceMode {
657 KeyOnly,
659 KeyAndPrevious,
661 PreviousLayer,
663 Any,
665}
666
667#[derive(Clone, Debug)]
669pub struct OperatingPoint {
670 pub idc: u16,
672 pub temporal_id: u8,
674 pub spatial_id: u8,
676 pub level: u8,
678 pub tier: u8,
680 pub cumulative_bitrate_fraction: f32,
682 pub cumulative_framerate_fraction: f32,
684}
685
686impl OperatingPoint {
687 #[must_use]
689 pub fn compute_idc(max_temporal_id: u8, max_spatial_id: u8) -> u16 {
690 let temporal_mask: u16 = (1 << (max_temporal_id + 1)) - 1;
691 let spatial_mask: u16 = ((1u16 << (max_spatial_id + 1)) - 1) << 8;
692 temporal_mask | spatial_mask
693 }
694
695 #[must_use]
697 pub fn includes_temporal(&self, temporal_id: u8) -> bool {
698 (self.idc & (1 << temporal_id)) != 0
699 }
700
701 #[must_use]
703 pub fn includes_spatial(&self, spatial_id: u8) -> bool {
704 (self.idc & (1 << (spatial_id + 8))) != 0
705 }
706}
707
708#[cfg(test)]
709mod tests {
710 use super::*;
711
712 #[test]
713 fn test_sequence_header_dimensions() {
714 let header = SequenceHeader {
715 profile: 0,
716 still_picture: false,
717 reduced_still_picture_header: false,
718 max_frame_width_minus_1: 1919,
719 max_frame_height_minus_1: 1079,
720 enable_order_hint: false,
721 order_hint_bits: 0,
722 enable_superres: false,
723 enable_cdef: false,
724 enable_restoration: false,
725 color_config: ColorConfig::default(),
726 film_grain_params_present: false,
727 };
728
729 assert_eq!(header.max_frame_width(), 1920);
730 assert_eq!(header.max_frame_height(), 1080);
731 }
732
733 #[test]
734 fn test_color_config_subsampling() {
735 let config_420 = ColorConfig {
736 subsampling_x: true,
737 subsampling_y: true,
738 ..Default::default()
739 };
740 assert!(config_420.is_420());
741 }
742
743 #[test]
748 fn test_svc_config_creation() {
749 let svc = SvcConfig::new(3, 1);
750 assert_eq!(svc.num_temporal_layers, 3);
751 assert_eq!(svc.num_spatial_layers, 1);
752 assert_eq!(svc.temporal_layers.len(), 3);
753 assert_eq!(svc.spatial_layers.len(), 1);
754 assert!(svc.inter_layer_prediction);
755 }
756
757 #[test]
758 fn test_svc_config_clamping() {
759 let svc = SvcConfig::new(0, 10);
760 assert_eq!(svc.num_temporal_layers, 1);
761 assert_eq!(svc.num_spatial_layers, 4);
762 }
763
764 #[test]
765 fn test_svc_single_layer() {
766 let svc = SvcConfig::new(1, 1);
767 assert_eq!(svc.num_operating_points(), 1);
768 assert_eq!(svc.frame_temporal_layer(0), 0);
769 assert_eq!(svc.frame_temporal_layer(1), 0);
770 assert!(!svc.is_droppable(0));
771 }
772
773 #[test]
774 fn test_svc_two_temporal_layers() {
775 let svc = SvcConfig::new(2, 1);
776 assert_eq!(svc.num_temporal_layers, 2);
777
778 assert_eq!(svc.frame_temporal_layer(0), 0);
780 assert_eq!(svc.frame_temporal_layer(1), 1);
781 assert_eq!(svc.frame_temporal_layer(2), 0);
782 assert_eq!(svc.frame_temporal_layer(3), 1);
783
784 assert!(!svc.is_droppable(0));
785 assert!(svc.is_droppable(1));
786 assert!(!svc.is_droppable(2));
787 }
788
789 #[test]
790 fn test_svc_three_temporal_layers() {
791 let svc = SvcConfig::new(3, 1);
792
793 assert_eq!(svc.frame_temporal_layer(0), 0);
798 assert_eq!(svc.frame_temporal_layer(1), 2);
799 assert_eq!(svc.frame_temporal_layer(2), 1);
800 assert_eq!(svc.frame_temporal_layer(3), 2);
801 assert_eq!(svc.frame_temporal_layer(4), 0);
802 }
803
804 #[test]
805 fn test_svc_operating_points() {
806 let svc = SvcConfig::new(3, 2);
807 assert_eq!(svc.num_operating_points(), 6);
809
810 let base_op = svc.get_operating_point(0, 0);
812 assert!(base_op.is_some());
813 let base = base_op.expect("base operating point exists");
814 assert_eq!(base.temporal_id, 0);
815 assert_eq!(base.spatial_id, 0);
816 assert!(base.includes_temporal(0));
817 assert!(!base.includes_temporal(1));
818 }
819
820 #[test]
821 fn test_svc_operating_point_idc() {
822 let idc = OperatingPoint::compute_idc(1, 0);
824 assert_eq!(idc, 0x0103); }
826
827 #[test]
828 fn test_svc_qp_offset() {
829 let svc = SvcConfig::new(3, 1);
830
831 let qp0 = svc.frame_qp_offset(0); let qp1 = svc.frame_qp_offset(2); let qp2 = svc.frame_qp_offset(1); assert!(qp0 <= qp1);
838 assert!(qp1 <= qp2);
839 }
840
841 #[test]
842 fn test_svc_bitrate_fractions() {
843 let svc = SvcConfig::new(3, 1);
844
845 let total: f32 = svc.temporal_layers.iter().map(|l| l.bitrate_fraction).sum();
846 assert!((total - 1.0).abs() < 0.01);
847
848 assert!(svc.temporal_layers[0].bitrate_fraction > svc.temporal_layers[1].bitrate_fraction);
850 assert!(svc.temporal_layers[1].bitrate_fraction > svc.temporal_layers[2].bitrate_fraction);
851 }
852
853 #[test]
854 fn test_svc_framerate_fractions() {
855 let svc = SvcConfig::new(3, 1);
856
857 for i in 1..svc.temporal_layers.len() {
859 assert!(
860 svc.temporal_layers[i].framerate_fraction
861 >= svc.temporal_layers[i - 1].framerate_fraction
862 );
863 }
864 }
865
866 #[test]
867 fn test_svc_validation_valid() {
868 let svc = SvcConfig::new(3, 1);
869 assert!(svc.validate().is_ok());
870 }
871
872 #[test]
873 fn test_svc_validation_bad_bitrate() {
874 let mut svc = SvcConfig::new(2, 1);
875 svc.temporal_layers[0].bitrate_fraction = 0.1;
876 svc.temporal_layers[1].bitrate_fraction = 0.1;
877 assert!(svc.validate().is_err());
879 }
880
881 #[test]
882 fn test_svc_set_temporal_layer() {
883 let mut svc = SvcConfig::new(3, 1);
884 svc.set_temporal_layer(
885 1,
886 TemporalLayerConfig {
887 layer_id: 1,
888 framerate_fraction: 0.5,
889 bitrate_fraction: 0.3,
890 qp_offset: 4,
891 reference_mode: SvcReferenceMode::Any,
892 },
893 );
894
895 assert_eq!(svc.temporal_layers[1].qp_offset, 4);
896 assert_eq!(svc.temporal_layers[1].reference_mode, SvcReferenceMode::Any);
897 }
898
899 #[test]
900 fn test_svc_spatial_layer_defaults() {
901 let svc = SvcConfig::new(2, 2);
902
903 assert_eq!(svc.spatial_layers.len(), 2);
904 assert!(svc.spatial_layers[0].width_scale < svc.spatial_layers[1].width_scale);
906 }
907
908 #[test]
909 fn test_svc_reference_mode() {
910 let svc = SvcConfig::new(3, 1);
911
912 let ref0 = svc.frame_reference_mode(0); let ref2 = svc.frame_reference_mode(1); assert_eq!(ref0, SvcReferenceMode::KeyAndPrevious);
917 assert_eq!(ref2, SvcReferenceMode::PreviousLayer);
918 }
919
920 #[test]
921 fn test_operating_point_includes() {
922 let op = OperatingPoint {
923 idc: 0x0107, temporal_id: 2,
925 spatial_id: 0,
926 level: 2,
927 tier: 0,
928 cumulative_bitrate_fraction: 1.0,
929 cumulative_framerate_fraction: 1.0,
930 };
931
932 assert!(op.includes_temporal(0));
933 assert!(op.includes_temporal(1));
934 assert!(op.includes_temporal(2));
935 assert!(!op.includes_temporal(3));
936 assert!(op.includes_spatial(0));
937 assert!(!op.includes_spatial(1));
938 }
939
940 #[test]
941 fn test_svc_droppable_frames() {
942 let svc = SvcConfig::new(3, 1);
943
944 let droppable: Vec<bool> = (0..8).map(|i| svc.is_droppable(i)).collect();
946
947 assert!(!droppable[0]); assert!(droppable[1]); assert!(droppable[2]); assert!(droppable[3]); assert!(!droppable[4]); }
954}