1#![forbid(unsafe_code)]
29#![allow(dead_code)]
30#![allow(clippy::doc_markdown)]
31#![allow(clippy::too_many_arguments)]
32#![allow(clippy::cast_possible_truncation)]
33#![allow(clippy::cast_sign_loss)]
34#![allow(clippy::cast_possible_wrap)]
35#![allow(clippy::similar_names)]
36#![allow(clippy::module_name_repetitions)]
37#![allow(clippy::struct_excessive_bools)]
38
39use super::block::{BlockModeInfo, InterMode, IntraMode as Av1IntraMode};
40use crate::error::{CodecError, CodecResult};
41use crate::frame::VideoFrame;
42use crate::intra::{
43 BitDepth, BlockDimensions, DcPredictor, DirectionalPredictor, HorizontalPredictor, IntraMode,
44 IntraPredContext, IntraPredictor, PaethPredictor, SmoothHPredictor, SmoothPredictor,
45 SmoothVPredictor, VerticalPredictor,
46};
47
48pub const SUBPEL_BITS: u8 = 3;
54
55pub const SUBPEL_SCALE: i32 = 1 << SUBPEL_BITS;
57
58pub const INTERP_TAPS: usize = 8;
60
61pub const MAX_OBMC_SIZE: usize = 128;
63
64pub const WARP_PARAMS: usize = 6;
66
67#[derive(Debug)]
73pub struct PredictionEngine {
74 current_frame: Option<VideoFrame>,
76 reference_frames: Vec<Option<VideoFrame>>,
78 bit_depth: u8,
80 intra_context: IntraPredContext,
82 mc_buffer: Vec<u16>,
84}
85
86impl PredictionEngine {
87 pub fn new(width: u32, height: u32, bit_depth: u8) -> Self {
89 let intra_bd = match bit_depth {
90 8 => BitDepth::Bits8,
91 10 => BitDepth::Bits10,
92 12 => BitDepth::Bits12,
93 _ => BitDepth::Bits8,
94 };
95
96 Self {
97 current_frame: None,
98 reference_frames: vec![None; 8],
99 bit_depth,
100 intra_context: IntraPredContext::new(width as usize, height as usize, intra_bd),
101 mc_buffer: vec![0; MAX_OBMC_SIZE * MAX_OBMC_SIZE],
102 }
103 }
104
105 pub fn predict_block(
107 &mut self,
108 mode_info: &BlockModeInfo,
109 x: u32,
110 y: u32,
111 plane: u8,
112 dst: &mut [u16],
113 stride: usize,
114 ) -> CodecResult<()> {
115 if mode_info.is_inter {
116 self.predict_inter(mode_info, x, y, plane, dst, stride)
117 } else {
118 self.predict_intra(mode_info, x, y, plane, dst, stride)
119 }
120 }
121
122 fn predict_intra(
124 &mut self,
125 mode_info: &BlockModeInfo,
126 _x: u32,
127 _y: u32,
128 plane: u8,
129 dst: &mut [u16],
130 stride: usize,
131 ) -> CodecResult<()> {
132 let bsize = mode_info.block_size;
133 let width = bsize.width() as usize;
134 let height = bsize.height() as usize;
135
136 let mode = if plane == 0 {
138 mode_info.intra_mode
139 } else {
140 mode_info.uv_mode
141 };
142
143 let intra_mode = self.map_intra_mode(mode);
145
146 if mode.is_directional() && plane == 0 {
155 let _angle_delta = mode_info.angle_delta[0];
156 }
158
159 self.apply_intra_mode(intra_mode, mode, width, height, dst, stride)?;
161
162 if mode_info.filter_intra_mode > 0 && plane == 0 {
164 self.apply_filter_intra(dst, width, height, stride, mode_info.filter_intra_mode)?;
165 }
166
167 Ok(())
168 }
169
170 fn map_intra_mode(&self, mode: Av1IntraMode) -> IntraMode {
172 match mode {
173 Av1IntraMode::DcPred => IntraMode::Dc,
174 Av1IntraMode::VPred => IntraMode::Vertical,
175 Av1IntraMode::HPred => IntraMode::Horizontal,
176 Av1IntraMode::D45Pred => IntraMode::D45,
177 Av1IntraMode::D135Pred => IntraMode::D135,
178 Av1IntraMode::D113Pred => IntraMode::D113,
179 Av1IntraMode::D157Pred => IntraMode::D157,
180 Av1IntraMode::D203Pred => IntraMode::D203,
181 Av1IntraMode::D67Pred => IntraMode::D67,
182 Av1IntraMode::SmoothPred => IntraMode::Smooth,
183 Av1IntraMode::SmoothVPred => IntraMode::SmoothV,
184 Av1IntraMode::SmoothHPred => IntraMode::SmoothH,
185 Av1IntraMode::PaethPred => IntraMode::Paeth,
186 }
187 }
188
189 fn apply_intra_mode(
191 &self,
192 intra_mode: IntraMode,
193 _av1_mode: Av1IntraMode,
194 width: usize,
195 height: usize,
196 dst: &mut [u16],
197 stride: usize,
198 ) -> CodecResult<()> {
199 let block_dims = BlockDimensions::new(width, height);
200 let bit_depth = self.intra_context.bit_depth();
201
202 match intra_mode {
203 IntraMode::Dc => {
204 let predictor = DcPredictor::new(bit_depth);
205 predictor.predict(&self.intra_context, dst, stride, block_dims);
206 }
207 IntraMode::Vertical => {
208 let predictor = VerticalPredictor::new();
209 predictor.predict(&self.intra_context, dst, stride, block_dims);
210 }
211 IntraMode::Horizontal => {
212 let predictor = HorizontalPredictor::new();
213 predictor.predict(&self.intra_context, dst, stride, block_dims);
214 }
215 IntraMode::Smooth => {
216 let predictor = SmoothPredictor::new();
217 predictor.predict(&self.intra_context, dst, stride, block_dims);
218 }
219 IntraMode::SmoothV => {
220 let predictor = SmoothVPredictor::new();
221 predictor.predict(&self.intra_context, dst, stride, block_dims);
222 }
223 IntraMode::SmoothH => {
224 let predictor = SmoothHPredictor::new();
225 predictor.predict(&self.intra_context, dst, stride, block_dims);
226 }
227 IntraMode::Paeth => {
228 let predictor = PaethPredictor::new();
229 predictor.predict(&self.intra_context, dst, stride, block_dims);
230 }
231 IntraMode::D45
232 | IntraMode::D135
233 | IntraMode::D113
234 | IntraMode::D157
235 | IntraMode::D203
236 | IntraMode::D67 => {
237 let angle = self.intra_mode_to_angle(intra_mode);
239 let bit_depth = self.intra_context.bit_depth();
240 let predictor = DirectionalPredictor::new(angle, bit_depth);
241 predictor.predict(&self.intra_context, dst, stride, block_dims);
242 }
243 IntraMode::FilterIntra => {
244 let predictor = DcPredictor::new(bit_depth);
246 predictor.predict(&self.intra_context, dst, stride, block_dims);
247 }
248 }
249
250 Ok(())
251 }
252
253 fn intra_mode_to_angle(&self, mode: IntraMode) -> u16 {
255 match mode {
256 IntraMode::D45 => 45,
257 IntraMode::D67 => 67,
258 IntraMode::D113 => 113,
259 IntraMode::D135 => 135,
260 IntraMode::D157 => 157,
261 IntraMode::D203 => 203,
262 _ => 0,
263 }
264 }
265
266 fn apply_angle_delta(&mut self, _ctx: &mut IntraPredContext, _delta: i8) {
268 }
271
272 fn apply_filter_intra(
274 &self,
275 dst: &mut [u16],
276 width: usize,
277 height: usize,
278 stride: usize,
279 _mode: u8,
280 ) -> CodecResult<()> {
281 for y in 0..height {
284 for x in 0..width {
285 let idx = y * stride + x;
286 if idx < dst.len() {
287 dst[idx] = self.apply_filter_tap(dst, x, y, width, height, stride);
289 }
290 }
291 }
292 Ok(())
293 }
294
295 fn apply_filter_tap(
297 &self,
298 src: &[u16],
299 x: usize,
300 y: usize,
301 width: usize,
302 height: usize,
303 stride: usize,
304 ) -> u16 {
305 let mut sum = 0u32;
306 let mut count = 0u32;
307
308 for dy in -1i32..=1 {
310 for dx in -1i32..=1 {
311 let nx = (x as i32 + dx) as usize;
312 let ny = (y as i32 + dy) as usize;
313
314 if nx < width && ny < height {
315 let idx = ny * stride + nx;
316 if idx < src.len() {
317 sum += u32::from(src[idx]);
318 count += 1;
319 }
320 }
321 }
322 }
323
324 sum.checked_div(count)
325 .map_or(src[y * stride + x], |v| v as u16)
326 }
327
328 fn predict_inter(
330 &mut self,
331 mode_info: &BlockModeInfo,
332 x: u32,
333 y: u32,
334 plane: u8,
335 dst: &mut [u16],
336 stride: usize,
337 ) -> CodecResult<()> {
338 let bsize = mode_info.block_size;
339 let width = bsize.width() as usize;
340 let height = bsize.height() as usize;
341
342 if mode_info.is_compound() {
343 self.predict_compound(mode_info, x, y, plane, dst, stride, width, height)
345 } else {
346 self.predict_single_ref(mode_info, x, y, plane, dst, stride, width, height)
348 }
349 }
350
351 fn predict_single_ref(
353 &mut self,
354 mode_info: &BlockModeInfo,
355 x: u32,
356 y: u32,
357 plane: u8,
358 dst: &mut [u16],
359 stride: usize,
360 width: usize,
361 height: usize,
362 ) -> CodecResult<()> {
363 let ref_idx = mode_info.ref_frames[0];
364 if ref_idx < 0 || ref_idx >= self.reference_frames.len() as i8 {
365 return Err(CodecError::InvalidBitstream(
366 "Invalid reference frame".to_string(),
367 ));
368 }
369
370 let ref_frame = &self.reference_frames[ref_idx as usize];
371 if ref_frame.is_none() {
372 return Err(CodecError::InvalidBitstream(
373 "Reference frame not available".to_string(),
374 ));
375 }
376
377 let mv = self.get_motion_vector(mode_info, 0);
379
380 let ref_frame_inner = ref_frame.as_ref().ok_or_else(|| {
382 CodecError::InvalidBitstream("Reference frame not available".to_string())
383 })?;
384 self.motion_compensate(
385 ref_frame_inner,
386 x,
387 y,
388 mv,
389 plane,
390 dst,
391 stride,
392 width,
393 height,
394 mode_info.interp_filter[0],
395 )?;
396
397 if mode_info.motion_mode == 1 {
399 self.apply_obmc(mode_info, x, y, plane, dst, stride, width, height)?;
400 }
401
402 if mode_info.motion_mode == 2 {
404 self.apply_warped_motion(mode_info, x, y, plane, dst, stride, width, height)?;
405 }
406
407 Ok(())
408 }
409
410 fn predict_compound(
412 &mut self,
413 mode_info: &BlockModeInfo,
414 x: u32,
415 y: u32,
416 plane: u8,
417 dst: &mut [u16],
418 stride: usize,
419 width: usize,
420 height: usize,
421 ) -> CodecResult<()> {
422 let mut pred0 = vec![0u16; width * height];
424 let mut pred1 = vec![0u16; width * height];
425
426 if mode_info.ref_frames[0] >= 0 {
428 let mv0 = self.get_motion_vector(mode_info, 0);
429 let ref0 = &self.reference_frames[mode_info.ref_frames[0] as usize];
430 if let Some(ref frame) = ref0 {
431 self.motion_compensate(
432 frame,
433 x,
434 y,
435 mv0,
436 plane,
437 &mut pred0,
438 width,
439 width,
440 height,
441 mode_info.interp_filter[0],
442 )?;
443 }
444 }
445
446 if mode_info.ref_frames[1] >= 0 {
448 let mv1 = self.get_motion_vector(mode_info, 1);
449 let ref1 = &self.reference_frames[mode_info.ref_frames[1] as usize];
450 if let Some(ref frame) = ref1 {
451 self.motion_compensate(
452 frame,
453 x,
454 y,
455 mv1,
456 plane,
457 &mut pred1,
458 width,
459 width,
460 height,
461 mode_info.interp_filter[1],
462 )?;
463 }
464 }
465
466 self.blend_compound_predictions(
468 &pred0,
469 &pred1,
470 dst,
471 stride,
472 width,
473 height,
474 mode_info.compound_type,
475 );
476
477 Ok(())
478 }
479
480 fn get_motion_vector(&self, mode_info: &BlockModeInfo, ref_idx: usize) -> [i32; 2] {
482 match mode_info.inter_mode {
483 InterMode::NewMv => [
484 i32::from(mode_info.mv[ref_idx][0]),
485 i32::from(mode_info.mv[ref_idx][1]),
486 ],
487 InterMode::NearestMv | InterMode::NearMv => {
488 [0, 0]
490 }
491 InterMode::GlobalMv => {
492 [0, 0]
494 }
495 }
496 }
497
498 #[allow(clippy::too_many_lines)]
500 fn motion_compensate(
501 &self,
502 ref_frame: &VideoFrame,
503 x: u32,
504 y: u32,
505 mv: [i32; 2],
506 plane: u8,
507 dst: &mut [u16],
508 stride: usize,
509 width: usize,
510 height: usize,
511 _interp_filter: u8,
512 ) -> CodecResult<()> {
513 let ref_x = (x as i32 * SUBPEL_SCALE) + mv[1];
515 let ref_y = (y as i32 * SUBPEL_SCALE) + mv[0];
516
517 let int_x = ref_x >> SUBPEL_BITS;
519 let int_y = ref_y >> SUBPEL_BITS;
520 let frac_x = (ref_x & (SUBPEL_SCALE - 1)) as usize;
521 let frac_y = (ref_y & (SUBPEL_SCALE - 1)) as usize;
522
523 let plane_idx = plane as usize;
525 let (ref_data, ref_stride) = if plane_idx < ref_frame.planes.len() {
526 (
527 &ref_frame.planes[plane_idx].data[..],
528 ref_frame.planes[plane_idx].stride,
529 )
530 } else {
531 return Err(CodecError::Internal("Invalid plane index".to_string()));
532 };
533
534 if frac_x == 0 && frac_y == 0 {
536 self.copy_block(
538 ref_data, ref_stride, int_x, int_y, dst, stride, width, height,
539 );
540 } else if frac_y == 0 {
541 self.interp_horizontal(
543 ref_data, ref_stride, int_x, int_y, frac_x, dst, stride, width, height,
544 );
545 } else if frac_x == 0 {
546 self.interp_vertical(
548 ref_data, ref_stride, int_x, int_y, frac_y, dst, stride, width, height,
549 );
550 } else {
551 self.interp_2d(
553 ref_data, ref_stride, int_x, int_y, frac_x, frac_y, dst, stride, width, height,
554 );
555 }
556
557 Ok(())
558 }
559
560 fn copy_block(
562 &self,
563 src: &[u8],
564 src_stride: usize,
565 x: i32,
566 y: i32,
567 dst: &mut [u16],
568 dst_stride: usize,
569 width: usize,
570 height: usize,
571 ) {
572 for row in 0..height {
573 let src_y = (y + row as i32).max(0) as usize;
574 let src_start = src_y * src_stride + x.max(0) as usize;
575
576 for col in 0..width {
577 if src_start + col < src.len() {
578 let dst_idx = row * dst_stride + col;
579 if dst_idx < dst.len() {
580 dst[dst_idx] = u16::from(src[src_start + col]);
581 }
582 }
583 }
584 }
585 }
586
587 fn interp_horizontal(
589 &self,
590 src: &[u8],
591 src_stride: usize,
592 x: i32,
593 y: i32,
594 frac: usize,
595 dst: &mut [u16],
596 dst_stride: usize,
597 width: usize,
598 height: usize,
599 ) {
600 let filter = self.get_interp_filter(frac);
602
603 for row in 0..height {
604 let src_y = (y + row as i32).max(0) as usize;
605
606 for col in 0..width {
607 let mut sum = 0i32;
608
609 for tap in 0..INTERP_TAPS {
611 let src_x =
612 (x + col as i32 + tap as i32 - INTERP_TAPS as i32 / 2).max(0) as usize;
613 let src_idx = src_y * src_stride + src_x;
614
615 if src_idx < src.len() {
616 sum += i32::from(src[src_idx]) * filter[tap];
617 }
618 }
619
620 let dst_idx = row * dst_stride + col;
621 if dst_idx < dst.len() {
622 dst[dst_idx] = ((sum + 64) >> 7).clamp(0, (1 << self.bit_depth) - 1) as u16;
623 }
624 }
625 }
626 }
627
628 fn interp_vertical(
630 &self,
631 src: &[u8],
632 src_stride: usize,
633 x: i32,
634 y: i32,
635 frac: usize,
636 dst: &mut [u16],
637 dst_stride: usize,
638 width: usize,
639 height: usize,
640 ) {
641 let filter = self.get_interp_filter(frac);
642
643 for row in 0..height {
644 for col in 0..width {
645 let mut sum = 0i32;
646
647 for tap in 0..INTERP_TAPS {
648 let src_y =
649 (y + row as i32 + tap as i32 - INTERP_TAPS as i32 / 2).max(0) as usize;
650 let src_x = (x + col as i32).max(0) as usize;
651 let src_idx = src_y * src_stride + src_x;
652
653 if src_idx < src.len() {
654 sum += i32::from(src[src_idx]) * filter[tap];
655 }
656 }
657
658 let dst_idx = row * dst_stride + col;
659 if dst_idx < dst.len() {
660 dst[dst_idx] = ((sum + 64) >> 7).clamp(0, (1 << self.bit_depth) - 1) as u16;
661 }
662 }
663 }
664 }
665
666 fn interp_2d(
668 &self,
669 src: &[u8],
670 src_stride: usize,
671 x: i32,
672 y: i32,
673 frac_x: usize,
674 frac_y: usize,
675 dst: &mut [u16],
676 dst_stride: usize,
677 width: usize,
678 height: usize,
679 ) {
680 let mut temp = vec![0u16; (width + INTERP_TAPS) * (height + INTERP_TAPS)];
682 let temp_stride = width + INTERP_TAPS;
683
684 self.interp_horizontal(
686 src,
687 src_stride,
688 x,
689 y - INTERP_TAPS as i32 / 2,
690 frac_x,
691 &mut temp,
692 temp_stride,
693 width,
694 height + INTERP_TAPS,
695 );
696
697 let filter_y = self.get_interp_filter(frac_y);
699
700 for row in 0..height {
701 for col in 0..width {
702 let mut sum = 0i32;
703
704 for tap in 0..INTERP_TAPS {
705 let temp_idx = (row + tap) * temp_stride + col;
706 if temp_idx < temp.len() {
707 sum += i32::from(temp[temp_idx]) * filter_y[tap];
708 }
709 }
710
711 let dst_idx = row * dst_stride + col;
712 if dst_idx < dst.len() {
713 dst[dst_idx] = ((sum + 64) >> 7).clamp(0, (1 << self.bit_depth) - 1) as u16;
714 }
715 }
716 }
717 }
718
719 fn get_interp_filter(&self, frac: usize) -> [i32; INTERP_TAPS] {
721 match frac {
723 0 => [0, 0, 0, 128, 0, 0, 0, 0],
724 1 => [0, 0, 16, 112, 16, 0, 0, 0],
725 2 => [0, 0, 32, 96, 32, 0, 0, 0],
726 3 => [0, 0, 48, 80, 48, 0, 0, 0],
727 4 => [0, 0, 64, 64, 64, 0, 0, 0],
728 5 => [0, 0, 48, 80, 48, 0, 0, 0],
729 6 => [0, 0, 32, 96, 32, 0, 0, 0],
730 7 => [0, 0, 16, 112, 16, 0, 0, 0],
731 _ => [0, 0, 0, 128, 0, 0, 0, 0],
732 }
733 }
734
735 fn blend_compound_predictions(
737 &self,
738 pred0: &[u16],
739 pred1: &[u16],
740 dst: &mut [u16],
741 stride: usize,
742 width: usize,
743 height: usize,
744 compound_type: u8,
745 ) {
746 for row in 0..height {
747 for col in 0..width {
748 let src_idx = row * width + col;
749 let dst_idx = row * stride + col;
750
751 if src_idx < pred0.len() && src_idx < pred1.len() && dst_idx < dst.len() {
752 let blended = if compound_type == 0 {
754 (u32::from(pred0[src_idx]) + u32::from(pred1[src_idx]) + 1) >> 1
755 } else {
756 u32::from(pred0[src_idx])
758 };
759
760 dst[dst_idx] = blended as u16;
761 }
762 }
763 }
764 }
765
766 fn apply_obmc(
768 &mut self,
769 _mode_info: &BlockModeInfo,
770 _x: u32,
771 _y: u32,
772 _plane: u8,
773 dst: &mut [u16],
774 stride: usize,
775 width: usize,
776 height: usize,
777 ) -> CodecResult<()> {
778 self.smooth_boundaries(dst, stride, width, height);
781 Ok(())
782 }
783
784 fn smooth_boundaries(&self, dst: &mut [u16], stride: usize, width: usize, height: usize) {
786 for row in 0..height {
788 for col in 0..width {
789 if row == 0 || col == 0 || row == height - 1 || col == width - 1 {
790 let idx = row * stride + col;
791 if idx < dst.len() {
792 let current = dst[idx];
794 dst[idx] = ((u32::from(current) * 3 + 2) >> 2) as u16;
795 }
796 }
797 }
798 }
799 }
800
801 fn apply_warped_motion(
803 &mut self,
804 _mode_info: &BlockModeInfo,
805 _x: u32,
806 _y: u32,
807 _plane: u8,
808 dst: &mut [u16],
809 stride: usize,
810 width: usize,
811 height: usize,
812 ) -> CodecResult<()> {
813 for row in 0..height {
816 for col in 0..width {
817 let idx = row * stride + col;
818 if idx < dst.len() {
819 dst[idx] = dst[idx];
821 }
822 }
823 }
824 Ok(())
825 }
826
827 pub fn set_reference_frame(&mut self, idx: usize, frame: VideoFrame) {
829 if idx < self.reference_frames.len() {
830 self.reference_frames[idx] = Some(frame);
831 }
832 }
833
834 pub fn set_current_frame(&mut self, frame: VideoFrame) {
836 self.current_frame = Some(frame);
837 }
838}
839
840#[cfg(test)]
845mod tests {
846 use super::*;
847 use crate::frame::{FrameType, VideoFrame};
848 use oximedia_core::{PixelFormat, Rational, Timestamp};
849
850 fn create_test_frame(width: u32, height: u32) -> VideoFrame {
851 let mut frame = VideoFrame::new(PixelFormat::Yuv420p, width, height);
852 frame.allocate();
853 frame.frame_type = FrameType::Key;
854 frame.timestamp = Timestamp::new(0, Rational::new(1, 30));
855 frame
856 }
857
858 fn create_test_mode_info() -> BlockModeInfo {
859 BlockModeInfo::new()
860 }
861
862 #[test]
863 fn test_prediction_engine_creation() {
864 let engine = PredictionEngine::new(1920, 1080, 8);
865 assert_eq!(engine.bit_depth, 8);
866 }
867
868 #[test]
869 fn test_map_intra_mode() {
870 let engine = PredictionEngine::new(64, 64, 8);
871
872 assert_eq!(engine.map_intra_mode(Av1IntraMode::DcPred), IntraMode::Dc);
873 assert_eq!(
874 engine.map_intra_mode(Av1IntraMode::VPred),
875 IntraMode::Vertical
876 );
877 assert_eq!(
878 engine.map_intra_mode(Av1IntraMode::HPred),
879 IntraMode::Horizontal
880 );
881 }
882
883 #[test]
884 fn test_predict_intra() {
885 let mut engine = PredictionEngine::new(64, 64, 8);
886 let frame = create_test_frame(64, 64);
887 engine.set_current_frame(frame);
888
889 let mode_info = create_test_mode_info();
890 let mut dst = vec![0u16; 16 * 16];
891
892 let result = engine.predict_intra(&mode_info, 0, 0, 0, &mut dst, 16);
894 assert!(result.is_ok());
895 }
896
897 #[test]
898 fn test_get_interp_filter() {
899 let engine = PredictionEngine::new(64, 64, 8);
900
901 let filter = engine.get_interp_filter(0);
902 assert_eq!(filter[3], 128); let filter_half = engine.get_interp_filter(4);
905 assert!(filter_half[3] > 0);
906 }
907
908 #[test]
909 fn test_set_reference_frame() {
910 let mut engine = PredictionEngine::new(64, 64, 8);
911 let frame = create_test_frame(64, 64);
912
913 engine.set_reference_frame(0, frame);
914 assert!(engine.reference_frames[0].is_some());
915 }
916
917 #[test]
918 fn test_constants() {
919 assert_eq!(SUBPEL_BITS, 3);
920 assert_eq!(SUBPEL_SCALE, 8);
921 assert_eq!(INTERP_TAPS, 8);
922 assert_eq!(WARP_PARAMS, 6);
923 }
924}