[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"] }
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"]
($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)]
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")]
--- /dev/null
+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
+];
--- /dev/null
+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!()
+ }
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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 ];
--- /dev/null
+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]]));
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
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"),
(b"PGVV", "pgvv"),
(b"VXS1", "vxvideo"),
+
+ (b"DX50", "mpeg4asp"),
+ (b"DIVX", "mpeg4asp"),
+ (b"XVID", "mpeg4asp"),
];
static WAV_CODEC_REGISTER: &[(u16, &str)] = &[
(b"VP30", "vp3"),
(b"VP31", "vp3"),
+ (b"mp4v", "mpeg4asp"),
(b"avc1", "h264"),
];