width: usize,
height: usize,
range: usize,
+ full_me: bool,
sent_pal: bool,
}
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);
}
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);
}
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);
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;
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);
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);
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 {
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);
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<EncodeParameters> {
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) => {
}
}
}
+ fn get_capabilities(&self) -> u64 { ENC_CAPS_SKIPFRAME }
fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
match encinfo.format {
NACodecTypeInfo::None => Err(EncoderError::FormatError),
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)) },
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;
"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,
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();
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]
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]
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]
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]);
}
}