From: Kostya Shishkov Date: Tue, 24 Mar 2026 16:54:47 +0000 (+0100) Subject: mpeg4asp: do not support qpel mode X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=40f6b5c7975e54cb5b301afbdc35af28a46adf05;p=nihav.git mpeg4asp: do not support qpel mode Quarterpel interpolation is too annoying, but having a quick and dirty approximation should work equally bad. --- diff --git a/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs b/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs index c497563..3cb7d6a 100644 --- a/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs +++ b/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs @@ -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]; diff --git a/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs b/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs index 927a9e9..ce7f792 100644 --- a/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs +++ b/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs @@ -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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, mv: MV) { + pub fn mb_mv(&self, frm: &mut NASimpleVideoFrame, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, mv: MV) { + pub fn avg_mb_mv(&self, frm: &mut NASimpleVideoFrame, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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); diff --git a/nihav-mpeg/src/codecs/mpeg4asp/mod.rs b/nihav-mpeg/src/codecs/mpeg4asp/mod.rs index eaab25e..dbbdf21 100644 --- a/nihav-mpeg/src/codecs/mpeg4asp/mod.rs +++ b/nihav-mpeg/src/codecs/mpeg4asp/mod.rs @@ -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();