]> git.nihav.org Git - nihav.git/commitdiff
add MPEG-4 ASP decoder master
authorKostya Shishkov <kostya.shishkov@gmail.com>
Mon, 14 Oct 2024 16:14:59 +0000 (18:14 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Mon, 14 Oct 2024 16:24:59 +0000 (18:24 +0200)
nihav-mpeg/Cargo.toml
nihav-mpeg/src/codecs/mod.rs
nihav-mpeg/src/codecs/mpeg4asp/bitstream.rs [new file with mode: 0644]
nihav-mpeg/src/codecs/mpeg4asp/decoder.rs [new file with mode: 0644]
nihav-mpeg/src/codecs/mpeg4asp/dispatch.rs [new file with mode: 0644]
nihav-mpeg/src/codecs/mpeg4asp/dsp.rs [new file with mode: 0644]
nihav-mpeg/src/codecs/mpeg4asp/mod.rs [new file with mode: 0644]
nihav-mpeg/src/codecs/mpeg4asp/types.rs [new file with mode: 0644]
nihav-registry/src/register.rs

index 84d0c4b811515a7ac73dbeffbf082fc743518d25..52b2bc74322e2a545b408a50299541d66418c6f8 100644 (file)
@@ -9,7 +9,7 @@ path = "../nihav-core"
 
 [dependencies.nihav_codec_support]
 path = "../nihav-codec-support"
-features = ["qmf", "fft", "mdct", "dsp_window"]
+features = ["qmf", "fft", "mdct", "dsp_window", "h263"]
 
 [dev-dependencies]
 nihav_flash = { path = "../nihav-flash", default-features=false, features = ["all_demuxers"] }
@@ -22,7 +22,8 @@ decoders = []
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 
-all_video_decoders = []
+all_video_decoders = ["decoder_mpeg4asp"]
+decoder_mpeg4asp = ["decoders"]
 
 all_audio_decoders = ["decoder_aac", "decoder_mpa"]
 decoder_aac = ["decoders"]
index f6fb31cd6301e48f3331cffb41a59362fae4a1ab..9d488089d85346992421b167a2b6af4f0d6ab96e 100644 (file)
@@ -9,6 +9,9 @@ macro_rules! validate {
     ($a:expr) => { if !$a { return Err(DecoderError::InvalidData); } };
 }
 
+#[cfg(feature="decoder_mpeg4asp")]
+mod mpeg4asp;
+
 #[cfg(feature="decoder_aac")]
 #[allow(clippy::comparison_chain)]
 #[allow(clippy::excessive_precision)]
@@ -20,6 +23,9 @@ mod aac;
 mod mpegaudio;
 
 const DECODERS: &[DecoderInfo] = &[
+#[cfg(feature="decoder_mpeg4asp")]
+    DecoderInfo { name: "mpeg4asp", get_decoder: mpeg4asp::get_decoder },
+
 #[cfg(feature="decoder_aac")]
     DecoderInfo { name: "aac", get_decoder: aac::get_decoder },
 #[cfg(feature="decoder_mpa")]
diff --git a/nihav-mpeg/src/codecs/mpeg4asp/bitstream.rs b/nihav-mpeg/src/codecs/mpeg4asp/bitstream.rs
new file mode 100644 (file)
index 0000000..b82226b
--- /dev/null
@@ -0,0 +1,1198 @@
+use nihav_core::frame::FrameType;
+use nihav_core::codecs::{DecoderResult, DecoderError};
+use nihav_core::io::byteio::*;
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_core::io::intcode::*;
+use nihav_codec_support::codecs::{MV, ZERO_MV, ZIGZAG};
+use nihav_codec_support::codecs::h263::data::*;
+
+use super::dsp::*;
+use super::types::*;
+
+trait ReadMarkerBit {
+    fn read_marker(&mut self) -> DecoderResult<()>;
+    fn read_long_val(&mut self, first_part_bits: u8, second_part_bits: u8) -> DecoderResult<u32>;
+}
+
+impl<'a> ReadMarkerBit for BitReader<'a> {
+    fn read_marker(&mut self) -> DecoderResult<()> {
+        if self.read(1)? == 1 {
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn read_long_val(&mut self, first_part_bits: u8, second_part_bits: u8) -> DecoderResult<u32> {
+        let part1 = self.read(first_part_bits)?;
+                    self.read_marker()?;
+        let part2 = self.read(second_part_bits)?;
+                    self.read_marker()?;
+        Ok((part1 << second_part_bits) | part2)
+    }
+}
+
+pub fn scan_start_codes(src: &[u8]) -> DecoderResult<Vec<usize>> {
+    let mut mr = MemoryReader::new_read(src);
+    let mut br = ByteReader::new(&mut mr);
+
+    let mut offs = Vec::new();
+    let mut pfx = br.read_u32be()?;
+    validate!((pfx >> 8) == 1);
+    offs.push(0);
+    while br.left() >= 4 {
+        pfx = (pfx << 8) | u32::from(br.read_byte()?);
+        if (pfx >> 8) == 1 {
+            offs.push(br.tell() as usize - 4);
+        }
+    }
+
+    offs.push(src.len());
+    Ok(offs)
+}
+
+pub fn check_xvid_user_data(src: &[u8]) -> bool {
+    src.len() > 7 && &src[1..5] == b"XviD"
+}
+
+fn read_var_len(br: &mut BitReader) -> DecoderResult<u32> {
+    let mut len = 0;
+    loop {
+        let b                       = br.read(8)?;
+        len = (len << 7) | (b & 0x7F);
+        if (b & 0x80) == 0 {
+            break;
+        }
+    }
+    Ok(len)
+}
+
+pub fn parse_es_descriptor(src: &[u8]) -> DecoderResult<(VideoObjectLayer, bool)> {
+    let mut br = BitReader::new(src, BitReaderMode::BE);
+
+    // ES_Descriptor
+    let tag                         = br.read(8)?;
+    validate!(tag == 0x03);
+    let len                         = read_var_len(&mut br)?;
+    validate!((len as isize * 8) <= br.left());
+    let esd_end = br.tell() + (len as usize) * 8;
+    let _id                         = br.read(16)?;
+    //validate!(id != 0 && id != 0xFFFF);
+    let stream_dependence           = br.read_bool()?;
+    let url                         = br.read_bool()?;
+    let ocr_stream                  = br.read_bool()?;
+    let _stream_priority            = br.read(5)?;
+    if stream_dependence {
+                                    br.skip(16)?; // depends_on_ES_ID
+    }
+    if url {
+        let url_len                 = br.read(8)?;
+                                    br.skip(url_len * 8)?;
+    }
+    if ocr_stream {
+                                    br.skip(16)?; // OCR_ES_ID
+    }
+    // DecoderConfigDescr
+    let tag                         = br.read(8)?;
+    validate!(tag == 0x04);
+    let len                         = read_var_len(&mut br)?;
+    let dcd_end = br.tell() + (len as usize) * 8;
+    validate!(dcd_end <= esd_end);
+    if len < 16 { // no MPEG-4 ASP decoder config data
+        return Err(DecoderError::InvalidData);
+    }
+    let object_type                 = br.read(8)?;
+    validate!(object_type == 0x20);
+    let stream_type                 = br.read(6)?;
+    validate!(stream_type == 4);
+    let _up_stream                  = br.read_bool()?;
+                                    br.read_marker()?;
+                                    br.skip(24)?; // bufferSizeDB
+                                    br.skip(32)?; // maxBitrate
+                                    br.skip(32)?; // avgBitrate
+    // DecoderSpecificInfo
+    let tag                         = br.read(8)?;
+    validate!(tag == 0x05);
+    let len                         = read_var_len(&mut br)?;
+    let dsi_end = br.tell() + (len as usize) * 8;
+    validate!(dsi_end <= dcd_end);
+
+    let dsi = &src[br.tell() / 8..][..len as usize];
+    let offs = scan_start_codes(dsi)?;
+    let mut vol = None;
+    let mut is_xvid = false;
+    for range in offs.windows(2) {
+        let obj_src = &dsi[range[0] + 3..range[1]];
+        match obj_src[0] {
+            0x00..=0x1F => {}, // video_object_start_code
+            0x20..=0x3F => { // video_object_layer_start_code
+                vol = Some(parse_video_object_layer(&obj_src[1..])?);
+            },
+            0x30..=0xAF | 0xB7..=0xB9 | 0xC4..=0xC5 => {}, // reserved, system start codes
+            0xB0 => { // visual_object_sequence_start_code
+                validate!(obj_src.len() >= 2);
+                // single byte is profile and level
+            },
+            0xB1 => {}, // visual_object_sequence_end_code
+            0xB2 => { // user_data_start_code
+                is_xvid |= check_xvid_user_data(obj_src);
+            },
+            0xB3 => return Err(DecoderError::InvalidData), // group_of_vop_start_code
+            0xB4 => return Err(DecoderError::NotImplemented), // video_session_error_code
+            0xB5 => {}, // visual_object_start_code
+            0xB6 => return Err(DecoderError::InvalidData), // vop_start_code
+            0xBA..=0xC2 => return Err(DecoderError::NotImplemented), // FBA, mesh, texture data
+            0xC3 => {}, // stuffing_start_code
+            0xC6..=0xFF => {}, // system start codes
+        }
+    }
+    // the rest - who cares?
+    validate!(vol.is_some());
+    Ok((vol.unwrap(), is_xvid))
+}
+
+pub fn parse_video_object_layer(src: &[u8]) -> DecoderResult<VideoObjectLayer> {
+    let mut br = BitReader::new(src, BitReaderMode::BE);
+
+    let _random_accessible          = br.read_bool()?;
+    let vol_type                    = br.read(8)? as u8;
+    /*if vol_type > 1 {
+        println!("vol_type {vol_type}");
+        return Err(DecoderError::NotImplemented);
+    }*/
+    let version = if br.read_bool()? {
+            let verid               = br.read(4)? as u8;
+            let _priority           = br.read(3)? as u8;
+            verid
+        } else { 1 };
+
+    let ar_info                     = br.read(4)?;
+    if ar_info == 0b1111 {
+        let _par_width              = br.read(8)?;
+        let _par_height             = br.read(8)?;
+    }
+
+    let vol_control_parameters      = br.read_bool()?;
+    let mut low_delay = false;
+    if vol_control_parameters {
+        let chroma_format           = br.read(2)?;
+        if chroma_format != 1 { println!("chroma!"); return Err(DecoderError::NotImplemented); }
+        low_delay                   = br.read_bool()?;
+        if br.read_bool()? { // vbv_parameters
+            let _bit_rate           = br.read_long_val(15, 15)?;
+            let _vbv_buf_size_part1 = br.read(15)?;
+                                      br.read_marker()?;
+            let _vbv_buf_size_part2 = br.read(3)?;
+            let _occupancy          = br.read_long_val(11, 15)?;
+        }
+    }
+
+    const SHAPES: [Shape; 4] = [ Shape::Rect, Shape::Binary, Shape::BinaryOnly, Shape::Grayscale ];
+    let shape                       = SHAPES[br.read(2)? as usize];
+    if shape != Shape::Rect { println!("non-rect shape!"); return Err(DecoderError::NotImplemented); }
+
+    if shape == Shape::Grayscale && version != 1 {
+        let _shape_extension        = br.read(4)?;
+    }
+                                    br.read_marker()?;
+    let time_res                    = br.read(16)?;
+    validate!(time_res != 0);
+                                    br.read_marker()?;
+    let fixed_rate = if br.read_bool()? {
+            let mut res_bits = 0;
+            while (1 << res_bits) < (time_res - 1) {
+                res_bits += 1;
+            }
+            Some(br.read(res_bits)?)
+        } else { None };
+
+    let resync_marker_disable;
+    let mut width = 0;
+    let mut height = 0;
+    let mut obmc_disable = false;
+    let mut sprite_info = SpriteInfo::default();
+    let mut quant_info = QuantInfo::default();
+    let mut quarter_sample = false;
+    let mut newpred_enable = false;
+    let mut complexity_estimation = false;
+    if shape != Shape::BinaryOnly {
+        if shape == Shape::Rect {
+                                    br.read_marker()?;
+            width                   = br.read(13)? as usize;
+                                    br.read_marker()?;
+            validate!(width > 0);
+            height                  = br.read(13)? as usize;
+                                    br.read_marker()?;
+            validate!(height > 0);
+        }
+        let interlaced              = br.read_bool()?;
+        if interlaced { println!("interlaced!"); return Err(DecoderError::NotImplemented); }
+        obmc_disable                = br.read_bool()?;
+        if version == 1 {
+            if br.read_bool()? {
+                sprite_info.sprite = Sprite::Static;
+            }
+        } else {
+            let code                = br.read(2)?;
+            sprite_info.sprite = match code {
+                    0 => Sprite::NotUsed,
+                    1 => Sprite::Static,
+                    2 => Sprite::GMC,
+                    _ => { println!("wrong sprite code"); return Err(DecoderError::NotImplemented); },
+                };
+        }
+        if matches!(sprite_info.sprite, Sprite::Static | Sprite::GMC) {
+            if sprite_info.sprite != Sprite::GMC {
+                let sprite_width    = br.read(13)? as usize;
+                                    br.read_marker()?;
+                validate!(sprite_width > 0);
+                let sprite_height   = br.read(13)? as usize;
+                                    br.read_marker()?;
+                validate!(sprite_height > 0);
+                let _sprite_left    = br.read(13)? as usize;
+                                    br.read_marker()?;
+                let _sprite_right   = br.read(13)? as usize;
+                                    br.read_marker()?;
+            }
+            sprite_info.num_warp_points     = br.read(6)? as usize;
+            sprite_info.warping_accuracy    = br.read(2)? as u8;
+            sprite_info.brightness_change   = br.read_bool()?;
+            if sprite_info.sprite != Sprite::GMC {
+                sprite_info.low_latency     = br.read_bool()?;
+            }
+        }
+
+        if version != 1 && shape != Shape::Rect {
+            let _sadct_disable      = br.read_bool()?;
+        }
+
+        let not_8_bit               = br.read_bool()?;
+        if not_8_bit {
+            //let quant_precision = br.read(4)?;
+            //let bits_per_pixel  = br.read(4)? as u8 + 8;
+            println!("non-8bit");
+            return Err(DecoderError::NotImplemented);
+        }
+
+        if shape == Shape::Grayscale {
+            let _no_gray_quant_update = br.read_bool()?;
+            let _composition_method   = br.read_bool()?;
+            let _linear_composition   = br.read_bool()?;
+        }
+
+        read_quant_info(&mut br, &mut quant_info, shape)?;
+
+        if version != 1 {
+            quarter_sample          = br.read_bool()?;
+        }
+
+        complexity_estimation       = !br.read_bool()?;
+        if complexity_estimation {
+            read_vop_complexity_estimation_header(&mut br)?;
+        }
+
+        resync_marker_disable       = br.read_bool()?;
+        let data_partitioned        = br.read_bool()?;
+        if data_partitioned {
+            //let reversible_vlc = br.read_bool()?;
+            println!("partitioned");
+            return Err(DecoderError::NotImplemented);
+        }
+
+        if version != 1 {
+            newpred_enable          = br.read_bool()?;
+            if newpred_enable {
+                let _req_upstream_msg_type = br.read(2)?;
+                let _newpred_segment_type  = br.read_bool()?;
+            }
+            let reduced_res_vop_enable = br.read_bool()?;
+            if reduced_res_vop_enable { return Err(DecoderError::NotImplemented); }
+        }
+        let scalability             = br.read_bool()?;
+        if scalability {
+            // lots of fields
+            println!("scalable");
+            return Err(DecoderError::NotImplemented);
+        }
+    } else {
+        if version != 1 {
+            let scalability         = br.read_bool()?;
+            if scalability {
+                //let ref_layer_id  = br.read(4)?;
+                //let shape_hor_sampling_factor_m = br.read(5)?;
+                //let shape_hor_sampling_factor_n = br.read(5)?;
+                //let shape_vert_sampling_factor_m = br.read(5)?;
+                //let shape_vert_sampling_factor_n = br.read(5)?;
+                return Err(DecoderError::NotImplemented);
+            }
+        }
+        resync_marker_disable       = br.read_bool()?;
+    }
+
+    Ok(VideoObjectLayer {
+        vol_type, version, low_delay, shape,
+        time_res, fixed_rate,
+        width, height,
+        sprite_info, quant_info, quarter_sample, newpred_enable,
+        complexity_estimation,
+        obmc_disable, resync_marker_disable,
+    })
+}
+
+fn read_quant_info(br: &mut BitReader, qinfo: &mut QuantInfo, shape: Shape) -> DecoderResult<()> {
+    qinfo.quant_type        = br.read_bool()?;
+    if qinfo.quant_type {
+        if br.read_bool()? {
+            read_quant_mat(br, &mut qinfo.intra_mat, true)?;
+        }
+        if br.read_bool()? {
+            read_quant_mat(br, &mut qinfo.inter_mat, false)?;
+        }
+        if shape == Shape::Grayscale {
+            return Err(DecoderError::NotImplemented);
+        }
+    }
+    Ok(())
+}
+
+fn read_quant_mat(br: &mut BitReader, mat: &mut [u8; 64], intra: bool) -> DecoderResult<()> {
+    let mut prev_val = 0;
+    let mut end = false;
+    for &idx in ZIGZAG.iter() {
+        let nval = if !end { br.read(8)? as u8 } else { prev_val };
+        if intra && idx == 0 {
+            validate!(nval == 8);
+        }
+        if nval == 0 {
+            validate!(prev_val != 0);
+            mat[idx] = prev_val;
+            end = true;
+        } else {
+            mat[idx] = nval;
+            prev_val = nval;
+        }
+    }
+    Ok(())
+}
+
+fn read_vop_complexity_estimation_header(br: &mut BitReader) -> DecoderResult<()> {
+    let estimation_method           = br.read(2)? as u8;
+    if estimation_method == 0b00 || estimation_method == 0b01 {
+        let shape_cplx_est          = !br.read_bool()?;
+        if shape_cplx_est {
+            let _opaque             = br.read_bool()?;
+            let _transparent        = br.read_bool()?;
+            let _intra_cae          = br.read_bool()?;
+            let _inter_cae          = br.read_bool()?;
+            let _no_update          = br.read_bool()?;
+            let _upsampling         = br.read_bool()?;
+        }
+        let tex_cplx_est_set_1      = !br.read_bool()?;
+        if tex_cplx_est_set_1 {
+            let _intra_blocks       = br.read_bool()?;
+            let _inter_blocks       = br.read_bool()?;
+            let _inter4v_blocks     = br.read_bool()?;
+            let _not_coded_blocks   = br.read_bool()?;
+        }
+                                    br.read_marker()?;
+        let tex_cplx_est_set_2      = !br.read_bool()?;
+        if tex_cplx_est_set_2 {
+            let _dct_coeffs         = br.read_bool()?;
+            let _dct_lines          = br.read_bool()?;
+            let _vlc_symbols        = br.read_bool()?;
+            let _vlc_bits           = br.read_bool()?;
+        }
+        let mc_cplx_est             = !br.read_bool()?;
+        if mc_cplx_est {
+            let _apm                = br.read_bool()?;
+            let _npm                = br.read_bool()?;
+            let _interpolate_mc_q   = br.read_bool()?;
+            let _forw_back_mc_q     = br.read_bool()?;
+            let _halfpel2           = br.read_bool()?;
+            let _halfpel4           = br.read_bool()?;
+        }
+                                    br.read_marker()?;
+        if estimation_method == 0b01 {
+            let ver2_cplx_est       = !br.read_bool()?;
+            if ver2_cplx_est {
+                let _sadct          = br.read_bool()?;
+                let _quarterpel     = br.read_bool()?;
+            }
+        }
+    }
+    Ok(())
+}
+
+struct Tables {
+    intra_mcbpc_cb:     Codebook<u8>,
+    inter_mcbpc_cb:     Codebook<u8>,
+    cbpy_cb:            Codebook<u8>,
+    intra_rl_cb:        Codebook<H263RLSym>,
+    inter_rl_cb:        Codebook<H263RLSym>,
+    mv_cb:              Codebook<u8>,
+    luma_dc_len_cb:     Codebook<u8>,
+    chroma_dc_len_cb:   Codebook<u8>,
+}
+
+fn map_idx(idx: usize) -> u8 { idx as u8 }
+
+impl Tables {
+    fn new() -> Self {
+        let mut coderead = H263ShortCodeReader::new(H263_INTRA_MCBPC);
+        let intra_mcbpc_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263ShortCodeReader::new(H263_INTER_MCBPC);
+        let inter_mcbpc_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263ShortCodeReader::new(H263_CBPY);
+        let cbpy_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263RLCodeReader::new(INTRA_RL_CODES);
+        let intra_rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263RLCodeReader::new(H263_RL_CODES);
+        let inter_rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+//        let mut coderead = H263RLCodeReader::new(H263_RL_CODES_AIC);
+//        let aic_rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263ShortCodeReader::new(H263_MV);
+        let mv_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = TableCodebookDescReader::new(LUMA_DC_LEN_CODES, LUMA_DC_LEN_BITS, map_idx);
+        let luma_dc_len_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = TableCodebookDescReader::new(CHROMA_DC_LEN_CODES, CHROMA_DC_LEN_BITS, map_idx);
+        let chroma_dc_len_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+
+        Self {
+            intra_mcbpc_cb,
+            inter_mcbpc_cb,
+            cbpy_cb,
+            intra_rl_cb,
+            inter_rl_cb,
+//            aic_rl_cb,
+            mv_cb,
+            luma_dc_len_cb, chroma_dc_len_cb,
+        }
+    }
+}
+
+pub struct FrameDecoder {
+    short_header:           bool,
+    mb_w:                   usize,
+    mb_h:                   usize,
+    mb_addr:                usize,
+    slice_start:            usize,
+    bitpos:                 usize,
+    pic_type:               FrameType,
+    mb_num_bits:            u8,
+    quant:                  u8,
+    intra_dc_vlc_thr:       u8,
+    fcode_forward:          u8,
+    fcode_backward:         u8,
+    shape:                  Shape,
+    sprite_info:            SpriteInfo,
+    cbs:                    Tables,
+}
+
+impl FrameDecoder {
+    pub fn new() -> Self {
+        Self {
+            short_header:           true,
+            mb_w:                   0,
+            mb_h:                   0,
+            mb_addr:                0,
+            slice_start:            0,
+            bitpos:                 0,
+            pic_type:               FrameType::Other,
+            mb_num_bits:            0,
+            quant:                  0,
+            intra_dc_vlc_thr:       0,
+            fcode_forward:          0,
+            fcode_backward:         0,
+            shape:                  Shape::Rect,
+            sprite_info:            SpriteInfo::default(),
+            cbs:                    Tables::new(),
+        }
+    }
+    pub fn init(&mut self, width: usize, height: usize) {
+        if (width + 15) / 16 == self.mb_w && (height + 15) / 16 == self.mb_h { return; }
+        self.mb_w = (width  + 15) / 16;
+        self.mb_h = (height + 15) / 16;
+        self.bitpos = 0;
+    }
+    pub fn get_dimensions(&self) -> (usize, usize) { (self.mb_w, self.mb_h) }
+    //pub fn is_short_header(&self) -> bool { self.short_header }
+    pub fn parse_vop_header(&mut self, src: &[u8], vol: &VideoObjectLayer) -> DecoderResult<PicInfo> {
+        let mut br = BitReader::new(src, BitReaderMode::BE);
+        br.skip(8)?;
+        const PIC_TYPES: [FrameType; 4] = [FrameType::I, FrameType::P, FrameType::B, FrameType::Other];
+        let pic_type                = PIC_TYPES[br.read(2)? as usize];
+        let modulo_time_base        = br.read_code(UintCodeType::UnaryOnes)?;
+                                    br.read_marker()?;
+        let mut res_bits = 0;
+        while (1 << res_bits) < (vol.time_res - 1) {
+            res_bits += 1;
+        }
+        let vop_time_increment      = br.read(res_bits)?;
+                                    br.read_marker()?;
+        if !br.read_bool()? {
+            return Ok(PicInfo { pic_type: FrameType::Skip, vop_time_increment, modulo_time_base, fcode_f: 1, fcode_b: 1, rounding: false })
+        }
+
+        if vol.newpred_enable {
+            //let vop_id = br.read(ilog2(vop_time_increment).min(15))?;
+            //let vop_id_for_pred = br.read_bool()?;
+            //if vop_id_for_pred {
+            //    let vop_id_for_prediction = br.read(ilog2(vop_time_increment).min(15))?
+            //    br.read_marker()?;
+            //}
+            return Err(DecoderError::NotImplemented);
+        }
+
+        let vop_rounding_type;
+        if vol.shape != Shape::BinaryOnly && (pic_type == FrameType::P || (pic_type == FrameType::Other && vol.sprite_info.sprite == Sprite::GMC)) {
+            vop_rounding_type       = br.read_bool()?;
+        } else {
+            vop_rounding_type = false;
+        }
+        /*if vol.reduced_res && vol.shape == Shape::Rect && (pic_type == FrameType::P || pic_type == FrameType::I) {
+            let _vop_reduced_res    = br.read_bool()?;
+        }*/
+        if vol.shape != Shape::Rect {
+            //if vol.sprite_info.sprite == Sprite::Static && pic_type == FrameType::I {
+            //    read sprite w, h, x, y
+            //}
+            //read various fields
+            return Err(DecoderError::NotImplemented);
+        }
+
+        if vol.shape != Shape::BinaryOnly {
+            if vol.complexity_estimation {
+//read bytes depending on frame size and flags in cplx_est header set
+unimplemented!();
+            }
+
+            const DC_VLC_THRESHOLDS: [u8; 8] = [64, 13, 15, 17, 19, 21, 23, 0];
+            self.intra_dc_vlc_thr    = DC_VLC_THRESHOLDS[br.read(3)? as usize];
+            /*if vol.interlaced {
+                let _top_field_first = br.read_bool()?;
+                let _alt_vert_scan   = br.read_bool()?;
+            }*/
+        }
+
+        if matches!(vol.sprite_info.sprite, Sprite::Static | Sprite::GMC) && pic_type == FrameType::Other {
+            if vol.sprite_info.num_warp_points > 0 {
+                for _ in 0..vol.sprite_info.num_warp_points {
+                    let _du = warping_mv_code(&mut br)?;
+                    let _dv = warping_mv_code(&mut br)?;
+                }
+            }
+            if vol.sprite_info.brightness_change {
+                let len             = br.read_code(UintCodeType::LimitedOnes(4))? as u8;
+                let _change         = br.read(len + 5)?;
+                // 0    ->   -16..-1 | 1..16
+                // 10   ->  -48..-17 | 17..48
+                // 110  -> -112..-49 | 49..112
+                // 1110 -> 113..624
+                // 1111 -> 625..1648
+            }
+            if vol.sprite_info.sprite == Sprite::Static {
+                // sprite piece transmission mode
+                return Err(DecoderError::NotImplemented);
+            }
+        }
+
+        if vol.shape != Shape::BinaryOnly {
+            self.quant              = br.read(5)? as u8; // or quant_precision for high-bitdepth
+            validate!(self.quant != 0);
+            if vol.shape == Shape::Grayscale {
+                // alpha quants
+                return Err(DecoderError::NotImplemented);
+            }
+            if pic_type != FrameType::I {
+                self.fcode_forward  = br.read(3)? as u8;
+                validate!(self.fcode_forward != 0);
+            }
+            if pic_type == FrameType::B {
+                self.fcode_backward = br.read(3)? as u8;
+                validate!(self.fcode_backward != 0);
+            }
+            /* if !vol.scalability */ {
+                if vol.shape != Shape::Rect && pic_type != FrameType::I {
+                    let _vop_shape_coding_type = br.read_bool()?;
+                }
+            } /* else {
+                if enhancement_type { various forward/backward shape parameters }
+                let ref_select_code = br.read(2)?;
+            } */
+        } else {
+            return Err(DecoderError::NotImplemented);
+        }
+
+        self.bitpos = br.tell();
+        self.short_header = false;
+        self.pic_type = pic_type;
+        self.mb_w = (vol.width  + 15) / 16;
+        self.mb_h = (vol.height + 15) / 16;
+        let num_mbs = self.mb_w * self.mb_h;
+        self.mb_num_bits = 1;
+        while (1 << self.mb_num_bits) < num_mbs {
+            self.mb_num_bits += 1;
+        }
+
+        self.shape = vol.shape;
+        self.sprite_info = vol.sprite_info;
+
+        Ok(PicInfo { pic_type, vop_time_increment, modulo_time_base, fcode_f: self.fcode_forward, fcode_b: self.fcode_backward, rounding: vop_rounding_type })
+    }
+    pub fn parse_vop_short_header(&mut self, src: &[u8]) -> DecoderResult<PicInfo> {
+        let mut br = BitReader::new(src, BitReaderMode::BE);
+        validate!(br.read(22)? == 0x20);
+        let _temp_ref               = br.read(8)?;
+                                    br.read_marker()?;
+        validate!(br.read(1)? == 0);
+        let _split_screen           = br.read_bool()?;
+        let _document_camera        = br.read_bool()?;
+        let _full_pic_freeze_release = br.read_bool()?;
+        let source_format           = br.read(3)?;
+        let (width, height) = match source_format {
+                1 => ( 128,   96),
+                2 => ( 176,  144),
+                3 => ( 352,  288),
+                4 => ( 704,  576),
+                5 => (1408, 1152),
+                _ => return Err(DecoderError::NotImplemented),
+            };
+        let pic_type                = if br.read_bool()? { FrameType::P } else { FrameType::I };
+        validate!(br.read(4)? == 0);
+        let vop_quant               = br.read(5)? as u8;
+        validate!(vop_quant != 0);
+        validate!(br.read(1)? == 0);
+        while br.read_bool()? { // PEI
+            br.skip(8)?;
+        }
+
+        self.bitpos = br.tell();
+        self.short_header = true;
+        self.pic_type = pic_type;
+        self.quant = vop_quant;
+        self.mb_w = (width  + 15) / 16;
+        self.mb_h = (height + 15) / 16;
+        self.shape = Shape::Rect;
+        self.sprite_info = SpriteInfo::default();
+        self.fcode_forward = 1;
+        self.fcode_backward = 1;
+        self.intra_dc_vlc_thr = 64;
+        Ok(PicInfo{ pic_type, vop_time_increment: 1, modulo_time_base: 0, fcode_f: 1, fcode_b: 1, rounding: false })
+    }
+
+    pub fn start_decoding(&mut self) {
+        self.mb_addr = 0;
+        self.slice_start = 0;
+    }
+
+    pub fn decode_mb(&mut self, src: &[u8], mb: &mut Macroblock, colo_not_coded: bool, mb_x: usize, pred: &mut PredState, qinfo: &QuantInfo) -> DecoderResult<()> {
+        let mut br = BitReader::new(src, BitReaderMode::BE);
+        br.skip(self.bitpos as u32)?;
+
+        if !self.short_header && br.left() > 17 {
+            let mut abits = br.peek(24);
+            let cur_tail = 8 - (br.tell() & 7);
+
+            let resync_marker = ((1 << cur_tail) - 1) >> 1;
+            let possible_stuffing = abits >> (24 - cur_tail);
+            if possible_stuffing == resync_marker {
+                abits = (abits << cur_tail) & 0xFFFFFF;
+            } else {
+                abits = !0;
+            }
+            if (abits >> 8) == 0x0000 {
+                br.skip(cur_tail as u32)?;
+                while !br.read_bool()? {}
+                // video packet header
+                if self.shape != Shape::Rect { return Err(DecoderError::NotImplemented); }
+                let mb_no = br.read(self.mb_num_bits)?;
+                validate!(mb_no as usize == self.mb_addr);
+                if self.shape != Shape::Binary {
+                    let qscale = br.read(5)? as u8;
+                    validate!(qscale != 0);
+                    self.quant = qscale;
+                }
+                if self.shape == Shape::Rect && br.read_bool()? {
+                    // extended header
+                    unimplemented!();
+                }
+                self.slice_start = self.mb_addr;
+                pred.reset();
+            }
+        }
+
+        if self.shape != Shape::Rect { return Err(DecoderError::NotImplemented); }
+        let mut dquant = 0;
+        if self.pic_type != FrameType::B {
+            /* ignore binary shape coding */
+            /* ignore non-rectangular shape with sprite stuff */
+            let cbpc;
+            let has_dquant;
+            {
+                let (mcbpc_cb, stuffing) = if self.pic_type == FrameType::I {
+                        (&self.cbs.intra_mcbpc_cb, 8)
+                    } else {
+                        (&self.cbs.inter_mcbpc_cb, 20)
+                    };
+                let mut mcbpc = stuffing;
+                while mcbpc == stuffing {
+                    if self.pic_type != FrameType::I /* and not piece static sprite */ {
+                        if br.read_bool()? { // not_coded
+                            mb.mb_type = MBType::Skip;
+                            mb.cbp = 0;
+                            mb.quant = self.quant;
+                            mb.mvs[0] = ZERO_MV;
+                            self.bitpos = br.tell();
+                            self.mb_addr += 1;
+                            return Ok(());
+                        }
+                    }
+                    mcbpc = br.read_cb(mcbpc_cb)?;
+                }
+                cbpc = mcbpc & 3;
+                if self.pic_type == FrameType::I {
+                    has_dquant = (mcbpc & 4) != 0;
+                    mb.mb_type = MBType::Intra;
+                    mb.mvs[0] = ZERO_MV;
+                } else {
+                    has_dquant = (mcbpc & 8) != 0;
+                    mb.mb_type = match (mcbpc & !8) >> 2 {
+                            1 => MBType::Intra,
+                            4 => MBType::Inter4MV,
+                            _ => MBType::Inter,
+                        };
+                }
+            }
+            /* ignore sprite mcsel */
+            if !self.short_header && mb.mb_type == MBType::Intra {
+                mb.ac_pred = br.read_bool()?;
+            }
+            let mut cbpy = br.read_cb(&self.cbs.cbpy_cb)?;
+            if mb.mb_type != MBType::Intra {
+                cbpy ^= 0xF;
+            }
+            mb.cbp = (cbpy << 2) | cbpc;
+            if has_dquant {
+                const DQUANTS: [i8; 4] = [-1, -2, 1, 2];
+                dquant = DQUANTS[br.read(2)? as usize];
+            }
+            /* interlaced information skipped */
+            /* scalability and sprite checks skipped */
+            if mb.mb_type == MBType::Inter {
+                mb.mvs[0] = read_mv(&mut br, &self.cbs.mv_cb, self.fcode_forward)?;
+            }
+            if mb.mb_type == MBType::Inter4MV {
+                for mv in mb.mvs.iter_mut() {
+                    *mv = read_mv(&mut br, &self.cbs.mv_cb, self.fcode_forward)?;
+                }
+            }
+        } else {
+            if !colo_not_coded /* or some sprite and scalability checks */ {
+                let modb = br.read_code(UintCodeType::LimitedZeroes(2))?; //none, mb_type, cbpb+mb_type
+                if modb != 0 {
+                    let mbtype = br.read_code(UintCodeType::UnaryZeroes)?;
+                    validate!(mbtype < 4);
+                    const B_MB_TYPES: [MBType; 4] = [MBType::Direct, MBType::Interpolate, MBType::Backward, MBType::Forward];
+                    mb.mb_type = B_MB_TYPES[mbtype as usize];
+                    mb.cbp = if modb == 2 { br.read(6)? as u8 } else { 0 };
+                    /* scalability checks ignored */
+                    if mb.mb_type != MBType::Direct && mb.cbp != 0 {
+                        const DBQUANTS: [i8; 3] = [0, -2, 2];
+                        dquant = DBQUANTS[br.read_code(UintCodeType::Unary012)? as usize];
+                    }
+                    /* interlaced information ignored */
+                    match mb.mb_type {
+                        MBType::Direct => {
+                            mb.mvs[0] = read_mv(&mut br, &self.cbs.mv_cb, 0)?;
+                        },
+                        MBType::Interpolate => {
+                            mb.mvs[0] = read_mv(&mut br, &self.cbs.mv_cb, self.fcode_forward)?;
+                            mb.mvs[1] = read_mv(&mut br, &self.cbs.mv_cb, self.fcode_backward)?;
+                        },
+                        MBType::Backward => {
+                            mb.mvs[0] = read_mv(&mut br, &self.cbs.mv_cb, self.fcode_backward)?;
+                        },
+                        MBType::Forward => {
+                            mb.mvs[0] = read_mv(&mut br, &self.cbs.mv_cb, self.fcode_forward)?;
+                        },
+                        _ => unreachable!(),
+                    }
+                } else {
+                    mb.mb_type = MBType::Direct;
+                    mb.cbp = 0;
+                    mb.mvs = [ZERO_MV; 4];
+                }
+            } else {
+                mb.mb_type = MBType::Forward;
+                mb.cbp = 0;
+                mb.mvs = [ZERO_MV; 4];
+            }
+        }
+        /* ignore alpha part for gray shape */
+
+        let quant = self.quant as i8 + dquant;
+        validate!(quant >= 0 && quant < 64);
+        let quant = quant as u8;
+        mb.quant = quant;
+
+        let mut cbp = mb.cbp;
+        if cbp != 0 || mb.mb_type == MBType::Intra {
+            mb.coeffs = [[0; 64]; 6];
+        }
+        if mb.mb_type == MBType::Intra {
+            let switch_cbs = quant >= self.intra_dc_vlc_thr;
+
+            pred.set_quant(mb_x, mb.quant);
+
+            let dc_scale = super::dsp::get_dc_scales(mb.quant);
+
+            for (i, block) in mb.coeffs.iter_mut().enumerate() {
+                let dc_len_cb = if i < 4 { &self.cbs.luma_dc_len_cb } else { &self.cbs.chroma_dc_len_cb };
+                let start = if self.short_header || !switch_cbs {
+                        block[0] = decode_intra_dc(&mut br, dc_len_cb, self.short_header)?;
+                        1
+                    } else {
+                        0
+                    };
+                let pred_dir;
+                if !self.short_header {
+                    let cur_scale = dc_scale[if i >= 4 { 1 } else { 0 }];
+                    let (dc, dir) = pred.predict_dc(mb_x, block[0], cur_scale, i);
+                    block[0] = dc;
+                    pred_dir = dir;
+                } else {
+                    block[0] *= 8;
+                    pred_dir = false;
+                }
+                if (cbp & 0x20) != 0 {
+                    let scan = if !mb.ac_pred {
+                            &ZIGZAG
+                        } else if pred_dir {
+                            &ALT_HOR_SCAN
+                        } else {
+                            &ALT_VERT_SCAN
+                        };
+                    decode_block_intra(&mut br, &self.cbs.intra_rl_cb, block, start, self.short_header, scan)?;
+                    dequant_intra_block(block, mb.quant, qinfo);
+                }
+                if mb.ac_pred {
+                    pred.predict_ac(block, mb_x, i, pred_dir);
+                } else {
+                    pred.save_ac(block, mb_x, i);
+                }
+                cbp <<= 1;
+            }
+        } else {
+            for block in mb.coeffs.iter_mut() {
+                if (cbp & 0x20) != 0 {
+                    decode_block_inter(&mut br, &self.cbs.inter_rl_cb, block, self.short_header)?;
+                }
+                cbp <<= 1;
+            }
+            dequant_inter(mb, qinfo);
+        }
+        self.bitpos = br.tell();
+
+        let nstate = if matches!(mb.mb_type, MBType::Inter | MBType::Inter4MV) {
+                NState {
+                    has_l:  mb_x > 0 && self.mb_addr > self.slice_start,
+                    has_t:  self.mb_addr >= self.slice_start + self.mb_w,
+                    has_tr: self.mb_addr + 1 >= self.slice_start + self.mb_w && (mb_x + 1) < self.mb_w,
+                }
+            } else { NState::default() };
+        match mb.mb_type {
+            MBType::Intra => {
+                mb.mvs = [ZERO_MV; 4];
+            },
+            MBType::Inter => {
+                mb.mvs[0] = pred.pred_mv(mb_x, &nstate, mb.mvs[0]);
+                mb.mvs = [mb.mvs[0]; 4];
+            },
+            MBType::Inter4MV => {
+                pred.pred_4mv(mb_x, &nstate, &mut mb.mvs);
+            },
+            MBType::Interpolate => {
+                mb.mvs[0] = pred.pred_mv_f(mb.mvs[0]);
+                mb.mvs[1] = pred.pred_mv_b(mb.mvs[1]);
+            },
+            MBType::Backward => {
+                mb.mvs[0] = pred.pred_mv_b(mb.mvs[0]);
+            },
+            MBType::Forward => {
+                mb.mvs[0] = pred.pred_mv_f(mb.mvs[0]);
+            },
+            _ => {},
+        }
+
+        self.mb_addr += 1;
+
+        Ok(())
+    }
+}
+
+fn read_mv(br: &mut BitReader, mv_cb: &Codebook<u8>, fcode: u8) -> DecoderResult<MV> {
+    let mut x = i16::from(br.read_cb(mv_cb)?);
+    let xsign = x != 0 && br.read_bool()?;
+    if x != 0 && fcode > 1 {
+        x = ((x - 1) << (fcode - 1)) + (br.read(fcode - 1)? as i16) + 1;
+    }
+    if xsign {
+        x = -x;
+    }
+    let mut y = i16::from(br.read_cb(mv_cb)?);
+    let ysign = y != 0 && br.read_bool()?;
+    if y != 0 && fcode > 1 {
+        y = ((y - 1) << (fcode - 1)) + (br.read(fcode - 1)? as i16) + 1;
+    }
+    if ysign {
+        y = -y;
+    }
+    Ok(MV{ x, y })
+}
+
+fn warping_mv_code(br: &mut BitReader) -> DecoderResult<i32> {
+    let mut val = 0;
+    let dmv_length = br.read(2)?;
+    if dmv_length > 0 {
+        let nbits = match dmv_length {
+                0b00 => unreachable!(),
+                0b01 => br.read(1)? as u8 + 1,
+                0b10 => br.read(1)? as u8 + 3,
+                _ => {
+                    let len = br.read_code(UintCodeType::UnaryOnes)?;
+                    validate!(len <= 9);
+                    len as u8 + 5
+                },
+            };
+        val = br.read(nbits)? as i32;
+        let hrange = 1 << (nbits - 1);
+        if val < hrange {
+            val -= (1 << nbits) - 1;
+        }
+    }
+    br.read_marker()?;
+    Ok(val)
+}
+
+fn decode_escape(br: &mut BitReader, cb: &Codebook<H263RLSym>, short_header: bool, intra: bool) -> DecoderResult<(u8, i16, bool)> {
+    if !short_header {
+        if !br.read_bool()? { // 0 - type 1
+            let code = br.read_cb(cb)?;
+            validate!(!code.is_escape());
+            let run = code.get_run();
+            let mut level = code.get_level();
+            let last = code.is_last();
+            let lmax = if intra {
+                    match (last, run) { // Table B-19
+                        (false, 0) => 27,
+                        (false, 1) => 10,
+                        (false, 2) => 5,
+                        (false, 3) => 4,
+                        (false, 4..=7) => 3,
+                        (false, 8..=9) => 2,
+                        (true, 0) => 8,
+                        (true, 1) => 3,
+                        (true, 2..=6) => 2,
+                        _ => 1,
+                    }
+                } else {
+                    match (last, run) { // Table B-20
+                        (false, 0) => 12,
+                        (false, 1) => 6,
+                        (false, 2) => 4,
+                        (false, 3..=6) => 3,
+                        (false, 7..=10) => 2,
+                        (true, 0) => 3,
+                        (true, 1) => 2,
+                        _ => 1,
+                    }
+                };
+            level += lmax;
+            if br.read_bool()? {
+                level = -level;
+            }
+            Ok((run, level, last))
+        } else if !br.read_bool()? { // 10 - type 2
+            let code = br.read_cb(cb)?;
+            validate!(!code.is_escape());
+            let run = code.get_run();
+            let mut level = code.get_level();
+            let last = code.is_last();
+            let rmax = if intra {
+                    match (last, level) { // Table B-21
+                        (false, 1) => 14,
+                        (false, 2) => 9,
+                        (false, 3) => 7,
+                        (false, 4) => 3,
+                        (false, 5) => 2,
+                        (false, 6..=10) => 1,
+                        (true, 1) => 20,
+                        (true, 2) => 6,
+                        (true, 3) => 1,
+                        _ => 0,
+                    }
+                } else {
+                    match (last, level) { // Table B-22
+                        (false, 1) => 26,
+                        (false, 2) => 10,
+                        (false, 3) => 6,
+                        (false, 4) => 2,
+                        (false, 5..=6) => 1,
+                        (true, 1) => 40,
+                        (true, 2) => 1,
+                        _ => 0,
+                    }
+                };
+            if br.read_bool()? {
+                level = -level;
+            }
+            Ok((run + rmax + 1, level, last))
+        } else { // 11 - type 3
+            let last  = br.read_bool()?;
+            let run   = br.read(6)? as u8;
+                        br.read_marker()?;
+            let level = br.read_s(12)? as i16;
+            validate!(level != -2048 && level != 0);
+                        br.read_marker()?;
+            Ok((run, level, last))
+        }
+    } else { // type 4
+        let last  = br.read_bool()?;
+        let run   = br.read(6)? as u8;
+        let level = br.read_s(8)? as i16;
+        validate!(level != 0 && level != -128);
+        Ok((run, level, last))
+    }
+}
+
+fn decode_intra_dc(br: &mut BitReader, dc_len_cb: &Codebook<u8>, short_header: bool) -> DecoderResult<i16> {
+    if short_header {
+        let mut val = br.read(8)? as i16;
+        validate!(val != 0 && val != 128);
+        if val == 0xFF {
+            val = 0x80;
+        }
+        Ok(val)
+    } else {
+        let dc_len = br.read_cb(dc_len_cb)?;
+        validate!(dc_len < 10); // higher lengths are for higher bitdepths
+        if dc_len > 0 {
+            let val = br.read(dc_len)? as i16;
+            let hsize = 1 << (dc_len - 1);
+            if val >= hsize {
+                Ok(val)
+            } else {
+                Ok(val - (1 << dc_len) + 1)
+            }
+        } else {
+            Ok(0)
+        }
+    }
+}
+
+fn decode_block_intra(br: &mut BitReader, cb: &Codebook<H263RLSym>, block: &mut [i16; 64], mut idx: usize, short_header: bool, scan: &[usize; 64]) -> DecoderResult<()> {
+    loop {
+        let code = br.read_cb(cb)?;
+        let (run, level, last) = if !code.is_escape() {
+                let mut level = code.get_level();
+                if br.read_bool()? {
+                    level = -level;
+                }
+                (code.get_run(), level, code.is_last())
+            } else {
+                decode_escape(br, cb, short_header, true)?
+            };
+        idx += usize::from(run);
+        validate!(idx < 64);
+        block[scan[idx]] = level;
+        idx += 1;
+        if last { break; }
+    }
+
+    Ok(())
+}
+
+fn decode_block_inter(br: &mut BitReader, cb: &Codebook<H263RLSym>, block: &mut [i16; 64], short_header: bool) -> DecoderResult<()> {
+    let mut idx = 0;
+    loop {
+        let code = br.read_cb(cb)?;
+        let (run, level, last) = if !code.is_escape() {
+                let mut level = code.get_level();
+                if br.read_bool()? {
+                    level = -level;
+                }
+                (code.get_run(), level, code.is_last())
+            } else {
+                decode_escape(br, cb, short_header, false)?
+            };
+        idx += usize::from(run);
+        validate!(idx < 64);
+        block[ZIGZAG[idx]] = level;
+        if last { break; }
+        idx += 1;
+    }
+    Ok(())
+}
+
+macro_rules! rlcodes{
+    ($(($c:expr, $b:expr, $r:expr, $l:expr)),*) => {
+        &[$(H263RLCodeDesc{ code: $c, bits: $b, sym: H263RLSym{ run: $r, level: $l}}),*]
+    }
+}
+
+const LUMA_DC_LEN_CODES: &[u8] = &[ 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1,  1,  1 ];
+const LUMA_DC_LEN_BITS:  &[u8] = &[ 3, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
+
+const CHROMA_DC_LEN_CODES: &[u8] = &[ 3, 2, 1, 1, 1, 1, 1, 1, 1, 1,  1,  1,  1 ];
+const CHROMA_DC_LEN_BITS:  &[u8] = &[ 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ];
+
+const INTRA_RL_CODES: &[H263RLCodeDesc] = rlcodes!(
+    (0x02,  2,  0,  1), (0x0F,  4,  0,  3), (0x15,  6,  0,  6), (0x17,  7,  0,  9),
+    (0x1F,  8,  0, 10), (0x25,  9,  0, 13), (0x24,  9,  0, 14), (0x21, 10,  0, 17),
+    (0x20, 10,  0, 18), (0x07, 11,  0, 21), (0x06, 11,  0, 22), (0x20, 11,  0, 23),
+    (0x06,  3,  0,  2), (0x14,  6,  1,  2), (0x1E,  8,  0, 11), (0x0F, 10,  0, 19),
+    (0x21, 11,  0, 24), (0x50, 12,  0, 25), (0x0E,  4,  1,  1), (0x1D,  8,  0, 12),
+    (0x0E, 10,  0, 20), (0x51, 12,  0, 26), (0x0D,  5,  0,  4), (0x23,  9,  0, 15),
+    (0x0D, 10,  1,  7), (0x0C,  5,  0,  5), (0x22,  9,  4,  2), (0x52, 12,  0, 27),
+    (0x0B,  5,  2,  1), (0x0C, 10,  2,  4), (0x53, 12,  1,  9), (0x13,  6,  0,  7),
+    (0x0B, 10,  3,  4), (0x54, 12,  6,  3), (0x12,  6,  0,  8), (0x0A, 10,  4,  3),
+    (0x11,  6,  3,  1), (0x09, 10,  8,  2), (0x10,  6,  4,  1), (0x08, 10,  5,  3),
+    (0x16,  7,  1,  3), (0x55, 12,  1, 10), (0x15,  7,  2,  2), (0x14,  7,  7,  1),
+    (0x1C,  8,  1,  4), (0x1B,  8,  3,  2), (0x21,  9,  0, 16), (0x20,  9,  1,  5),
+    (0x1F,  9,  1,  6), (0x1E,  9,  2,  3), (0x1D,  9,  3,  3), (0x1C,  9,  5,  2),
+    (0x1B,  9,  6,  2), (0x1A,  9,  7,  2), (0x22, 11,  1,  8), (0x23, 11,  9,  2),
+    (0x56, 12,  2,  5), (0x57, 12,  7,  3), (0x07,  4,  0, -1), (0x19,  9, 11,  1),
+    (0x05, 11,  0, -6), (0x0F,  6,  1, -1), (0x04, 11,  0, -7), (0x0E,  6,  2, -1),
+    (0x0D,  6,  5,  1), (0x0C,  6,  0, -2), (0x13,  7,  5, -1), (0x12,  7,  6,  1),
+    (0x11,  7,  3, -1), (0x10,  7,  4, -1), (0x1A,  8,  9, -1), (0x19,  8,  8,  1),
+    (0x18,  8,  9,  1), (0x17,  8, 10,  1), (0x16,  8,  0, -3), (0x15,  8,  6, -1),
+    (0x14,  8,  7, -1), (0x13,  8,  8, -1), (0x18,  9, 12,  1), (0x17,  9,  0, -4),
+    (0x16,  9,  1, -2), (0x15,  9, 10, -1), (0x14,  9, 11, -1), (0x13,  9, 12, -1),
+    (0x12,  9, 13, -1), (0x11,  9, 14, -1), (0x07, 10, 13,  1), (0x06, 10,  0, -5),
+    (0x05, 10,  1, -3), (0x04, 10,  2, -2), (0x24, 11,  3, -2), (0x25, 11,  4, -2),
+    (0x26, 11, 15, -1), (0x27, 11, 16, -1), (0x58, 12, 14,  1), (0x59, 12,  0, -8),
+    (0x5A, 12,  5, -2), (0x5B, 12,  6, -2), (0x5C, 12, 17, -1), (0x5D, 12, 18, -1),
+    (0x5E, 12, 19, -1), (0x5F, 12, 20, -1), (0x03,  7,  0,  0)
+);
+
+const ALT_HOR_SCAN: [usize; 64] = [
+     0,  1,  2,  3,  8,  9, 16, 17,
+    10, 11,  4,  5,  6,  7, 15, 14,
+    13, 12, 19, 18, 24, 25, 32, 33,
+    26, 27, 20, 21, 22, 23, 28, 29,
+    30, 31, 34, 35, 40, 41, 48, 49,
+    42, 43, 36, 37, 38, 39, 44, 45,
+    46, 47, 50, 51, 56, 57, 58, 59,
+    52, 53, 54, 55, 60, 61, 62, 63
+];
+
+const ALT_VERT_SCAN: [usize; 64] = [
+     0,  8, 16, 24,  1,  9,  2, 10,
+    17, 25, 32, 40, 48, 56, 57, 49,
+    41, 33, 26, 18,  3, 11,  4, 12,
+    19, 27, 34, 42, 50, 58, 35, 43,
+    51, 59, 20, 28,  5, 13,  6, 14,
+    21, 29, 36, 44, 52, 60, 37, 45,
+    53, 61, 22, 30,  7, 15, 23, 31,
+    38, 46, 54, 62, 39, 47, 55, 63
+];
diff --git a/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs b/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs
new file mode 100644 (file)
index 0000000..a71da94
--- /dev/null
@@ -0,0 +1,259 @@
+use nihav_core::codecs::*;
+use nihav_codec_support::codecs::{MV, ZERO_MV};
+
+use super::bitstream::*;
+use super::dispatch::*;
+use super::dsp::*;
+use super::types::*;
+
+#[derive(Clone,Copy,Default)]
+pub struct FrameTimestamp {
+    pub base:       u32,
+    pub offset:     u32,
+}
+
+impl FrameTimestamp {
+    pub fn get_ts(self, base_rate: u32) -> u64 {
+        u64::from(self.base) * u64::from(base_rate) + u64::from(self.offset)
+    }
+}
+
+pub type OptionalRef = Option<NAVideoBufferRef<u8>>;
+
+pub struct FrameContext {
+    fdec:           FrameDecoder,
+    cur_ts:         u64,
+    bwd_ref:        OptionalRef,
+    bwd_ts:         u64,
+    fwd_ref:        OptionalRef,
+    fwd_ts:         u64,
+    pred:           PredState,
+    dsp:            DSP,
+}
+
+impl FrameContext {
+    pub fn new() -> Self {
+        Self {
+            fdec:           FrameDecoder::new(),
+            cur_ts:         0,
+            bwd_ref:        None,
+            bwd_ts:         0,
+            fwd_ref:        None,
+            fwd_ts:         0,
+            pred:           PredState::new(),
+            dsp:            DSP::new(),
+        }
+    }
+    pub fn use_xvid_idct(&mut self) {
+        self.dsp.use_xvid_idct();
+    }
+    pub fn resize(&mut self, mb_w: usize) {
+        self.pred.resize(mb_w);
+    }
+    pub fn get_dimensions(&self) -> (usize, usize) { self.fdec.get_dimensions() }
+    pub fn decode_frame_header(&mut self, src: &[u8], seq: &SequenceContext) -> DecoderResult<PicInfo> {
+        self.fdec.init(seq.width, seq.height);
+        if let Some(ref vol) = seq.vol {
+            self.fdec.parse_vop_header(src, vol)
+        } else {
+            self.fdec.parse_vop_short_header(src)
+        }
+    }
+    pub fn set_refs(&mut self, cur_ts: u64, fwd_ref: OptionalRef, fwd_ts: u64, bwd_ref: OptionalRef, bwd_ts: u64) {
+        self.cur_ts  = cur_ts;
+        self.fwd_ref = fwd_ref;
+        self.fwd_ts  = fwd_ts;
+        self.bwd_ref = bwd_ref;
+        self.bwd_ts  = bwd_ts;
+    }
+    fn calc_mvpos_blk(mv: MV, x: usize, y: usize, mb_w: usize, mb_h: usize, bsize: usize) -> (usize, usize) {
+        let mut xpos = (x as isize) + (((mv.x + 1) >> 1) as isize) + (bsize as isize);
+        if (mv.x & 1) != 0 {
+            xpos += 1;
+        }
+        let mut ypos = (y as isize) + (((mv.y + 1) >> 1) as isize) + (bsize as isize);
+        if (mv.y & 1) != 0 {
+            ypos += 1;
+        }
+        let xpos = (xpos.max(1) as usize).min(mb_w * 16);
+        let ypos = (ypos.max(1) as usize).min(mb_h * 16);
+        (xpos, ypos)
+    }
+    fn calc_mvpos(mv: MV, mb_x: usize, mb_y: usize, mb_w: usize, mb_h: usize) -> (usize, usize) {
+        Self::calc_mvpos_blk(mv, mb_x * 16, mb_y * 16, mb_w, mb_h, 16)
+    }
+    fn calc_mvpos_4mv(mvs: &[MV; 4], mb_x: usize, mb_y: usize, mb_w: usize, mb_h: usize) -> (usize, usize) {
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let avg_mv = MV {x: (sum_mv.x + 3) >> 2, y: (sum_mv.y + 3) >> 2 };
+        let (mut xpos, mut ypos) = Self::calc_mvpos_blk(avg_mv, mb_x * 16, mb_y * 16, mb_w, mb_h, 8);
+        for (n, &mv) in mvs.iter().enumerate() {
+            let (xcand, ycand) = Self::calc_mvpos_blk(mv, mb_x * 16 + (n & 1) * 8, mb_y * 16 + (n / 2) * 8, mb_w, mb_h, 8);
+            if (ycand > ypos) || (ycand == ypos && xcand > xpos) {
+                xpos = xcand;
+                ypos = ycand;
+            }
+        }
+        (xpos, ypos)
+    }
+    pub fn decode_frame(&mut self, src: &[u8], mut frm: NASimpleVideoFrame<u8>, seq: &SequenceContext, pic_info: &PicInfo, pstate: &mut PicState, helper: &mut dyn MTHelper) -> DecoderResult<()> {
+        let (mb_w, mb_h) = self.fdec.get_dimensions();
+        let ftype = pic_info.pic_type;
+
+        let mut mb = Macroblock::new();
+        self.pred.reset();
+        self.pred.set_fcodes(pic_info.fcode_f, pic_info.fcode_b);
+        self.fdec.start_decoding();
+        self.dsp.set_rounding(pic_info.rounding);
+        let mut mb_addr = 0;
+        for mb_y in 0..mb_h {
+            for mb_x in 0..mb_w {
+                let colo_not_coded = ftype == FrameType::B && helper.is_colo_mb_not_coded(mb_addr, self.fwd_ts as u32)?;
+                self.fdec.decode_mb(src, &mut mb, colo_not_coded, mb_x, &mut self.pred, &seq.qinfo)?;
+
+                pstate.mb_type[mb_addr] = mb.mb_type;
+                match mb.mb_type {
+                    MBType::Intra => {
+                        self.dsp.put_mb(&mut frm, &mut mb, mb_x, mb_y);
+                    },
+                    MBType::Skip => {
+                        if let Some(pframe) = self.fwd_ref.clone() {
+                            helper.wait_for_mb(mb_x * 16 + 16, mb_y * 16 + 16, self.fwd_ts as u32)?;
+                            self.dsp.mb_mv(&mut frm, mb_x, mb_y, pframe, ZERO_MV);
+                        }
+                    },
+                    MBType::Inter => {
+                        if let Some(pframe) = self.fwd_ref.clone() {
+                            let (xpos, ypos) = Self::calc_mvpos(mb.mvs[0], mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.fwd_ts as u32)?;
+                            self.dsp.mb_mv(&mut frm, mb_x, mb_y, pframe, mb.mvs[0]);
+                            self.dsp.add_coeffs(&mut frm, &mut mb, mb_x, mb_y);
+                        }
+                    },
+                    MBType::Inter4MV => {
+                        if let Some(pframe) = self.fwd_ref.clone() {
+                            let (xpos, ypos) = Self::calc_mvpos_4mv(&mb.mvs, mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.fwd_ts as u32)?;
+                            self.dsp.mb_4mv(&mut frm, mb_x, mb_y, pframe, &mb.mvs);
+                            self.dsp.add_coeffs(&mut frm, &mut mb, mb_x, mb_y);
+                        }
+                    },
+                    MBType::Direct => {
+                        let trd = (self.fwd_ts - self.bwd_ts) as i32;
+                        let trb = (self.cur_ts - self.bwd_ts) as i32;
+                        validate!(trd > 0 && trb > 0);
+                        let diff_mv = mb.mvs[0];
+                        let mut f_mvs = [ZERO_MV; 4];
+                        let mut b_mvs = [ZERO_MV; 4];
+                        let colo_mvs = helper.get_colo_mvs(mb_addr, self.fwd_ts as u32)?;
+                        for ((f_mv, b_mv), &src_mv) in f_mvs.iter_mut().zip(b_mvs.iter_mut()).zip(colo_mvs.iter()) {
+                            let ref_mv = src_mv.scale(trb, trd);
+                            *f_mv = ref_mv + diff_mv;
+                            *b_mv = MV::b_sub(src_mv, *f_mv, diff_mv, trb, trd);
+                        }
+                        if let (Some(f_frame), Some(b_frame)) = (self.fwd_ref.clone(), self.bwd_ref.clone()) {
+                            let (xpos, ypos) = Self::calc_mvpos_4mv(&f_mvs, mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.fwd_ts as u32)?;
+                            self.dsp.mb_4mv(&mut frm, mb_x, mb_y, f_frame, &f_mvs);
+                            let (xpos, ypos) = Self::calc_mvpos_4mv(&b_mvs, mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.bwd_ts as u32)?;
+                            self.dsp.avg_mb_4mv(&mut frm, mb_x, mb_y, b_frame, &b_mvs);
+                            self.dsp.add_coeffs(&mut frm, &mut mb, mb_x, mb_y);
+                        }
+                    },
+                    MBType::Interpolate => {
+                        if let (Some(f_frame), Some(b_frame)) = (self.fwd_ref.clone(), self.bwd_ref.clone()) {
+                            let (xpos, ypos) = Self::calc_mvpos(mb.mvs[0], mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.fwd_ts as u32)?;
+                            self.dsp.mb_mv(&mut frm, mb_x, mb_y, f_frame, mb.mvs[0]);
+                            let (xpos, ypos) = Self::calc_mvpos(mb.mvs[1], mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.bwd_ts as u32)?;
+                            self.dsp.avg_mb_mv(&mut frm, mb_x, mb_y, b_frame, mb.mvs[1]);
+                            self.dsp.add_coeffs(&mut frm, &mut mb, mb_x, mb_y);
+                        }
+                    },
+                    MBType::Backward => {
+                        if let Some(pframe) = self.bwd_ref.clone() {
+                            let (xpos, ypos) = Self::calc_mvpos(mb.mvs[0], mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.bwd_ts as u32)?;
+                            self.dsp.mb_mv(&mut frm, mb_x, mb_y, pframe, mb.mvs[0]);
+                            self.dsp.add_coeffs(&mut frm, &mut mb, mb_x, mb_y);
+                        }
+                    },
+                    MBType::Forward => {
+                        if let Some(pframe) = self.fwd_ref.clone() {
+                            let (xpos, ypos) = Self::calc_mvpos(mb.mvs[0], mb_x, mb_y, mb_w, mb_h);
+                            helper.wait_for_mb(xpos, ypos, self.fwd_ts as u32)?;
+                            self.dsp.mb_mv(&mut frm, mb_x, mb_y, pframe, mb.mvs[0]);
+                            self.dsp.add_coeffs(&mut frm, &mut mb, mb_x, mb_y);
+                        }
+                    },
+                }
+                pstate.mv[mb_addr] = mb.mvs;
+                mb_addr += 1;
+                helper.update_pos(mb_addr);
+            }
+            self.pred.update_row();
+        }
+        Ok(())
+    }
+}
+
+pub struct SequenceContext {
+    width:          usize,
+    height:         usize,
+    vol:            Option<VideoObjectLayer>,
+    qinfo:          QuantInfo,
+    bwd_ts:         FrameTimestamp,
+    fwd_ts:         FrameTimestamp,
+}
+
+impl SequenceContext {
+    pub fn new() -> Self {
+        Self {
+            width:          0,
+            height:         0,
+            vol:            None,
+            qinfo:          QuantInfo::default(),
+            bwd_ts:         FrameTimestamp::default(),
+            fwd_ts:         FrameTimestamp::default(),
+        }
+    }
+    pub fn get_dimensions(&self) -> (usize, usize) { (self.width, self.height) }
+    pub fn set_dimensions(&mut self, width: usize, height: usize) {
+        self.width  = width;
+        self.height = height;
+    }
+    pub fn set_vol(&mut self, vol: VideoObjectLayer) {
+        self.set_dimensions(vol.width, vol.height);
+        self.qinfo = vol.quant_info.clone();
+        self.vol = Some(vol);
+    }
+    pub fn update_ts(&mut self, pic_info: &PicInfo) -> (u64, u64, u64) {
+        match pic_info.pic_type {
+            FrameType::I | FrameType::P | FrameType::Skip => {
+                self.bwd_ts = self.fwd_ts;
+                if let Some(ref vol) = self.vol {
+                    self.fwd_ts.base += pic_info.modulo_time_base;
+                    self.fwd_ts.offset = pic_info.vop_time_increment;
+                    (self.fwd_ts.get_ts(vol.time_res), self.bwd_ts.get_ts(vol.time_res), 0)
+                } else {
+                    self.fwd_ts.offset += 1;
+                    (self.fwd_ts.get_ts(0), self.bwd_ts.get_ts(0), 0)
+                }
+            },
+            FrameType::B => {
+                let mut cur_ts = self.bwd_ts;
+                cur_ts.base += pic_info.modulo_time_base;
+                cur_ts.offset = pic_info.vop_time_increment;
+                if let Some(ref vol) = self.vol {
+                    (cur_ts.get_ts(vol.time_res),
+                     self.fwd_ts.get_ts(vol.time_res),
+                     self.bwd_ts.get_ts(vol.time_res))
+                } else {
+                    unreachable!()
+                }
+            },
+            _ => unimplemented!()
+        }
+    }
+}
diff --git a/nihav-mpeg/src/codecs/mpeg4asp/dispatch.rs b/nihav-mpeg/src/codecs/mpeg4asp/dispatch.rs
new file mode 100644 (file)
index 0000000..67948ee
--- /dev/null
@@ -0,0 +1,9 @@
+use nihav_core::codecs::DecoderResult;
+use nihav_codec_support::codecs::MV;
+
+pub trait MTHelper {
+    fn wait_for_mb(&mut self, xpos: usize, ypos: usize, id: u32) -> DecoderResult<()>;
+    fn is_colo_mb_not_coded(&mut self, mb_addr: usize, id: u32) -> DecoderResult<bool>;
+    fn get_colo_mvs(&mut self, mb_addr: usize, id: u32) -> DecoderResult<[MV; 4]>;
+    fn update_pos(&mut self, mb_addr: usize);
+}
diff --git a/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs b/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs
new file mode 100644 (file)
index 0000000..3353275
--- /dev/null
@@ -0,0 +1,440 @@
+use nihav_core::frame::*;
+use nihav_codec_support::codecs::MV;
+use nihav_codec_support::codecs::blockdsp::*;
+use nihav_codec_support::codecs::h263::code::*;
+use nihav_codec_support::codecs::h263::data::H263_CHROMA_ROUND;
+use super::types::*;
+
+// IDCT based on C version of Walken IDCT from Xvid
+
+const ROW_ROUNDING: [i32; 8] = [ 65536, 3597, 2260, 1203, 0, 120, 512, 512 ];
+const ROW_TAB0: &[i32; 7] = &[ 22725, 21407, 19266, 16384, 12873,  8867, 4520 ];
+const ROW_TAB1: &[i32; 7] = &[ 31521, 29692, 26722, 22725, 17855, 12299, 6270 ];
+const ROW_TAB2: &[i32; 7] = &[ 29692, 27969, 25172, 21407, 16819, 11585, 5906 ];
+const ROW_TAB3: &[i32; 7] = &[ 26722, 25172, 22654, 19266, 15137, 10426, 5315 ];
+const ROW_TABS: [&[i32; 7]; 8] = [ ROW_TAB0, ROW_TAB1, ROW_TAB2, ROW_TAB3, ROW_TAB0, ROW_TAB1, ROW_TAB2, ROW_TAB3 ];
+
+const COL_C1: i32 =  13036;
+const COL_C2: i32 =  27146;
+const COL_C3: i32 = -21746;
+const CSQRT2: i32 =  23170;
+
+macro_rules! idct {
+    (row; $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr, $c6:expr, $c7:expr, $tab:expr, $round:expr) => {
+        let t00 = $tab[3] * i32::from($c0) + $round;
+        let t01 = $tab[3] * i32::from($c4);
+        let t02 = $tab[1] * i32::from($c2) + $tab[5] * i32::from($c6);
+        let t03 = $tab[5] * i32::from($c2) - $tab[1] * i32::from($c6);
+
+        let t10 = t00 + t01 + t02;
+        let t11 = t00 - t01 - t03;
+        let t12 = t00 - t01 + t03;
+        let t13 = t00 + t01 - t02;
+
+        let t20 = $tab[0] * i32::from($c1) + $tab[2] * i32::from($c3) + $tab[4] * i32::from($c5) + $tab[6] * i32::from($c7);
+        let t21 = $tab[2] * i32::from($c1) - $tab[6] * i32::from($c3) - $tab[0] * i32::from($c5) - $tab[4] * i32::from($c7);
+        let t22 = $tab[4] * i32::from($c1) - $tab[0] * i32::from($c3) + $tab[6] * i32::from($c5) + $tab[2] * i32::from($c7);
+        let t23 = $tab[6] * i32::from($c1) - $tab[4] * i32::from($c3) + $tab[2] * i32::from($c5) - $tab[0] * i32::from($c7);
+
+        $c0 = ((t10 + t20) >> 11) as i16;
+        $c7 = ((t10 - t20) >> 11) as i16;
+        $c1 = ((t11 + t21) >> 11) as i16;
+        $c6 = ((t11 - t21) >> 11) as i16;
+        $c2 = ((t12 + t22) >> 11) as i16;
+        $c5 = ((t12 - t22) >> 11) as i16;
+        $c3 = ((t13 + t23) >> 11) as i16;
+        $c4 = ((t13 - t23) >> 11) as i16;
+    };
+    (col; $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr, $c6:expr, $c7:expr) => {
+        let t00 = (i32::from($c7) * COL_C1 >> 16) + i32::from($c1);
+        let t01 = (i32::from($c1) * COL_C1 >> 16) - i32::from($c7);
+        let t02 = (i32::from($c5) * COL_C3 >> 16) + i32::from($c3);
+        let t03 = (i32::from($c3) * COL_C3 >> 16) - i32::from($c5);
+
+        let t10 = t00 + t02;
+        let t11 = t00 - t02;
+        let t12 = t01 + t03;
+        let t13 = t01 - t03;
+        let t14 = (t11 + t12) * CSQRT2 >> 15;
+        let t15 = (t11 - t12) * CSQRT2 >> 15;
+
+        let t20 =  i32::from($c2) + (i32::from($c6) * COL_C2 >> 16);
+        let t21 = -i32::from($c6) + (i32::from($c2) * COL_C2 >> 16);
+        let t22 = i32::from($c0 + $c4);
+        let t23 = i32::from($c0 - $c4);
+
+        $c0 = ((t22 + t20 + t10) >> 6) as i16;
+        $c7 = ((t22 + t20 - t10) >> 6) as i16;
+
+        $c3 = ((t22 - t20 + t13) >> 6) as i16;
+        $c4 = ((t22 - t20 - t13) >> 6) as i16;
+
+        $c1 = ((t23 + t21 + t14) >> 6) as i16;
+        $c6 = ((t23 + t21 - t14) >> 6) as i16;
+
+        $c2 = ((t23 - t21 + t15) >> 6) as i16;
+        $c5 = ((t23 - t21 - t15) >> 6) as i16;
+    }
+}
+
+fn xvidish_idct(block: &mut [i16; 64]) {
+    for (row, (&tab, &rounding)) in block.chunks_exact_mut(8)
+            .zip(ROW_TABS.iter().zip(ROW_ROUNDING.iter())) {
+        idct!(row; row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], tab, rounding);
+    }
+    for col in 0..8 {
+        idct!(col; block[col], block[col + 8], block[col + 8 * 2], block[col + 8 * 3],
+              block[col + 8 * 4], block[col + 8 * 5], block[col + 8 * 6], block[col + 8 * 7]);
+    }
+}
+
+trait Clip8 {
+    fn clip8(self) -> u8;
+}
+
+impl Clip8 for i16 {
+    fn clip8(self) -> u8 { self.max(0).min(255) as u8 }
+}
+
+trait ClipCoef {
+    fn clip_coef(self) -> i16;
+}
+
+impl ClipCoef for i32 {
+    fn clip_coef(self) -> i16 { self.max(-2048).min(2047) as i16 }
+}
+
+pub fn get_dc_scales(quant: u8) -> [u8; 2] {
+    match quant {
+        1..=4 => [8; 2],
+        5..=8 => [quant * 2, (quant + 13) / 2],
+        9..=24 => [quant + 8, (quant + 13) / 2],
+        _ => [quant * 2 - 16, quant - 6],
+    }
+}
+
+fn idct_dc(block: &mut [i16; 64]) -> u8 { (block[0] / 8).clip8() }
+
+trait MVOps {
+    fn get_chroma_mv(&self) -> MV;
+    fn get_chroma_4mv(&self) -> MV;
+}
+
+impl MVOps for MV {
+    fn get_chroma_mv(&self) -> MV {
+        const CHROMA_ROUND: [i16; 4] = [0, 1, 0, 0];
+        MV{ x: (self.x >> 1) + CHROMA_ROUND[(self.x & 3) as usize],
+            y: (self.y >> 1) + CHROMA_ROUND[(self.y & 3) as usize] }
+    }
+    fn get_chroma_4mv(&self) -> MV {
+        MV{ x: (self.x >> 3) + H263_CHROMA_ROUND[(self.x & 0xF) as usize],
+            y: (self.y >> 3) + H263_CHROMA_ROUND[(self.y & 0xF) as usize] }
+    }
+}
+
+pub struct DSP {
+    idct:           fn(block: &mut [i16; 64]),
+    mc_funcs:       &'static [BlkInterpFunc],
+    avg_mc_funcs:   &'static [BlkInterpFunc],
+}
+
+impl DSP {
+    pub fn new() -> Self {
+        DSP {
+            idct:           h263_idct,
+            mc_funcs:       ASP_INTERP_FUNCS_NR,
+            avg_mc_funcs:   ASP_INTERP_AVG_FUNCS_NR,
+        }
+    }
+    pub fn use_xvid_idct(&mut self) {
+        self.idct = xvidish_idct;
+    }
+    pub fn set_rounding(&mut self, rounding: bool) {
+        if !rounding {
+            self.mc_funcs       = ASP_INTERP_FUNCS_NR;
+            self.avg_mc_funcs   = ASP_INTERP_AVG_FUNCS_NR;
+        } else {
+            self.mc_funcs       = H263_INTERP_FUNCS;
+            self.avg_mc_funcs   = H263_INTERP_AVG_FUNCS;
+        }
+    }
+    pub fn put_mb(&self, frm: &mut NASimpleVideoFrame<u8>, mb: &mut Macroblock, mb_x: usize, mb_y: usize) {
+        let mut cbp = mb.cbp;
+        let y_offset = frm.offset[0] + mb_x * 16 + mb_y * 16 * frm.stride[0];
+        let y_stride = frm.stride[0];
+        if mb.ac_pred { // to be safe
+            cbp = 0x3F;
+        }
+        for (n, y_blk) in mb.coeffs[..4].iter_mut().enumerate() {
+            let dst = &mut frm.data[y_offset + (n & 1) * 8 + (n / 2) * 8 * y_stride..];
+            if (cbp & 0x20) != 0 {
+                (self.idct)(y_blk);
+                for (drow, srow) in dst.chunks_mut(y_stride).zip(y_blk.chunks_exact(8)) {
+                    for (dst, &src) in drow.iter_mut().zip(srow.iter()) {
+                        *dst = src.clip8();
+                    }
+                }
+            } else {
+                let dc = idct_dc(y_blk);
+                for row in dst.chunks_mut(y_stride).take(8) {
+                    for el in row[..8].iter_mut() {
+                        *el = dc;
+                    }
+                }
+            }
+            cbp <<= 1;
+        }
+
+        for (plane, c_blk) in mb.coeffs[4..].iter_mut().enumerate() {
+            let c_stride = frm.stride[plane + 1];
+            let dst = &mut frm.data[frm.offset[plane + 1] + mb_x * 8 + mb_y * 8 * c_stride..];
+            if (cbp & 0x20) != 0 {
+                (self.idct)(c_blk);
+                for (drow, srow) in dst.chunks_mut(c_stride).zip(c_blk.chunks_exact(8)) {
+                    for (dst, &src) in drow.iter_mut().zip(srow.iter()) {
+                        *dst = src.clip8();
+                    }
+                }
+            } else {
+                let dc = idct_dc(c_blk);
+                for row in dst.chunks_mut(c_stride).take(8) {
+                    for el in row[..8].iter_mut() {
+                        *el = dc;
+                    }
+                }
+            }
+            cbp <<= 1;
+        }
+    }
+
+    pub fn add_coeffs(&self, frm: &mut NASimpleVideoFrame<u8>, mb: &mut Macroblock, mb_x: usize, mb_y: usize) {
+        let mut cbp = mb.cbp;
+        for (n, block) in mb.coeffs.iter_mut().enumerate() {
+            if (cbp & 0x20) != 0 {
+                (self.idct)(block);
+                let (plane, dx, dy) = if n < 4 {
+                        (0, mb_x * 16 + (n & 1) * 8, mb_y * 16 + (n & 2) * 4)
+                    } else {
+                        (n - 3, mb_x * 8, mb_y * 8)
+                    };
+                let stride = frm.stride[plane];
+                let off = frm.offset[plane] + dx + dy * stride;
+                for (drow, brow) in frm.data[off..].chunks_mut(stride).zip(block.chunks_exact(8)) {
+                    for (coef, &diff) in drow.iter_mut().zip(brow.iter()) {
+                        *coef = (i16::from(*coef) + diff).clip8();
+                    }
+                }
+            }
+            cbp <<= 1;
+        }
+    }
+
+    pub fn mb_mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mv: MV) {
+        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);
+        let cmv = mv.get_chroma_mv();
+        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);
+        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);
+    }
+
+    pub fn avg_mb_mv(&self, frm: &mut NASimpleVideoFrame<u8>, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef<u8>, mv: MV) {
+        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);
+        let cmv = mv.get_chroma_mv();
+        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, mv.y >> 1, 8, 8, 0, 1, cmode, self.avg_mc_funcs);
+        copy_block(frm, pframe,         2, mb_x * 8, mb_y * 8, cmv.x >> 1, mv.y >> 1, 8, 8, 0, 1, cmode, self.avg_mc_funcs);
+    }
+
+    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 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);
+        }
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        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);
+        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);
+    }
+
+    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 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);
+        }
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        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, cmv.y, 8, 8, 0, 1, cmode, self.avg_mc_funcs);
+        copy_block(frm, pframe,         2, mb_x * 8, mb_y * 8, cmv.x, cmv.y, 8, 8, 0, 1, cmode, self.avg_mc_funcs);
+    }
+}
+
+pub fn dequant_intra_block(block: &mut [i16; 64], ac_scale: u8, qinfo: &QuantInfo) {
+    if qinfo.quant_type {
+        for (coef, &weight) in block.iter_mut().zip(qinfo.intra_mat.iter()).skip(1) {
+            *coef = (i32::from(*coef * 2) * i32::from(ac_scale) * i32::from(weight) / 16).clip_coef();
+        }
+    } else {
+        let scale = i32::from(ac_scale);
+        let bbias = if (ac_scale & 1) == 0 { 1 } else { 0 };
+        for coef in block.iter_mut().skip(1) {
+            if *coef != 0 {
+                let mut c0 = i32::from(*coef * 2);
+                let bias;
+                if c0 > 0 {
+                    c0 += 1;
+                    bias = -bbias;
+                } else {
+                    c0 -= 1;
+                    bias = bbias;
+                }
+                *coef = (c0 * scale + bias).clip_coef();
+            }
+        }
+    }
+}
+
+pub fn dequant_inter(mb: &mut Macroblock, qinfo: &QuantInfo) {
+    let ac_scale = mb.quant;
+    let mut cbp = mb.cbp;
+    if qinfo.quant_type {
+        for block in mb.coeffs.iter_mut() {
+            if (cbp & 0x20) != 0 {
+                for (coef, &weight) in block.iter_mut().zip(qinfo.inter_mat.iter()) {
+                    if *coef != 0 {
+                        let mut c0 = i32::from(*coef * 2);
+                        if c0 > 0 { c0 += 1; }
+                        else      { c0 -= 1; }
+                        *coef = (c0 * i32::from(ac_scale) * i32::from(weight) / 16).clip_coef();
+                    }
+                }
+            }
+            cbp <<= 1;
+        }
+    } else {
+        let scale = i32::from(ac_scale);
+        let bbias = if (ac_scale & 1) == 0 { 1 } else { 0 };
+        for block in mb.coeffs.iter_mut() {
+            if (cbp & 0x20) != 0 {
+                for coef in block.iter_mut() {
+                    if *coef != 0 {
+                        let mut c0 = i32::from(*coef * 2);
+                        let bias;
+                        if c0 > 0 {
+                            c0 += 1;
+                            bias = -bbias;
+                        } else {
+                            c0 -= 1;
+                            bias = bbias;
+                        }
+                        *coef = (c0 * scale + bias).clip_coef();
+                    }
+                }
+            }
+            cbp <<= 1;
+        }
+    }
+}
+
+fn asp_interp00nr(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    for (drow, srow) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(bh) {
+        drow[..bw].copy_from_slice(&srow[..bw]);
+    }
+}
+
+fn asp_interp01nr(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    for (drow, srow) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(bh) {
+        for (dst, src) in drow.iter_mut().zip(srow.windows(2)).take(bw) {
+            *dst = ((u16::from(src[0]) + u16::from(src[1])) >> 1) as u8;
+        }
+    }
+}
+
+fn asp_interp10nr(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    for (drow, (srow0, srow1)) in dst.chunks_mut(dstride)
+            .zip(src.chunks(sstride).zip(src[sstride..].chunks(sstride))).take(bh) {
+        for (dst, (&src0, &src1)) in drow.iter_mut().zip(srow0.iter().zip(srow1.iter())).take(bw) {
+            *dst = ((u16::from(src0) + u16::from(src1)) >> 1) as u8;
+        }
+    }
+}
+
+fn asp_interp11nr(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    for (drow, (srow0, srow1)) in dst.chunks_mut(dstride)
+            .zip(src.chunks(sstride).zip(src[sstride..].chunks(sstride))).take(bh) {
+        for (dst, (src0, src1)) in drow.iter_mut()
+                .zip(srow0.windows(2).zip(srow1.windows(2))).take(bw) {
+            *dst = ((u16::from(src0[0]) + u16::from(src0[1]) + u16::from(src1[0]) + u16::from(src1[1])) >> 2) as u8;
+        }
+    }
+}
+
+pub const ASP_INTERP_FUNCS_NR: &[BlkInterpFunc] = &[
+        asp_interp00nr, asp_interp01nr, asp_interp10nr, asp_interp11nr ];
+
+fn asp_interp00nr_avg(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    for (dline, sline) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(bh) {
+        for (dst, &src) in dline.iter_mut().zip(sline.iter()).take(bw) {
+            *dst = ((u16::from(*dst) + u16::from(src) + 1) >> 1) as u8;
+        }
+    }
+}
+
+fn asp_interp01nr_avg(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw {
+            let a = dst[didx + x] as u16;
+            let b = ((src[sidx + x] as u16) + (src[sidx + x + 1] as u16)) >> 1;
+            dst[didx + x] = ((a + b + 1) >> 1) as u8;
+        }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+fn asp_interp10nr_avg(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw {
+            let a = dst[didx + x] as u16;
+            let b = ((src[sidx + x] as u16) + (src[sidx + x + sstride] as u16)) >> 1;
+            dst[didx + x] = ((a + b + 1) >> 1) as u8;
+        }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+fn asp_interp11nr_avg(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw {
+            let a = dst[didx + x] as u16;
+            let b = ((src[sidx + x] as u16) +
+                     (src[sidx + x + 1] as u16) +
+                     (src[sidx + x + sstride] as u16) +
+                     (src[sidx + x + sstride + 1] as u16)) >> 2;
+            dst[didx + x] = ((a + b + 1) >> 1) as u8;
+        }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+pub const ASP_INTERP_AVG_FUNCS_NR: &[BlkInterpFunc] = &[
+        asp_interp00nr_avg, asp_interp01nr_avg, asp_interp10nr_avg, asp_interp11nr_avg ];
diff --git a/nihav-mpeg/src/codecs/mpeg4asp/mod.rs b/nihav-mpeg/src/codecs/mpeg4asp/mod.rs
new file mode 100644 (file)
index 0000000..d4fd23e
--- /dev/null
@@ -0,0 +1,422 @@
+use nihav_core::codecs::*;
+use nihav_codec_support::codecs::{IPBShuffler, MV};
+
+mod bitstream;
+use bitstream::*;
+mod decoder;
+use decoder::*;
+mod dispatch;
+use dispatch::*;
+mod dsp;
+mod types;
+use types::*;
+
+struct SingleThreadHelper<'a> {
+    last_ref_info:  &'a PicState,
+}
+
+impl<'a> MTHelper for SingleThreadHelper<'a> {
+    fn wait_for_mb(&mut self, _xpos: usize, _ypos: usize, _id: u32) -> DecoderResult<()> { Ok(()) }
+    fn is_colo_mb_not_coded(&mut self, mb_addr: usize, _id: u32) -> DecoderResult<bool> {
+        Ok(self.last_ref_info.mb_type[mb_addr] == MBType::Skip)
+    }
+    fn get_colo_mvs(&mut self, mb_addr: usize, _id: u32) -> DecoderResult<[MV; 4]> {
+        Ok(self.last_ref_info.mv[mb_addr])
+    }
+    fn update_pos(&mut self, _mb_addr: usize) {}
+}
+
+#[derive(Default,Debug,PartialEq)]
+enum AssignMode {
+    #[default]
+    Undefined,
+    Yes,
+    No,
+}
+
+struct ASPDecoder {
+    info:           NACodecInfoRef,
+    skip_mode:      FrameSkipMode,
+    ipbs:           IPBShuffler,
+    seq:            SequenceContext,
+    fctx:           FrameContext,
+    saved:          Option<(FrameType, u64, NABufferType)>,
+    last_ref_info:  PicState,
+    pb_mode:        bool,
+    assign_mode:    AssignMode,
+}
+
+impl ASPDecoder {
+    fn new() -> Self {
+        ASPDecoder{
+            info:           NACodecInfoRef::default(),
+            skip_mode:      FrameSkipMode::default(),
+            ipbs:           IPBShuffler::new(),
+            seq:            SequenceContext::new(),
+            fctx:           FrameContext::new(),
+            saved:          None,
+            last_ref_info:  PicState::new(0, 0, 0),
+            pb_mode:        false,
+            assign_mode:    AssignMode::default(),
+        }
+    }
+}
+
+impl NADecoder for ASPDecoder {
+    fn init(&mut self, supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT));
+            self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+
+            let mut parsed_vol = false;
+            if let Some(edata) = info.get_extradata() {
+                // ES descriptor, usually happens in MOV
+                if edata.len() > 16 && &edata[..4] == b"esds" {
+                    let (vol, is_xvid) = parse_es_descriptor(&edata[8..])?;
+                    if vol.quarter_sample {
+                        println!("quarterpel is not supported!");
+                        return Err(DecoderError::NotImplemented);
+                    }
+                    if is_xvid {
+                        self.fctx.use_xvid_idct();
+                    }
+                    self.seq.set_vol(vol);
+                    parsed_vol = true;
+                }
+            }
+            if !parsed_vol {
+                self.seq.set_dimensions(vinfo.get_width(), vinfo.get_height());
+            }
+
+            let (width, height) = self.seq.get_dimensions();
+
+            supp.pool_u8.set_dec_bufs(4);
+            supp.pool_u8.prealloc_video(NAVideoInfo::new(width, height, false, YUV420_FORMAT), 4)?;
+
+            let mb_w = (width + 15) / 16;
+            self.fctx.resize(mb_w);
+
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+
+        let offs = scan_start_codes(&src)?;
+
+        let mut dst_frm = None;
+        for range in offs.windows(2) {
+            let obj_src = &src[range[0] + 3..range[1]];
+            match obj_src[0] {
+                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.width != 0 {
+                        if self.assign_mode == AssignMode::Undefined && pkt.get_dts().is_none() && !vol.low_delay {
+                            let tinfo = pkt.get_time_information();
+                            if tinfo.tb_num == 1 && tinfo.tb_den == vol.time_res {
+                                self.assign_mode = AssignMode::Yes;
+                            } else {
+                                println!("Warning: container reported timebase is {}/{} while ASP stream timebase is 1/{} - PTS correction won't be possible!", tinfo.tb_num, tinfo.tb_den, vol.time_res);
+                                self.assign_mode = AssignMode::No;
+                            }
+                        }
+                        let tmp_vinfo = NAVideoInfo::new(vol.width, vol.height, false, YUV420_FORMAT);
+                        if supp.pool_u8.get_info() != Some(tmp_vinfo) {
+                            supp.pool_u8.reset();
+                            supp.pool_u8.prealloc_video(tmp_vinfo, 4)?;
+                            self.ipbs.clear();
+                            let mb_w = (vol.width + 15) / 16;
+                            self.fctx.resize(mb_w);
+                            self.seq.set_dimensions(vol.width, vol.height);
+                        }
+                    }
+                    self.seq.set_vol(vol);
+                },
+                0x30..=0xAF | 0xB7..=0xB9 | 0xC4..=0xC5 => {}, // reserved, system start codes
+                0xB0 => { // visual_object_sequence_start_code
+                    validate!(obj_src.len() >= 2);
+                    // single byte is profile and level
+                },
+                0xB1 => {}, // visual_object_sequence_end_code
+                0xB2 => { // user_data_start_code
+                    if check_xvid_user_data(obj_src) {
+                        self.fctx.use_xvid_idct();
+                    }
+                },
+                0xB3 => { // group_of_vop_start_code
+                    validate!(obj_src.len() == 4);
+                    // time_code   - 18 bits
+                    // closed_gov  - 1 bit
+                    // broken_link - 1 bit
+                    // stuffing    - 4 bits (0111)
+                },
+                0xB4 => return Err(DecoderError::NotImplemented), // video_session_error_code
+                0xB5 => {}, // visual_object_start_code
+                0xB6 => { // vop_start_code
+                    validate!(dst_frm.is_none() || self.saved.is_none());
+                    let ret = supp.pool_u8.get_free();
+                    if ret.is_none() {
+                        return Err(DecoderError::AllocError);
+                    }
+                    let mut buf = ret.unwrap();
+                    let frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap();
+
+                    let pic_info = self.fctx.decode_frame_header(obj_src, &self.seq)?;
+
+                    let ftype = pic_info.pic_type;
+                    let (cur_ts, fwd_ts, bwd_ts) = if ftype != FrameType::Skip || !self.pb_mode {
+                            self.seq.update_ts(&pic_info)
+                        } else { (0, 0, 0) };
+
+                    if ftype == FrameType::Skip {
+                        let mut saved = None;
+                        std::mem::swap(&mut saved, &mut self.saved);
+                        let (ftype, sv_ts, buf) = saved.unwrap_or((FrameType::Skip, cur_ts, NABufferType::None));
+                        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buf);
+                        frm.set_keyframe(ftype == FrameType::I);
+                        if self.assign_mode == AssignMode::Yes {
+                            frm.set_dts(frm.get_pts());
+                            frm.set_pts(Some(sv_ts));
+                        } else if frm.get_pts().is_none() {
+                            frm.set_pts(Some(sv_ts));
+                        }
+                        frm.set_id(sv_ts as i64);
+                        frm.set_frame_type(ftype);
+                        return Ok(frm.into_ref());
+                    }
+
+                    match (self.skip_mode, pic_info.pic_type) {
+                        (FrameSkipMode::IntraOnly, FrameType::P) |
+                        (FrameSkipMode::IntraOnly, FrameType::B) |
+                        (FrameSkipMode::KeyframesOnly, FrameType::B) |
+                        (_, FrameType::Other) => {
+                            let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::None);
+                            frm.set_keyframe(false);
+                            if self.assign_mode == AssignMode::Yes {
+                                frm.set_dts(frm.get_pts());
+                                frm.set_pts(Some(cur_ts));
+                            } else if frm.get_pts().is_none() {
+                                frm.set_pts(Some(cur_ts));
+                            }
+                            frm.set_id(cur_ts as i64);
+                            frm.set_frame_type(FrameType::Skip);
+                            return Ok(frm.into_ref());
+                        },
+                        _ => {},
+                    }
+
+                    let (fwd_ref, bwd_ref) = match ftype {
+                            FrameType::P => {
+                                let prev_frm = self.ipbs.get_lastref();
+                                if prev_frm.is_none() {
+                                    return Err(DecoderError::MissingReference);
+                                }
+                                (prev_frm, None)
+                            },
+                            FrameType::B => {
+                                let fref = self.ipbs.get_b_fwdref();
+                                let bref = self.ipbs.get_b_bwdref();
+                                if fref.is_none() || bref.is_none() {
+                                    return Err(DecoderError::MissingReference);
+                                }
+                                (fref, bref)
+                            },
+                            _ => (None, None),
+                        };
+                    self.fctx.set_refs(cur_ts, fwd_ref, fwd_ts, bwd_ref, bwd_ts);
+                    let (mb_w, mb_h) = self.fctx.get_dimensions();
+                    let mut pstate = PicState::new(cur_ts as u32, mb_w, mb_h);
+                    let mut mt_helper = SingleThreadHelper { last_ref_info: &self.last_ref_info };
+                    self.fctx.decode_frame(obj_src, frm, &self.seq, &pic_info, &mut pstate, &mut mt_helper)?;
+
+                    if ftype == FrameType::I || ftype == FrameType::P {
+                        self.last_ref_info = pstate;
+                        self.ipbs.add_frame(buf.clone());
+                    }
+
+                    if dst_frm.is_none() {
+                        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf));
+                        frm.set_keyframe(ftype == FrameType::I);
+                        if self.assign_mode == AssignMode::Yes {
+                            frm.set_dts(frm.get_pts());
+                            frm.set_pts(Some(cur_ts));
+                        } else if frm.get_pts().is_none() {
+                            frm.set_pts(Some(cur_ts));
+                        }
+                        frm.set_id(cur_ts as i64);
+                        frm.set_frame_type(ftype);
+                        dst_frm = Some(frm.into_ref());
+                    } else {
+                        validate!(ftype == FrameType::B);
+                        let frm = dst_frm.unwrap();
+
+                        if !self.pb_mode {
+                            println!("PB-mode");
+                        }
+                        self.pb_mode = true;
+                        self.saved = Some((frm.get_frame_type(), frm.get_pts().unwrap_or(0), frm.get_buffer()));
+
+                        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf));
+                        frm.set_keyframe(false);
+                        if self.assign_mode == AssignMode::Yes {
+                            frm.set_dts(frm.get_pts());
+                            frm.set_pts(Some(cur_ts));
+                        } else if frm.get_pts().is_none() {
+                            frm.set_pts(Some(cur_ts));
+                        }
+                        frm.set_id(cur_ts as i64);
+                        frm.set_frame_type(FrameType::P);
+                        dst_frm = Some(frm.into_ref());
+                    }
+                },
+                0xBA..=0xC2 => return Err(DecoderError::NotImplemented), // FBA, mesh, texture data
+                0xC3 => {}, // stuffing_start_code
+                0xC6..=0xFF => {}, // system start codes
+            }
+        }
+        validate!(dst_frm.is_some());
+        Ok(dst_frm.unwrap())
+    }
+    fn flush(&mut self) {
+        self.ipbs.clear();
+    }
+}
+
+const NO_REORDER_OPTION: &str = "noreorder";
+
+const DECODER_OPTIONS: &[NAOptionDefinition] = &[
+    NAOptionDefinition {
+        name: FRAME_SKIP_OPTION, description: FRAME_SKIP_OPTION_DESC,
+        opt_type: NAOptionDefinitionType::String(Some(&[
+                FRAME_SKIP_OPTION_VAL_NONE,
+                FRAME_SKIP_OPTION_VAL_KEYFRAME,
+                FRAME_SKIP_OPTION_VAL_INTRA
+            ])) },
+    NAOptionDefinition {
+        name: NO_REORDER_OPTION, description: "Do not attempt to reorder frames",
+        opt_type: NAOptionDefinitionType::None },
+];
+
+impl NAOptionHandler for ASPDecoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { DECODER_OPTIONS }
+    fn set_options(&mut self, options: &[NAOption]) {
+        for option in options.iter() {
+            for opt_def in DECODER_OPTIONS.iter() {
+                if opt_def.check(option).is_ok() {
+                    match (option.name, &option.value) {
+                        (FRAME_SKIP_OPTION, NAValue::String(ref strval)) => {
+                            if let Ok(smode) = FrameSkipMode::from_str(strval) {
+                                self.skip_mode = smode;
+                            }
+                        },
+                        (NO_REORDER_OPTION, _) => {
+                            self.assign_mode = AssignMode::No;
+                        },
+                        _ => {},
+                    }
+                }
+            }
+        }
+    }
+    fn query_option_value(&self, name: &str) -> Option<NAValue> {
+        match name {
+            FRAME_SKIP_OPTION => Some(NAValue::String(self.skip_mode.to_string())),
+            _ => None,
+        }
+    }
+}
+
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(ASPDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::*;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::*;
+    use nihav_commonfmt::generic_register_all_demuxers;
+
+    #[test]
+    fn test_mpeg4asp_basic() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        mpeg_register_all_decoders(&mut dec_reg);
+
+        // https://samples.mplayerhq.hu/V-codecs/MPEG4/WobblyWindowsIntro.avi
+        test_decoding("avi", "mpeg4asp",
+                      "assets/MPEG/asp/WobblyWindowsIntro.avi",
+                      Some(8), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                        [0x01264d54, 0xfb462b03, 0x34dd4dab, 0x8d976230],
+                        [0x03be8499, 0x2234824e, 0x4b13d013, 0x635a8ca9],
+                        [0x11e9bccc, 0x78370850, 0x80a836e8, 0x8eede59b],
+                        [0x0c22b418, 0xf3c29564, 0x33474027, 0xa0e41461],
+                        [0x6d1c5064, 0x1c8c863e, 0x2ea9bd7c, 0xefbe5791],
+                        [0xece7e446, 0x5f096da0, 0xb6c91e31, 0x6a4ef9f3],
+                        [0x25450ef1, 0xa5594f36, 0x8df05845, 0x6e1f1fb2],
+                        [0x080ac91b, 0x26d417e0, 0x2137543e, 0xf93584cb],
+                        [0x2d0476a7, 0x291ac9bc, 0xc7147ab8, 0xaf8e3b24]]));
+    }
+    #[test]
+    fn test_mpeg4asp_mov() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        mpeg_register_all_decoders(&mut dec_reg);
+
+        // https://samples.mplayerhq.hu/V-codecs/MPEG4/problem.mov
+        test_decoding("mov", "mpeg4asp",
+                      "assets/MPEG/asp/problem.mov",
+                      Some(256), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                        [0xb1ac5e3c, 0xef74b180, 0xcb5ed0dd, 0x60bdaf30],
+                        [0xaff6d592, 0x9d450833, 0xf90047b1, 0xf4aa7fb6],
+                        [0xb8d70078, 0x649464dc, 0xe237186b, 0x8dd3821b],
+                        [0x972bb38a, 0xcfea0fbd, 0xe4fcdf29, 0xbbfba841]]));
+    }
+    #[test]
+    fn test_mpeg4asp_bframes() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        mpeg_register_all_decoders(&mut dec_reg);
+
+        // https://samples.mplayerhq.hu/V-codecs/DX50-DivX5/test.b-frames.divx5.avi
+        test_decoding("avi", "mpeg4asp",
+                      "assets/MPEG/asp/test.b-frames.divx5.avi",
+                      Some(2), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                        [0x2c73036d, 0xa9c3e655, 0x50d82010, 0x51a5c928],
+                        [0xa2cf38f3, 0xa5c77472, 0x3962107d, 0x92acc355],
+                        [0xa2cf38f3, 0xa5c77472, 0x3962107d, 0x92acc355]]));
+    }
+    #[test]
+    fn test_mpeg4asp_packed_b_frames() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        mpeg_register_all_decoders(&mut dec_reg);
+
+        // https://samples.mplayerhq.hu/V-codecs/MPEG4/vtm_video.avi
+        test_decoding("avi", "mpeg4asp",
+                      "assets/MPEG/asp/vtm_video.avi",
+                      Some(8), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                        [0x536b8b3f, 0x6b33df1f, 0x3253605f, 0x92b6c801],
+                        [0xb047efb5, 0x7ca6e7c7, 0x1e0cbe99, 0x2209ac0c],
+                        [0x919383bb, 0x19269661, 0x5065b9dc, 0xd11ac7b7],
+                        [0x430cb217, 0x79517417, 0x7d469406, 0x88a25eee],
+                        [0x22ed09ff, 0x0fcc87b5, 0x2938cd02, 0x533b406f],
+                        [0xedaf2d73, 0xb04d9b36, 0xcaa819dc, 0x423bd7b7],
+                        [0x85aece48, 0x5b3cb0a9, 0x6e6f7231, 0xbdcf2c87],
+                        [0x18073137, 0x7f3517ff, 0xac0b4565, 0xf11cfe68],
+                        [0x72e254b1, 0xe7ad8ccd, 0x3b67a04d, 0xe37853af]]));
+    }
+}
diff --git a/nihav-mpeg/src/codecs/mpeg4asp/types.rs b/nihav-mpeg/src/codecs/mpeg4asp/types.rs
new file mode 100644 (file)
index 0000000..73164da
--- /dev/null
@@ -0,0 +1,429 @@
+use nihav_core::frame::*;
+use nihav_codec_support::codecs::{MV, ZERO_MV};
+use nihav_codec_support::data::GenericCache;
+
+pub trait FrameMV {
+    fn add_pred(pred: MV, diff: MV, fcode: u8) -> MV;
+    fn scale(&self, trb: i32, trd: i32) -> MV;
+    fn b_sub(pvec: MV, fwdvec: MV, bvec: MV, trb: i32, trd: i32) -> MV;
+}
+
+impl FrameMV for MV {
+    fn add_pred(pred: MV, diff: MV, fcode: u8) -> MV {
+        let shift = fcode.saturating_sub(1);
+        let high =  (1 << (shift + 5)) - 1;
+        let low  = -(1 << (shift + 5));
+        let range = 1 << (shift + 6);
+
+        let mut mv = pred + diff;
+        if mv.x < low {
+            mv.x += range;
+        }
+        if mv.x > high {
+            mv.x -= range;
+        }
+        if mv.y < low {
+            mv.y += range;
+        }
+        if mv.y > high {
+            mv.y -= range;
+        }
+        mv
+    }
+    fn scale(&self, trb: i32, trd: i32) -> MV {
+        if (trd == 0) || (trb == 0) {
+            ZERO_MV
+        } else {
+            MV { x: (((self.x as i32) * trb) / trd) as i16, y: (((self.y as i32) * trb) / trd) as i16 }
+        }
+    }
+    fn b_sub(pvec: MV, fwdvec: MV, bvec: MV, trb: i32, trd: i32) -> MV {
+        let bscale = trb  - trd;
+        let x = if bvec.x != 0 { fwdvec.x - pvec.x } else if trd != 0 { (bscale * (pvec.x as i32) / trd) as i16 } else { 0 };
+        let y = if bvec.y != 0 { fwdvec.y - pvec.y } else if trd != 0 { (bscale * (pvec.y as i32) / trd) as i16 } else { 0 };
+        MV { x, y }
+    }
+}
+
+#[derive(Copy,Clone,Debug,PartialEq)]
+pub enum Shape {
+    Rect,
+    Binary,
+    BinaryOnly,
+    Grayscale
+}
+
+#[derive(Default,Copy,Clone,Debug,PartialEq)]
+pub enum Sprite {
+    #[default]
+    NotUsed,
+    Static,
+    GMC,
+}
+
+#[derive(Default,Clone,Copy)]
+pub struct SpriteInfo {
+    pub sprite:             Sprite,
+    pub num_warp_points:    usize,
+    pub warping_accuracy:   u8,
+    pub brightness_change:  bool,
+    pub low_latency:        bool,
+}
+
+#[derive(Clone)]
+pub struct QuantInfo {
+    pub quant_type:         bool,
+    pub intra_mat:          [u8; 64],
+    pub inter_mat:          [u8; 64],
+}
+
+impl Default for QuantInfo {
+    fn default() -> Self {
+        Self {
+            quant_type: false,
+            intra_mat:  DEF_INTRA_MAT,
+            inter_mat:  DEF_INTER_MAT,
+        }
+    }
+}
+
+pub struct VideoObjectLayer {
+    pub vol_type:               u8,
+    pub version:                u8,
+    pub low_delay:              bool,
+    pub shape:                  Shape,
+    pub time_res:               u32,
+    pub fixed_rate:             Option<u32>,
+    pub width:                  usize,
+    pub height:                 usize,
+    pub obmc_disable:           bool,
+    pub sprite_info:            SpriteInfo,
+    pub quant_info:             QuantInfo,
+    pub quarter_sample:         bool,
+    pub complexity_estimation:  bool,
+    pub resync_marker_disable:  bool,
+    pub newpred_enable:         bool,
+}
+
+pub struct PicInfo {
+    pub pic_type:               FrameType,
+    pub vop_time_increment:     u32,
+    pub modulo_time_base:       u32,
+    pub fcode_f:                u8,
+    pub fcode_b:                u8,
+    pub rounding:               bool,
+}
+
+#[derive(Clone,Copy,Debug,Default,PartialEq)]
+pub enum MBType {
+    #[default]
+    Intra,
+    Skip,
+    Inter,
+    Inter4MV,
+    Direct,
+    Interpolate,
+    Backward,
+    Forward,
+}
+
+pub struct Macroblock {
+    pub mb_type:                MBType,
+    pub quant:                  u8,
+    pub mvs:                    [MV; 4],
+    pub cbp:                    u8,
+    pub ac_pred:                bool,
+    pub coeffs:                 [[i16; 64]; 6],
+}
+
+impl Macroblock {
+    pub fn new() -> Self {
+        Self {
+            mb_type:    MBType::Intra,
+            quant:      0,
+            mvs:        [ZERO_MV; 4],
+            cbp:        0,
+            ac_pred:    false,
+            coeffs:     [[0; 64]; 6],
+        }
+    }
+}
+
+pub struct PicState {
+    pub timestamp:      u32,
+    pub mb_type:        Vec<MBType>,
+    pub mv:             Vec<[MV; 4]>,
+    pub mb_w:           usize,
+    pub mb_h:           usize,
+}
+
+impl PicState {
+    pub fn new(timestamp: u32, mb_w: usize, mb_h: usize) -> Self {
+        Self {
+            timestamp,
+            mb_w, mb_h,
+            mb_type:    vec![MBType::Skip; mb_w * mb_h],
+            mv:         vec![[ZERO_MV; 4]; mb_w * mb_h],
+        }
+    }
+}
+
+const DEF_INTRA_MAT: [u8; 64] = [
+     8, 17, 18, 19, 21, 23, 25, 27,
+    17, 18, 19, 21, 23, 25, 27, 28,
+    20, 21, 22, 23, 24, 26, 28, 30,
+    21, 22, 23, 24, 26, 28, 30, 32,
+    22, 23, 24, 26, 38, 30, 32, 35,
+    23, 24, 26, 38, 30, 32, 35, 38,
+    25, 26, 28, 30, 32, 35, 38, 41,
+    27, 28, 30, 32, 35, 38, 41, 45
+];
+
+const DEF_INTER_MAT: [u8; 64] = [
+    16, 17, 18, 19, 20, 21, 22, 23,
+    17, 18, 19, 20, 21, 22, 23, 24,
+    18, 19, 20, 21, 22, 23, 24, 25,
+    19, 20, 21, 22, 23, 24, 26, 27,
+    20, 21, 22, 23, 25, 26, 27, 28,
+    21, 22, 23, 24, 26, 27, 28, 30,
+    22, 23, 24, 26, 27, 28, 30, 31,
+    23, 24, 25, 27, 28, 30, 31, 33
+];
+
+#[derive(Default)]
+pub struct NState {
+    pub has_l:  bool,
+    pub has_t:  bool,
+    pub has_tr: bool,
+}
+
+pub type SavedAC = [[i16; 7]; 2];
+
+pub struct PredState {
+    pub y_dc:           GenericCache<i16>,
+    pub y_ac:           GenericCache<SavedAC>,
+    pub u_dc:           GenericCache<i16>,
+    pub u_ac:           GenericCache<SavedAC>,
+    pub v_dc:           GenericCache<i16>,
+    pub v_ac:           GenericCache<SavedAC>,
+    pub qps:            GenericCache<u8>,
+    pub mv:             GenericCache<MV>,
+    pub fcode_f:        u8,
+    pub fcode_b:        u8,
+    pub pred_mv_f:      MV,
+    pub pred_mv_b:      MV,
+}
+
+impl PredState {
+    pub fn new() -> Self {
+        Self {
+            y_dc:       GenericCache::new(0, 0, 0),
+            y_ac:       GenericCache::new(0, 0, SavedAC::default()),
+            u_dc:       GenericCache::new(0, 0, 0),
+            u_ac:       GenericCache::new(0, 0, SavedAC::default()),
+            v_dc:       GenericCache::new(0, 0, 0),
+            v_ac:       GenericCache::new(0, 0, SavedAC::default()),
+            qps:        GenericCache::new(0, 0, 0),
+            mv:         GenericCache::new(0, 0, ZERO_MV),
+            fcode_f:    0,
+            fcode_b:    0,
+            pred_mv_f:  ZERO_MV,
+            pred_mv_b:  ZERO_MV,
+        }
+    }
+    pub fn set_fcodes(&mut self, fcode_f: u8, fcode_b: u8) {
+        self.fcode_f = fcode_f;
+        self.fcode_b = fcode_b;
+    }
+    pub fn resize(&mut self, mb_w: usize) {
+        self.y_dc = GenericCache::new(2, mb_w * 2 + 1, 1 << (8 + 2));
+        self.y_ac = GenericCache::new(2, mb_w * 2 + 1, SavedAC::default());
+        self.u_dc = GenericCache::new(1, mb_w + 1, 1 << (8 + 2));
+        self.u_ac = GenericCache::new(1, mb_w + 1, SavedAC::default());
+        self.v_dc = GenericCache::new(1, mb_w + 1, 1 << (8 + 2));
+        self.v_ac = GenericCache::new(1, mb_w + 1, SavedAC::default());
+        self.qps  = GenericCache::new(1, mb_w + 1, 1);
+        self.mv   = GenericCache::new(2, mb_w * 2 + 1, ZERO_MV);
+    }
+    pub fn reset(&mut self) {
+        self.y_dc.reset();
+        self.y_ac.reset();
+        self.u_dc.reset();
+        self.u_ac.reset();
+        self.v_dc.reset();
+        self.v_ac.reset();
+        self.qps.reset();
+        self.mv.reset();
+        self.pred_mv_f = ZERO_MV;
+        self.pred_mv_b = ZERO_MV;
+    }
+    pub fn update_row(&mut self) {
+        self.y_dc.update_row();
+        self.y_ac.update_row();
+        self.u_dc.update_row();
+        self.u_ac.update_row();
+        self.v_dc.update_row();
+        self.v_ac.update_row();
+        self.qps.update_row();
+        self.mv.update_row();
+        self.pred_mv_f = ZERO_MV;
+        self.pred_mv_b = ZERO_MV;
+    }
+    pub fn dc_pred(dcs: &GenericCache<i16>, off: usize) -> (i16, bool) {
+        let addr = dcs.xpos + off;
+        let a_dc = dcs.data[addr - 1];
+        let b_dc = dcs.data[addr - 1 - dcs.stride];
+        let c_dc = dcs.data[addr     - dcs.stride];
+        if (a_dc - b_dc).abs() < (b_dc - c_dc).abs() {
+            (c_dc, true)
+        } else {
+            (a_dc, false)
+        }
+    }
+    pub fn set_quant(&mut self, mb_x: usize, quant: u8) {
+        self.qps.data[self.qps.xpos + mb_x] = quant;
+    }
+    pub fn predict_dc(&mut self, mb_x: usize, cur_dc: i16, dc_scale: u8, n: usize) -> (i16, bool) {
+        match n {
+            0..=3 => {
+                let cur_addr = mb_x * 2 + (n & 1) + (n / 2) * self.y_dc.stride;
+                let (diff, dir) = Self::dc_pred(&self.y_dc, cur_addr);
+                let dc = ((cur_dc + diff / i16::from(dc_scale)) * i16::from(dc_scale)).max(-2048).min(2047);
+                self.y_dc.data[self.y_dc.xpos + cur_addr] = dc;
+                (dc, dir)
+            },
+            4 => {
+                let (diff, dir) = Self::dc_pred(&self.u_dc, mb_x);
+                let dc = ((cur_dc + diff / i16::from(dc_scale)) * i16::from(dc_scale)).max(-2048).min(2047);
+                self.u_dc.data[self.u_dc.xpos + mb_x] = dc;
+                (dc, dir)
+            },
+            5 => {
+                let (diff, dir) = Self::dc_pred(&self.v_dc, mb_x);
+                let dc = ((cur_dc + diff / i16::from(dc_scale)) * i16::from(dc_scale)).max(-2048).min(2047);
+                self.v_dc.data[self.v_dc.xpos + mb_x] = dc;
+                (dc, dir)
+            },
+            _ => unreachable!(),
+        }
+
+    }
+    pub fn predict_ac(&mut self, blk: &mut [i16; 64], mb_x: usize, n: usize, dir: bool) {
+        let quant = self.qps.data[self.qps.xpos + mb_x];
+        let (pq, src) = match (n, dir) {
+                (4, true)  => {
+                    (self.qps.data[self.qps.xpos + mb_x - self.qps.stride],
+                     &self.u_ac.data[self.u_ac.xpos + mb_x - self.u_ac.stride][1])
+                },
+                (4, false) => {
+                    (self.qps.data[self.qps.xpos + mb_x - 1],
+                     &self.u_ac.data[self.u_ac.xpos + mb_x - 1][0])
+                },
+                (5, true)  => {
+                    (self.qps.data[self.qps.xpos + mb_x - self.qps.stride],
+                     &self.v_ac.data[self.v_ac.xpos + mb_x - self.v_ac.stride][1])
+                },
+                (5, false) => {
+                    (self.qps.data[self.qps.xpos + mb_x - 1],
+                     &self.v_ac.data[self.v_ac.xpos + mb_x - 1][0])
+                },
+                (0 | 1, true) => {
+                    (self.qps.data[self.qps.xpos + mb_x - self.qps.stride],
+                     &self.y_ac.data[self.y_ac.xpos + mb_x * 2 + n - self.y_ac.stride][1])
+                },
+                (_, true) => {
+                    (quant, &self.y_ac.data[self.y_ac.xpos + mb_x * 2 + (n & 1)][1])
+                },
+                (0 | 2, false) => {
+                    (self.qps.data[self.qps.xpos + mb_x - 1],
+                     &self.y_ac.data[self.y_ac.xpos + mb_x * 2 + (n / 2) * self.y_ac.stride - 1][0])
+                },
+                (_, false) => {
+                    (quant, &self.y_ac.data[self.y_ac.xpos + mb_x * 2 + (n / 2) * self.y_ac.stride][0])
+                },
+            };
+        if dir {
+            for (dst, &src) in blk[1..8].iter_mut().zip(src.iter()) {
+                *dst += (i32::from(src) * i32::from(pq) / i32::from(quant)) as i16;
+            }
+        } else {
+            for (dst, &src) in blk[8..].chunks_exact_mut(8).zip(src.iter()) {
+                dst[0] += (i32::from(src) * i32::from(pq) / i32::from(quant)) as i16;
+            }
+        }
+        let dblock = match n {
+                0..=3 => &mut self.y_ac.data[self.y_ac.xpos + mb_x * 2 + (n & 1) + (n / 2) * self.y_ac.stride],
+                4 => &mut self.u_ac.data[self.u_ac.xpos + mb_x],
+                _ => &mut self.v_ac.data[self.v_ac.xpos + mb_x],
+            };
+        for (dst, srow) in dblock[0].iter_mut().zip(blk[8..].chunks_exact(8)) {
+            *dst = srow[0];
+        }
+        dblock[1].copy_from_slice(&blk[1..8]);
+    }
+    pub fn save_ac(&mut self, blk: &[i16; 64], mb_x: usize, n: usize) {
+        let dblock = match n {
+                0..=3 => &mut self.y_ac.data[self.y_ac.xpos + mb_x * 2 + (n & 1) + (n / 2) * self.y_ac.stride],
+                4 => &mut self.u_ac.data[self.u_ac.xpos + mb_x],
+                _ => &mut self.v_ac.data[self.v_ac.xpos + mb_x],
+            };
+        for (dst, srow) in dblock[0].iter_mut().zip(blk[8..].chunks_exact(8)) {
+            *dst = srow[0];
+        }
+        dblock[1].copy_from_slice(&blk[1..8]);
+    }
+    pub fn pred_mv(&mut self, mb_x: usize, nstate: &NState, mv: MV) -> MV {
+        let addr = self.mv.xpos + mb_x * 2;
+        let mv_1 = self.mv.data[addr - 1];
+        let mv_2 = self.mv.data[addr - self.mv.stride];
+        let mv_3 = self.mv.data[addr - self.mv.stride + 2];
+        let pred = match (nstate.has_l as u8) + (nstate.has_t as u8) + (nstate.has_tr as u8) {
+                0 => ZERO_MV,
+                1 => MV{ x: mv_1.x | mv_2.x | mv_3.x, y: mv_1.y | mv_2.y | mv_3.y },
+                _ => MV::pred(mv_1, mv_2, mv_3),
+            };
+        let new_mv = MV::add_pred(pred, mv, self.fcode_f);
+        self.mv.data[addr] = new_mv;
+        self.mv.data[addr + 1] = new_mv;
+        self.mv.data[addr + self.mv.stride] = new_mv;
+        self.mv.data[addr + self.mv.stride + 1] = new_mv;
+        new_mv
+    }
+    pub fn pred_mv_f(&mut self, mv: MV) -> MV {
+        let new_mv = MV::add_pred(self.pred_mv_f, mv, self.fcode_f);
+        self.pred_mv_f = new_mv;
+        new_mv
+    }
+    pub fn pred_mv_b(&mut self, mv: MV) -> MV {
+        let new_mv = MV::add_pred(self.pred_mv_b, mv, self.fcode_b);
+        self.pred_mv_b = new_mv;
+        new_mv
+    }
+    pub fn pred_4mv(&mut self, mb_x: usize, nstate: &NState, mvs: &mut [MV; 4]) {
+        const MV_3_OFFS: [u8; 4] = [0, 1, 1, 3]; // real xpos = 2 - offs
+        let base_addr = self.mv.xpos + mb_x * 2;
+        for (n, (mv, &mv3_off)) in mvs.iter_mut().zip(MV_3_OFFS.iter()).enumerate() {
+            let cur_addr = base_addr + (n & 1) + (n / 2) * self.mv.stride;
+            let mv_1 = self.mv.data[cur_addr - 1];
+            let mv_2 = self.mv.data[cur_addr - self.mv.stride];
+            let mv_3 = self.mv.data[cur_addr - self.mv.stride + 2 - usize::from(mv3_off)];
+            let mut num_cand = 0;
+            if n == 1 || n == 3 || nstate.has_l {
+                num_cand += 1;
+            }
+            if n == 2 || n == 3 || nstate.has_t {
+                num_cand += 1;
+            }
+            if n == 2 || n == 3 || nstate.has_tr {
+                num_cand += 1;
+            }
+            let pred = match num_cand {
+                    0 => ZERO_MV,
+                    1 => MV{ x: mv_1.x | mv_2.x | mv_3.x, y: mv_1.y | mv_2.y | mv_3.y },
+                    _ => MV::pred(mv_1, mv_2, mv_3),
+                };
+            *mv = MV::add_pred(pred, *mv, self.fcode_f);
+            self.mv.data[cur_addr] = *mv;
+        }
+    }
+}
index a29505b0d25aedce67ae75837c0132c46fbbd404..3df96a091404499e17c86eb8bbe8920244dbb2a4 100644 (file)
@@ -307,6 +307,7 @@ static CODEC_REGISTER: &[CodecDescription] = &[
     desc!(video-ll; "gif",           "GIF"),
     desc!(video-im; "jpeg",          "JPEG"),
     desc!(video;    "h264",          "ITU H.264", CODEC_CAP_COMPLEX_REORDER | CODEC_CAP_HYBRID),
+    desc!(video-modern; "mpeg4asp",  "MPEG-4 ASP"),
 
     desc!(video-im; "mwv1",          "Aware MotionWavelets"),
 
@@ -364,6 +365,10 @@ static AVI_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[
     (b"PGVV", "pgvv"),
 
     (b"VXS1", "vxvideo"),
+
+    (b"DX50", "mpeg4asp"),
+    (b"DIVX", "mpeg4asp"),
+    (b"XVID", "mpeg4asp"),
 ];
 
 static WAV_CODEC_REGISTER: &[(u16, &str)] = &[
@@ -406,6 +411,7 @@ static MOV_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[
     (b"VP30", "vp3"),
     (b"VP31", "vp3"),
 
+    (b"mp4v", "mpeg4asp"),
     (b"avc1", "h264"),
 ];