]> git.nihav.org Git - nihav.git/commitdiff
mpeg4asp: do not support qpel mode
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 24 Mar 2026 16:54:47 +0000 (17:54 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 24 Mar 2026 16:54:47 +0000 (17:54 +0100)
Quarterpel interpolation is too annoying, but having a quick and dirty
approximation should work equally bad.

nihav-mpeg/src/codecs/mpeg4asp/decoder.rs
nihav-mpeg/src/codecs/mpeg4asp/dsp.rs
nihav-mpeg/src/codecs/mpeg4asp/mod.rs

index c497563ed01fcabc8868053c819367d1df39bbaf..3cb7d6aecfeb5ddbf45909324f5dd637e4145683 100644 (file)
@@ -55,6 +55,9 @@ impl FrameContext {
     pub fn set_solid_dmb(&mut self) {
         self.solid_dmb = true;
     }
+    pub fn set_qpel(&mut self, qpel: bool) {
+        self.dsp.set_qpel(qpel);
+    }
     pub fn resize(&mut self, mb_w: usize) {
         self.pred.resize(mb_w);
     }
@@ -158,7 +161,7 @@ impl FrameContext {
                             *f_mv = ref_mv + diff_mv;
                             *b_mv = MV::b_sub(src_mv, *f_mv, diff_mv, trb, trd);
                         }
-                        if self.solid_dmb { //xxx: but not for quarter sample apparently
+                        if self.solid_dmb && !self.dsp.is_qpel() {
                             f_mvs[1] = f_mvs[0];
                             f_mvs[2] = f_mvs[0];
                             f_mvs[3] = f_mvs[0];
index 927a9e9d39fa33aa2ce420f01d95206f54a03907..ce7f792e600cf8662845d30a99a056ddd1cfe617 100644 (file)
@@ -1,5 +1,5 @@
 use nihav_core::frame::*;
-use nihav_codec_support::codecs::MV;
+use nihav_codec_support::codecs::{MV, ZERO_MV};
 use nihav_codec_support::codecs::blockdsp::{BlkInterpFunc, edge_emu};
 use nihav_codec_support::codecs::h263::code::*;
 use nihav_codec_support::codecs::h263::data::H263_CHROMA_ROUND;
@@ -116,11 +116,15 @@ pub fn get_dc_scales(quant: u8) -> [u8; 2] {
 fn idct_dc(block: &mut [i16; 64]) -> u8 { (block[0] / 8).clip8() }
 
 trait MVOps {
+    fn halve(&self) -> MV;
     fn get_chroma_mv(&self) -> MV;
     fn get_chroma_4mv(&self) -> MV;
 }
 
 impl MVOps for MV {
+    fn halve(&self) -> MV {
+        MV{ x: (self.x >> 1) | (self.x & 1), y: (self.y >> 1) | (self.y & 1) }
+    }
     fn get_chroma_mv(&self) -> MV {
         MV{ x: self.x >> 1, y: self.y >> 1 }
     }
@@ -172,6 +176,7 @@ pub struct DSP {
     mc_funcs:       &'static [BlkInterpFunc],
     avg_mc_funcs:   &'static [BlkInterpFunc],
     align:          u8,
+    qpel:           bool,
 }
 
 impl DSP {
@@ -181,8 +186,13 @@ impl DSP {
             mc_funcs:       ASP_INTERP_FUNCS_NR,
             avg_mc_funcs:   ASP_INTERP_AVG_FUNCS_NR,
             align:          4,
+            qpel:           false,
         }
     }
+    pub fn set_qpel(&mut self, qpel: bool) {
+        self.qpel = qpel;
+    }
+    pub fn is_qpel(&self) -> bool { self.qpel }
     pub fn use_xvid_idct(&mut self) {
         self.idct = xvidish_idct;
     }
@@ -269,7 +279,10 @@ impl DSP {
         }
     }
 
-    pub fn mb_mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mv: MV) {
+    pub fn mb_mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mut mv: MV) {
+        if self.qpel {
+            mv = mv.halve();
+        }
         let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
         copy_block(frm, pframe.clone(), 0, mb_x * 16, mb_y * 16, mv.x >> 1, mv.y >> 1, 16, 16, 0, 1, mode, self.mc_funcs, self.align);
         let cmv = mv.get_chroma_mv();
@@ -278,7 +291,10 @@ impl DSP {
         copy_block(frm, pframe,         2, mb_x * 8, mb_y * 8, cmv.x >> 1, cmv.y >> 1, 8, 8, 0, 1, cmode, self.mc_funcs, self.align);
     }
 
-    pub fn avg_mb_mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mv: MV) {
+    pub fn avg_mb_mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mut mv: MV) {
+        if self.qpel {
+            mv = mv.halve();
+        }
         let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
         copy_block(frm, pframe.clone(), 0, mb_x * 16, mb_y * 16, mv.x >> 1, mv.y >> 1, 16, 16, 0, 1, mode, self.avg_mc_funcs, self.align);
         let cmv = mv.get_chroma_mv();
@@ -289,10 +305,15 @@ impl DSP {
 
     pub fn mb_4mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mvs: &[MV; 4]) {
         for (n, &mv) in mvs.iter().enumerate() {
+            let mv = if !self.qpel { mv } else { mv.halve() };
             let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
             copy_block(frm, pframe.clone(), 0, mb_x * 16 + (n & 1) * 8, mb_y * 16 + (n / 2) * 8, mv.x >> 1, mv.y >> 1, 8, 8, 0, 1, mode, self.mc_funcs, self.align);
         }
-        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let sum_mv = if !self.qpel {
+                mvs[0] + mvs[1] + mvs[2] + mvs[3]
+            } else {
+                mvs.iter().fold(ZERO_MV, |sum, a| sum + MV {x: a.x / 2, y: a.y / 2 })
+            };
         let cmv = sum_mv.get_chroma_4mv();
         let cmode = ((cmv.x & 1) + (cmv.y & 1) * 2) as usize;
         copy_block(frm, pframe.clone(), 1, mb_x * 8, mb_y * 8, cmv.x >> 1, cmv.y >> 1, 8, 8, 0, 1, cmode, self.mc_funcs, self.align);
@@ -301,10 +322,15 @@ impl DSP {
 
     pub fn avg_mb_4mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mvs: &[MV; 4]) {
         for (n, &mv) in mvs.iter().enumerate() {
+            let mv = if !self.qpel { mv } else { mv.halve() };
             let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
             copy_block(frm, pframe.clone(), 0, mb_x * 16 + (n & 1) * 8, mb_y * 16 + (n / 2) * 8, mv.x >> 1, mv.y >> 1, 8, 8, 0, 1, mode, self.avg_mc_funcs, self.align);
         }
-        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let sum_mv = if !self.qpel {
+                mvs[0] + mvs[1] + mvs[2] + mvs[3]
+            } else {
+                mvs.iter().fold(ZERO_MV, |sum, a| sum + MV {x: a.x / 2, y: a.y / 2 })
+            };
         let cmv = sum_mv.get_chroma_4mv();
         let cmode = ((cmv.x & 1) + (cmv.y & 1) * 2) as usize;
         copy_block(frm, pframe.clone(), 1, mb_x * 8, mb_y * 8, cmv.x >> 1, cmv.y >> 1, 8, 8, 0, 1, cmode, self.avg_mc_funcs, self.align);
index eaab25e2fa2f60a7e302f78f48d6c638058cccdc..dbbdf21ff3915587d3949fa329cd5b4ead7752bf 100644 (file)
@@ -44,6 +44,7 @@ struct ASPDecoder {
     last_ref_info:  PicState,
     pb_mode:        bool,
     assign_mode:    AssignMode,
+    warned_qpel:    bool,
 }
 
 impl ASPDecoder {
@@ -58,6 +59,7 @@ impl ASPDecoder {
             last_ref_info:  PicState::new(0, 0, 0),
             pb_mode:        false,
             assign_mode:    AssignMode::default(),
+            warned_qpel:    false,
         }
     }
 }
@@ -73,10 +75,11 @@ impl NADecoder for ASPDecoder {
                 // ES descriptor, usually happens in MOV
                 if edata.len() > 16 && &edata[..4] == b"esds" {
                     let (vol, bugs) = parse_es_descriptor(&edata[8..])?;
-                    if vol.quarter_sample {
-                        println!("quarterpel is not supported!");
-                        return Err(DecoderError::NotImplemented);
+                    if vol.quarter_sample && !self.warned_qpel {
+                        println!("Warning: quarterpel decoding is not (really) supported");
+                        self.warned_qpel = true;
                     }
+                    self.fctx.set_qpel(vol.quarter_sample);
                     if bugs.is_xvid_dct() {
                         self.fctx.use_xvid_idct();
                     }
@@ -119,10 +122,11 @@ impl NADecoder for ASPDecoder {
                 0x00..=0x1F => {}, // video_object_start_code
                 0x20..=0x3F => { // video_object_layer_start_code
                     let vol = parse_video_object_layer(&obj_src[1..])?;
-                    if vol.quarter_sample {
-                        println!("quarterpel is not supported!");
-                        return Err(DecoderError::NotImplemented);
+                    if vol.quarter_sample && !self.warned_qpel {
+                        println!("Warning: quarterpel decoding is not (really) supported");
+                        self.warned_qpel = true;
                     }
+                    self.fctx.set_qpel(vol.quarter_sample);
                     if vol.width != 0 {
                         if self.assign_mode == AssignMode::Undefined && pkt.get_dts().is_none() && !vol.low_delay {
                             let tinfo = pkt.get_time_information();