X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-commonfmt%2Fsrc%2Fcodecs%2Fzmbvenc.rs;h=1ea74d01585e3377058ff81b8fa2acff4cd87e9c;hb=HEAD;hp=7de6c23a8f97162cb31ca53a24edbc0d0993e44f;hpb=b09ecc095508e73b170333ff85d25f64bd8394db;p=nihav.git diff --git a/nihav-commonfmt/src/codecs/zmbvenc.rs b/nihav-commonfmt/src/codecs/zmbvenc.rs index 7de6c23..1ea74d0 100644 --- a/nihav-commonfmt/src/codecs/zmbvenc.rs +++ b/nihav-commonfmt/src/codecs/zmbvenc.rs @@ -35,6 +35,7 @@ struct ZMBVEncoder { width: usize, height: usize, range: usize, + full_me: bool, sent_pal: bool, } @@ -135,17 +136,19 @@ impl ZMBVEncoder { width: 0, height: 0, range: 128, + full_me: false, sent_pal: false, } } fn encode_intra(&mut self, bw: &mut ByteWriter, buf: NABufferType) -> EncoderResult<()> { - let bpp = buf_type_to_bpp(&buf); + let mut bpp = buf_type_to_bpp(&buf); if let NABufferType::None = buf { if self.bpp == 0 { return Err(EncoderError::FormatError); } self.frm1.copy_from_slice(&self.frm2); + bpp = self.bpp; } else { if bpp == 0 { return Err(EncoderError::FormatError); @@ -184,7 +187,7 @@ impl ZMBVEncoder { } bw.write_buf(&self.frm1[..self.width * self.height * bm])?; } else { - self.tmp_buf.truncate(0); + self.tmp_buf.clear(); if bpp == 8 { self.tmp_buf.extend_from_slice(&self.pal); } @@ -193,7 +196,7 @@ impl ZMBVEncoder { let mut db = Vec::new(); std::mem::swap(&mut db, &mut self.zbuf); - db.truncate(0); + db.clear(); let mut wr = DeflateWriter::new(db); self.compr.write_zlib_header(&mut wr); self.compr.compress(&self.tmp_buf, &mut wr); @@ -211,7 +214,7 @@ impl ZMBVEncoder { self.frm1.copy_from_slice(&self.frm2); bw.write_byte(0)?; - self.tmp_buf.truncate(0); + self.tmp_buf.clear(); let tile_w = (self.width + self.tile_w - 1) / self.tile_w; let tile_h = (self.height + self.tile_h - 1) / self.tile_h; let mv_size = (tile_w * tile_h * 2 + 3) & !3; @@ -224,7 +227,7 @@ impl ZMBVEncoder { let mut db = Vec::new(); std::mem::swap(&mut db, &mut self.zbuf); - db.truncate(0); + db.clear(); let mut wr = DeflateWriter::new(db); self.compr.compress(&self.tmp_buf, &mut wr); self.compr.compress_flush(&mut wr); @@ -240,7 +243,7 @@ impl ZMBVEncoder { return Err(EncoderError::FormatError); } - self.tmp_buf.truncate(0); + self.tmp_buf.clear(); if let (NABufferType::Video(ref vbuf), true) = (&buf, bpp == 8) { let mut npal = [0; 768]; let off = vbuf.get_offset(1); @@ -286,48 +289,7 @@ impl ZMBVEncoder { for x in (0..self.width).step_by(self.tile_w) { let cur_w = (self.width - x).min(self.tile_w); - let mut best_dist = std::u32::MAX; - let mut best_x = x; - let mut best_y = y; - - 'search: for yoff in 0..self.range { - let ypos = (y as isize) + to_signed(yoff); - if ypos < 0 { - continue; - } - let ypos = ypos as usize; - if ypos + cur_h > self.height { - break; - } - for xoff in 0..self.range { - let xpos = (x as isize) + to_signed(xoff); - if xpos < 0 { - continue; - } - let xpos = xpos as usize; - if xpos + cur_w > self.width { - break; - } - - let mut diff = 0; - let roff = xpos * bpp + ypos * stride; - for (line0, line1) in self.frm1[off..].chunks(stride).take(cur_h).zip(self.frm2[roff..].chunks(stride)) { - for (&a, &b) in line0[..cur_w * bpp].iter().zip(line1[..cur_w * bpp].iter()) { - diff += u32::from(a ^ b); - } - } - - if best_dist > diff { - best_dist = diff; - best_x = xpos; - best_y = ypos; - if diff == 0 { - break 'search; - } - } - } - } - + let (best_x, best_y, best_dist) = self.motion_search(&self.frm1[off..], x, y, cur_w, cur_h, bpp); let has_delta = best_dist != 0; self.tmp_buf[mv_start] = (best_x.wrapping_sub(x) << 1) as u8; if has_delta { @@ -356,7 +318,7 @@ impl ZMBVEncoder { let mut db = Vec::new(); std::mem::swap(&mut db, &mut self.zbuf); - db.truncate(0); + db.clear(); let mut wr = DeflateWriter::new(db); self.compr.compress(&self.tmp_buf, &mut wr); self.compr.compress_flush(&mut wr); @@ -368,15 +330,103 @@ impl ZMBVEncoder { Ok(()) } + fn calc_dist(&self, cur_frm: &[u8], xpos: usize, ypos: usize, cur_w: usize, cur_h: usize, bpp: usize) -> u32 { + let stride = self.width * bpp; + let mut diff = 0; + let roff = xpos * bpp + ypos * stride; + for (line0, line1) in cur_frm.chunks(stride).take(cur_h).zip(self.frm2[roff..].chunks(stride)) { + for (&a, &b) in line0[..cur_w * bpp].iter().zip(line1[..cur_w * bpp].iter()) { + diff += u32::from(a ^ b); + } + } + diff + } + fn motion_search(&self, cur_frm: &[u8], x: usize, y: usize, cur_w: usize, cur_h: usize, bpp: usize) -> (usize, usize, u32) { + let mut best_dist = self.calc_dist(cur_frm, x, y, cur_w, cur_h, bpp); + if best_dist == 0 { + return (x, y, 0); + } + let mut best_x = x; + let mut best_y = y; + + if !self.full_me { + let mut cur_range = self.range.min(64); + + while cur_range > 1 { + let x1 = best_x.saturating_sub(cur_range); + let x2 = (best_x + cur_range).min(self.width - cur_w); + let y1 = best_y.saturating_sub(cur_range); + let y2 = (best_y + cur_range).min(self.height - cur_h); + let points = [(best_x, y1), + (x2, y1), + (x2, best_y), + (x2, y2), + (best_x, y2), + (x1, y2), + (x1, best_y), + (x1, y1)]; + + for &(pt_x, pt_y) in points.iter() { + if ((x as isize) - (pt_x as isize)).abs() >= 64 { + continue; + } + if ((y as isize) - (pt_y as isize)).abs() >= 64 { + continue; + } + let dist = self.calc_dist(cur_frm, pt_x, pt_y, cur_w, cur_h, bpp); + if dist < best_dist { + best_dist = dist; + best_x = pt_x; + best_y = pt_y; + } + } + cur_range = (cur_range + 1) >> 1; + } + } else { + for yoff in 0..self.range { + let ypos = (y as isize) + to_signed(yoff); + if ypos < 0 { + continue; + } + let ypos = ypos as usize; + if ypos + cur_h > self.height { + break; + } + for xoff in 0..self.range { + let xpos = (x as isize) + to_signed(xoff); + if xpos < 0 { + continue; + } + let xpos = xpos as usize; + if xpos + cur_w > self.width { + break; + } + + let diff = self.calc_dist(cur_frm, xpos, ypos, cur_w, cur_h, bpp); + + if best_dist > diff { + best_dist = diff; + best_x = xpos; + best_y = ypos; + if diff == 0 { + return (best_x, best_y, 0); + } + } + } + } + } + (best_x, best_y, best_dist) + } } impl NAEncoder for ZMBVEncoder { fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult { match encinfo.format { NACodecTypeInfo::None => { - let mut ofmt = EncodeParameters::default(); - ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT)); - Ok(ofmt) + Ok(EncodeParameters { + format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT)), + ..Default::default() + }) }, NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), NACodecTypeInfo::Video(vinfo) => { @@ -397,6 +447,7 @@ impl NAEncoder for ZMBVEncoder { } } } + fn get_capabilities(&self) -> u64 { ENC_CAPS_SKIPFRAME } fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult { match encinfo.format { NACodecTypeInfo::None => Err(EncoderError::FormatError), @@ -473,6 +524,9 @@ const ENCODER_OPTS: &[NAOptionDefinition] = &[ NAOptionDefinition { name: "range", description: "Block search range (0-128)", opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, + NAOptionDefinition { + name: "full_me", description: "Brute force search", + opt_type: NAOptionDefinitionType::Bool }, NAOptionDefinition { name: "tile_width", description: "Block width (1-255)", opt_type: NAOptionDefinitionType::Int(Some(1), Some(255)) }, @@ -508,6 +562,11 @@ impl NAOptionHandler for ZMBVEncoder { self.range = intval as usize; } }, + "full_me" => { + if let NAValue::Bool(bval) = option.value { + self.full_me = bval; + } + }, "tile_width" => { if let NAValue::Int(intval) = option.value { self.tile_w = intval as usize; @@ -529,6 +588,7 @@ impl NAOptionHandler for ZMBVEncoder { "compr_level" => Some(NAValue::String(self.cmode.to_string())), KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))), "range" => Some(NAValue::Int(self.range as i64)), + "full_me" => Some(NAValue::Bool(self.full_me)), "tile_width" => Some(NAValue::Int(self.tile_w as i64)), "tile_height" => Some(NAValue::Int(self.tile_h as i64)), _ => None, @@ -549,6 +609,7 @@ mod test { use nihav_codec_support::test::enc_video::*; use super::{RGB555_FORMAT, RGB24_0_FORMAT}; + // samples are from https://samples.mplayerhq.hu/V-codecs/ZMBV/ #[test] fn test_zmbv_encoder_8bit() { let mut dmx_reg = RegisteredDemuxers::new(); @@ -588,10 +649,13 @@ mod test { tb_den: 0, flags: 0, }; - let enc_options = &[]; + let enc_options = &[ + NAOption { name: "range", value: NAValue::Int(16) }, + NAOption { name: "full_me", value: NAValue::Bool(true) }, + ]; //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, - &[0x50df10e2, 0x606f3268, 0xdd4bc2ff, 0x844e7d87]); + &[0x18bd3754, 0x97007f81, 0xff2bcd07, 0x739c48dc]); } #[test] @@ -633,10 +697,13 @@ mod test { tb_den: 0, flags: 0, }; - let enc_options = &[]; + let enc_options = &[ + NAOption { name: "range", value: NAValue::Int(16) }, + NAOption { name: "full_me", value: NAValue::Bool(true) }, + ]; //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, - &[0x0b4cb528, 0x66c91f6c, 0x1c2187a5, 0x2723a08d]); + &[0x00311257, 0xd26a0e9e, 0xfd4b003f, 0x7c962d7b]); } #[test] @@ -678,10 +745,13 @@ mod test { tb_den: 0, flags: 0, }; - let enc_options = &[]; + let enc_options = &[ + NAOption { name: "range", value: NAValue::Int(16) }, + NAOption { name: "full_me", value: NAValue::Bool(true) }, + ]; //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, - &[0x1a522743, 0x6c320a6e, 0xd08539e1, 0x03fc17ea]); + &[0x4eea104f, 0x2ebe544b, 0x54deb0f9, 0xe5ca88f4]); } #[test] @@ -723,9 +793,12 @@ mod test { tb_den: 0, flags: 0, }; - let enc_options = &[]; + let enc_options = &[ + NAOption { name: "range", value: NAValue::Int(16) }, + NAOption { name: "full_me", value: NAValue::Bool(true) }, + ]; //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, - &[0x3880e045, 0xe6c88dc7, 0x21066058, 0xc789f1e9]); + &[0xffceb4bd, 0xb1beccd9, 0x4983e7f6, 0xf46e33ba]); } }