From 2260bfdc785eedbcfb659093bcc488ba1fe23ee9 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Mon, 14 Oct 2024 18:14:59 +0200 Subject: [PATCH] add MPEG-4 ASP decoder --- nihav-mpeg/Cargo.toml | 5 +- nihav-mpeg/src/codecs/mod.rs | 6 + nihav-mpeg/src/codecs/mpeg4asp/bitstream.rs | 1198 +++++++++++++++++++ nihav-mpeg/src/codecs/mpeg4asp/decoder.rs | 259 ++++ nihav-mpeg/src/codecs/mpeg4asp/dispatch.rs | 9 + nihav-mpeg/src/codecs/mpeg4asp/dsp.rs | 440 +++++++ nihav-mpeg/src/codecs/mpeg4asp/mod.rs | 422 +++++++ nihav-mpeg/src/codecs/mpeg4asp/types.rs | 429 +++++++ nihav-registry/src/register.rs | 6 + 9 files changed, 2772 insertions(+), 2 deletions(-) create mode 100644 nihav-mpeg/src/codecs/mpeg4asp/bitstream.rs create mode 100644 nihav-mpeg/src/codecs/mpeg4asp/decoder.rs create mode 100644 nihav-mpeg/src/codecs/mpeg4asp/dispatch.rs create mode 100644 nihav-mpeg/src/codecs/mpeg4asp/dsp.rs create mode 100644 nihav-mpeg/src/codecs/mpeg4asp/mod.rs create mode 100644 nihav-mpeg/src/codecs/mpeg4asp/types.rs diff --git a/nihav-mpeg/Cargo.toml b/nihav-mpeg/Cargo.toml index 84d0c4b..52b2bc7 100644 --- a/nihav-mpeg/Cargo.toml +++ b/nihav-mpeg/Cargo.toml @@ -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"] diff --git a/nihav-mpeg/src/codecs/mod.rs b/nihav-mpeg/src/codecs/mod.rs index f6fb31c..9d48808 100644 --- a/nihav-mpeg/src/codecs/mod.rs +++ b/nihav-mpeg/src/codecs/mod.rs @@ -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 index 0000000..b82226b --- /dev/null +++ b/nihav-mpeg/src/codecs/mpeg4asp/bitstream.rs @@ -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; +} + +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 { + 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> { + 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 { + 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 { + 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, + inter_mcbpc_cb: Codebook, + cbpy_cb: Codebook, + intra_rl_cb: Codebook, + inter_rl_cb: Codebook, + mv_cb: Codebook, + luma_dc_len_cb: Codebook, + chroma_dc_len_cb: Codebook, +} + +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 { + 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 { + 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, fcode: u8) -> DecoderResult { + 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 { + 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, 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, short_header: bool) -> DecoderResult { + 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, 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, 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 index 0000000..a71da94 --- /dev/null +++ b/nihav-mpeg/src/codecs/mpeg4asp/decoder.rs @@ -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>; + +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 { + 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, 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, + 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 index 0000000..67948ee --- /dev/null +++ b/nihav-mpeg/src/codecs/mpeg4asp/dispatch.rs @@ -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; + 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 index 0000000..3353275 --- /dev/null +++ b/nihav-mpeg/src/codecs/mpeg4asp/dsp.rs @@ -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, 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, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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, mb_x: usize, mb_y: usize, pframe: NAVideoBufferRef, 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 index 0000000..d4fd23e --- /dev/null +++ b/nihav-mpeg/src/codecs/mpeg4asp/mod.rs @@ -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 { + 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 { + 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 { + match name { + FRAME_SKIP_OPTION => Some(NAValue::String(self.skip_mode.to_string())), + _ => None, + } + } +} + + +pub fn get_decoder() -> Box { + 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 index 0000000..73164da --- /dev/null +++ b/nihav-mpeg/src/codecs/mpeg4asp/types.rs @@ -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, + 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, + 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, + pub y_ac: GenericCache, + pub u_dc: GenericCache, + pub u_ac: GenericCache, + pub v_dc: GenericCache, + pub v_ac: GenericCache, + pub qps: GenericCache, + pub mv: GenericCache, + 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, 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; + } + } +} diff --git a/nihav-registry/src/register.rs b/nihav-registry/src/register.rs index a29505b..3df96a0 100644 --- a/nihav-registry/src/register.rs +++ b/nihav-registry/src/register.rs @@ -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"), ]; -- 2.39.5