1 use nihav_core::codecs::*;
2 use nihav_core::compr::deflate::{Deflate, DeflateMode, DeflateWriter};
3 use nihav_core::io::byteio::*;
5 const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
7 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
8 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }),
9 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }),
11 elem_size: 2, be: false, alpha: false, palette: false };
12 const RGB24_0_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
14 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }),
15 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }),
16 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }),
18 elem_size: 4, be: false, alpha: false, palette: false };
21 stream: Option<NAStreamRef>,
27 pkt: Option<NAPacket>,
42 fn buf_type_to_bpp(buf: &NABufferType) -> u8 {
44 NABufferType::Video(ref vbuf) => {
45 let vinfo = vbuf.get_info();
46 if vinfo.get_format().is_paletted() {
49 vinfo.get_format().get_total_depth()
52 NABufferType::VideoPacked(ref vbuf) => {
53 let vinfo = vbuf.get_info();
54 vinfo.get_format().elem_size * 8
56 NABufferType::Video16(ref vbuf) => {
57 let vinfo = vbuf.get_info();
58 vinfo.get_format().get_total_depth()
64 fn copy_frame(buf: NABufferType, dst: &mut [u8], bpp: u8) -> EncoderResult<()> {
66 NABufferType::Video(ref vbuf) => {
68 return Err(EncoderError::FormatError);
70 let off = vbuf.get_offset(0);
71 let stride = vbuf.get_stride(0);
72 let data = vbuf.get_data();
73 let w = vbuf.get_info().get_width();
74 let h = vbuf.get_info().get_height();
76 for (dline, sline) in dst.chunks_mut(w).zip(data[off..].chunks(stride)).take(h) {
77 dline[..w].copy_from_slice(&sline[..w]);
80 NABufferType::Video16(ref vbuf) => {
81 let off = vbuf.get_offset(0);
82 let stride = vbuf.get_stride(0);
83 let data = vbuf.get_data();
84 let w = vbuf.get_info().get_width();
85 let h = vbuf.get_info().get_height();
87 for (dline, sline) in dst.chunks_mut(w * 2).zip(data[off..].chunks(stride)).take(h) {
88 for (dst, &src) in dline[..w * 2].chunks_exact_mut(2).zip(sline.iter()) {
90 dst[1] = (src >> 8) as u8;
94 NABufferType::VideoPacked(ref vbuf) => {
95 let off = vbuf.get_offset(0);
96 let stride = vbuf.get_stride(0);
97 let data = vbuf.get_data();
98 let w = vbuf.get_info().get_width();
99 let h = vbuf.get_info().get_height();
100 let w = w * (((bpp as usize) + 7) / 8);
102 for (dline, sline) in dst.chunks_mut(w).zip(data[off..].chunks(stride)).take(h) {
103 dline[..w].copy_from_slice(&sline[..w]);
106 _ => return Err(EncoderError::FormatError),
111 fn to_signed(val: usize) -> isize {
115 -((val >> 1) as isize) - 1
133 cmode: DeflateMode::default(),
134 compr: Deflate::new(DeflateMode::default()),
143 fn encode_intra(&mut self, bw: &mut ByteWriter, buf: NABufferType) -> EncoderResult<()> {
144 let mut bpp = buf_type_to_bpp(&buf);
146 if let NABufferType::None = buf {
148 return Err(EncoderError::FormatError);
150 self.frm1.copy_from_slice(&self.frm2);
154 return Err(EncoderError::FormatError);
158 if let (NABufferType::Video(ref vbuf), true) = (&buf, bpp == 8) {
159 let off = vbuf.get_offset(1);
160 let data = vbuf.get_data();
161 self.pal.copy_from_slice(&data[off..][..768]);
164 copy_frame(buf, &mut self.frm1, self.bpp)?;
167 bw.write_byte(1)?; // intra flag
168 bw.write_byte(0)?; // high version
169 bw.write_byte(1)?; // low version
170 bw.write_byte(if self.cmode == DeflateMode::NoCompr { 0 } else { 1 })?;
171 let fmt = match self.bpp {
180 bw.write_byte(self.tile_w as u8)?;
181 bw.write_byte(self.tile_h as u8)?;
183 let bm = ((bpp as usize) + 7) / 8;
184 if self.cmode == DeflateMode::NoCompr {
186 bw.write_buf(&self.pal)?;
188 bw.write_buf(&self.frm1[..self.width * self.height * bm])?;
190 self.tmp_buf.clear();
192 self.tmp_buf.extend_from_slice(&self.pal);
194 self.tmp_buf.extend_from_slice(&self.frm1[..self.width * self.height * bm]);
195 self.compr = Deflate::new(self.cmode);
197 let mut db = Vec::new();
198 std::mem::swap(&mut db, &mut self.zbuf);
200 let mut wr = DeflateWriter::new(db);
201 self.compr.write_zlib_header(&mut wr);
202 self.compr.compress(&self.tmp_buf, &mut wr);
203 self.compr.compress_flush(&mut wr);
204 let mut db = wr.end();
205 std::mem::swap(&mut db, &mut self.zbuf);
207 bw.write_buf(&self.zbuf)?;
212 fn encode_inter(&mut self, bw: &mut ByteWriter, buf: NABufferType) -> EncoderResult<()> {
213 if let NABufferType::None = buf {
214 self.frm1.copy_from_slice(&self.frm2);
217 self.tmp_buf.clear();
218 let tile_w = (self.width + self.tile_w - 1) / self.tile_w;
219 let tile_h = (self.height + self.tile_h - 1) / self.tile_h;
220 let mv_size = (tile_w * tile_h * 2 + 3) & !3;
221 for _ in 0..mv_size {
222 self.tmp_buf.push(0);
224 if self.cmode == DeflateMode::NoCompr {
225 bw.write_buf(&self.tmp_buf)?;
227 let mut db = Vec::new();
229 std::mem::swap(&mut db, &mut self.zbuf);
231 let mut wr = DeflateWriter::new(db);
232 self.compr.compress(&self.tmp_buf, &mut wr);
233 self.compr.compress_flush(&mut wr);
234 let mut db = wr.end();
235 std::mem::swap(&mut db, &mut self.zbuf);
237 bw.write_buf(&self.zbuf)?;
241 let bpp = buf_type_to_bpp(&buf);
242 if bpp == 0 || bpp != self.bpp {
243 return Err(EncoderError::FormatError);
246 self.tmp_buf.clear();
247 if let (NABufferType::Video(ref vbuf), true) = (&buf, bpp == 8) {
248 let mut npal = [0; 768];
249 let off = vbuf.get_offset(1);
250 let data = vbuf.get_data();
251 npal.copy_from_slice(&data[off..][..768]);
253 for (&a, &b) in self.pal.iter().zip(npal.iter()) {
260 for (&a, &b) in self.pal.iter().zip(npal.iter()) {
261 self.tmp_buf.push(a ^ b);
267 self.sent_pal = false;
274 copy_frame(buf, &mut self.frm1, self.bpp)?;
276 let tile_w = (self.width + self.tile_w - 1) / self.tile_w;
277 let tile_h = (self.height + self.tile_h - 1) / self.tile_h;
278 let mut mv_start = self.tmp_buf.len();
279 let mv_size = (tile_w * tile_h * 2 + 3) & !3;
280 for _ in 0..mv_size {
281 self.tmp_buf.push(0);
284 let bpp = ((self.bpp as usize) + 7) / 8;
285 let stride = self.width * bpp;
287 for y in (0..self.height).step_by(self.tile_h) {
288 let cur_h = (self.height - y).min(self.tile_h);
289 for x in (0..self.width).step_by(self.tile_w) {
290 let cur_w = (self.width - x).min(self.tile_w);
292 let (best_x, best_y, best_dist) = self.motion_search(&self.frm1[off..], x, y, cur_w, cur_h, bpp);
293 let has_delta = best_dist != 0;
294 self.tmp_buf[mv_start] = (best_x.wrapping_sub(x) << 1) as u8;
296 self.tmp_buf[mv_start] |= 1;
298 self.tmp_buf[mv_start + 1] = (best_y.wrapping_sub(y) << 1) as u8;
301 let rpos = best_x * bpp + best_y * stride;
302 for (line0, line1) in self.frm1[off..].chunks(stride).take(cur_h).zip(self.frm2[rpos..].chunks(stride)) {
303 for (&a, &b) in line0[..cur_w * bpp].iter().zip(line1[..cur_w * bpp].iter()) {
304 self.tmp_buf.push(a ^ b);
309 off += self.tile_w * bpp;
311 off -= tile_w * self.tile_w * bpp;
312 off += stride * self.tile_h;
315 if self.cmode == DeflateMode::NoCompr {
316 bw.write_buf(&self.tmp_buf)?;
318 let mut db = Vec::new();
320 std::mem::swap(&mut db, &mut self.zbuf);
322 let mut wr = DeflateWriter::new(db);
323 self.compr.compress(&self.tmp_buf, &mut wr);
324 self.compr.compress_flush(&mut wr);
325 let mut db = wr.end();
326 std::mem::swap(&mut db, &mut self.zbuf);
328 bw.write_buf(&self.zbuf)?;
333 fn calc_dist(&self, cur_frm: &[u8], xpos: usize, ypos: usize, cur_w: usize, cur_h: usize, bpp: usize) -> u32 {
334 let stride = self.width * bpp;
336 let roff = xpos * bpp + ypos * stride;
337 for (line0, line1) in cur_frm.chunks(stride).take(cur_h).zip(self.frm2[roff..].chunks(stride)) {
338 for (&a, &b) in line0[..cur_w * bpp].iter().zip(line1[..cur_w * bpp].iter()) {
339 diff += u32::from(a ^ b);
344 fn motion_search(&self, cur_frm: &[u8], x: usize, y: usize, cur_w: usize, cur_h: usize, bpp: usize) -> (usize, usize, u32) {
345 let mut best_dist = self.calc_dist(cur_frm, x, y, cur_w, cur_h, bpp);
353 let mut cur_range = self.range.min(64);
355 while cur_range > 1 {
356 let x1 = best_x.saturating_sub(cur_range);
357 let x2 = (best_x + cur_range).min(self.width - cur_w);
358 let y1 = best_y.saturating_sub(cur_range);
359 let y2 = (best_y + cur_range).min(self.height - cur_h);
360 let points = [(best_x, y1),
369 for &(pt_x, pt_y) in points.iter() {
370 if ((x as isize) - (pt_x as isize)).abs() >= 64 {
373 if ((y as isize) - (pt_y as isize)).abs() >= 64 {
376 let dist = self.calc_dist(cur_frm, pt_x, pt_y, cur_w, cur_h, bpp);
377 if dist < best_dist {
383 cur_range = (cur_range + 1) >> 1;
386 for yoff in 0..self.range {
387 let ypos = (y as isize) + to_signed(yoff);
391 let ypos = ypos as usize;
392 if ypos + cur_h > self.height {
395 for xoff in 0..self.range {
396 let xpos = (x as isize) + to_signed(xoff);
400 let xpos = xpos as usize;
401 if xpos + cur_w > self.width {
405 let diff = self.calc_dist(cur_frm, xpos, ypos, cur_w, cur_h, bpp);
407 if best_dist > diff {
412 return (best_x, best_y, 0);
418 (best_x, best_y, best_dist)
422 impl NAEncoder for ZMBVEncoder {
423 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
424 match encinfo.format {
425 NACodecTypeInfo::None => {
426 Ok(EncodeParameters {
427 format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT)),
431 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
432 NACodecTypeInfo::Video(vinfo) => {
433 let depth = vinfo.format.get_total_depth();
434 let pix_fmt = if vinfo.format.is_paletted() {
436 } else if !vinfo.format.model.is_rgb() || depth > 16 {
438 } else if depth < 16 {
443 let outinfo = NAVideoInfo::new(vinfo.width, vinfo.height, false, pix_fmt);
444 let mut ofmt = *encinfo;
445 ofmt.format = NACodecTypeInfo::Video(outinfo);
450 fn get_capabilities(&self) -> u64 { ENC_CAPS_SKIPFRAME }
451 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
452 match encinfo.format {
453 NACodecTypeInfo::None => Err(EncoderError::FormatError),
454 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
455 NACodecTypeInfo::Video(vinfo) => {
456 self.width = vinfo.width;
457 self.height = vinfo.height;
459 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format);
460 let info = NACodecInfo::new("zmbv", NACodecTypeInfo::Video(out_info), None);
461 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
462 stream.set_num(stream_id as usize);
463 let stream = stream.into_ref();
465 self.stream = Some(stream.clone());
467 self.frm1 = vec![0; vinfo.width * vinfo.height * 4];
468 self.frm2 = vec![0; vinfo.width * vinfo.height * 4];
474 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
475 let buf = frm.get_buffer();
476 let mut dbuf = Vec::with_capacity(4);
477 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
478 let mut bw = ByteWriter::new(&mut gw);
479 let is_intra = if self.frmcount == 0 {
480 self.encode_intra(&mut bw, buf)?;
483 self.encode_inter(&mut bw, buf)?;
486 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
487 if self.bpp == 8 && !self.sent_pal {
488 if let NABufferType::Video(ref buf) = frm.get_buffer() {
489 let paloff = buf.get_offset(1);
490 let data = buf.get_data();
491 let mut pal = [0; 1024];
492 let srcpal = &data[paloff..][..768];
493 for (dclr, sclr) in pal.chunks_exact_mut(4).zip(srcpal.chunks_exact(3)) {
494 dclr[..3].copy_from_slice(sclr);
496 if let Some(ref mut pkt) = &mut self.pkt {
497 pkt.side_data.push(NASideData::Palette(true, Arc::new(pal)));
500 self.sent_pal = true;
503 if self.frmcount == self.key_int {
506 std::mem::swap(&mut self.frm1, &mut self.frm2);
509 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
511 std::mem::swap(&mut self.pkt, &mut npkt);
514 fn flush(&mut self) -> EncoderResult<()> {
520 const ENCODER_OPTS: &[NAOptionDefinition] = &[
522 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
523 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
525 name: "range", description: "Block search range (0-128)",
526 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
528 name: "full_me", description: "Brute force search",
529 opt_type: NAOptionDefinitionType::Bool },
531 name: "tile_width", description: "Block width (1-255)",
532 opt_type: NAOptionDefinitionType::Int(Some(1), Some(255)) },
534 name: "tile_height", description: "Block width (1-255)",
535 opt_type: NAOptionDefinitionType::Int(Some(1), Some(255)) },
537 name: "compr_level", description: "Compression level",
538 opt_type: DEFLATE_OPTION_VALUES },
541 impl NAOptionHandler for ZMBVEncoder {
542 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
543 fn set_options(&mut self, options: &[NAOption]) {
544 for option in options.iter() {
545 for opt_def in ENCODER_OPTS.iter() {
546 if opt_def.check(option).is_ok() {
549 if let NAValue::String(ref s) = option.value {
550 if let Ok(val) = s.parse::<DeflateMode>() {
556 if let NAValue::Int(intval) = option.value {
557 self.key_int = intval as u8;
561 if let NAValue::Int(intval) = option.value {
562 self.range = intval as usize;
566 if let NAValue::Bool(bval) = option.value {
571 if let NAValue::Int(intval) = option.value {
572 self.tile_w = intval as usize;
576 if let NAValue::Int(intval) = option.value {
577 self.tile_h = intval as usize;
586 fn query_option_value(&self, name: &str) -> Option<NAValue> {
588 "compr_level" => Some(NAValue::String(self.cmode.to_string())),
589 KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
590 "range" => Some(NAValue::Int(self.range as i64)),
591 "full_me" => Some(NAValue::Bool(self.full_me)),
592 "tile_width" => Some(NAValue::Int(self.tile_w as i64)),
593 "tile_height" => Some(NAValue::Int(self.tile_h as i64)),
599 pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
600 Box::new(ZMBVEncoder::new())
605 use nihav_core::codecs::*;
606 use nihav_core::demuxers::*;
607 use nihav_core::muxers::*;
609 use nihav_codec_support::test::enc_video::*;
610 use super::{RGB555_FORMAT, RGB24_0_FORMAT};
612 // samples are from https://samples.mplayerhq.hu/V-codecs/ZMBV/
614 fn test_zmbv_encoder_8bit() {
615 let mut dmx_reg = RegisteredDemuxers::new();
616 generic_register_all_demuxers(&mut dmx_reg);
617 let mut dec_reg = RegisteredDecoders::new();
618 generic_register_all_decoders(&mut dec_reg);
619 let mut mux_reg = RegisteredMuxers::new();
620 generic_register_all_muxers(&mut mux_reg);
621 let mut enc_reg = RegisteredEncoders::new();
622 generic_register_all_encoders(&mut enc_reg);
624 let dec_config = DecoderTestParams {
626 in_name: "assets/Misc/td3_000.avi",
627 stream_type: StreamType::Video,
631 let enc_config = EncoderTestParams {
634 out_name: "zmbv8.avi",
637 let dst_vinfo = NAVideoInfo {
644 let enc_params = EncodeParameters {
645 format: NACodecTypeInfo::Video(dst_vinfo),
653 NAOption { name: "range", value: NAValue::Int(16) },
654 NAOption { name: "full_me", value: NAValue::Bool(true) },
656 //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
657 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options,
658 &[0x18bd3754, 0x97007f81, 0xff2bcd07, 0x739c48dc]);
662 fn test_zmbv_encoder_15bit() {
663 let mut dmx_reg = RegisteredDemuxers::new();
664 generic_register_all_demuxers(&mut dmx_reg);
665 let mut dec_reg = RegisteredDecoders::new();
666 generic_register_all_decoders(&mut dec_reg);
667 let mut mux_reg = RegisteredMuxers::new();
668 generic_register_all_muxers(&mut mux_reg);
669 let mut enc_reg = RegisteredEncoders::new();
670 generic_register_all_encoders(&mut enc_reg);
672 let dec_config = DecoderTestParams {
674 in_name: "assets/Misc/zmbv_15bit.avi",
675 stream_type: StreamType::Video,
679 let enc_config = EncoderTestParams {
682 out_name: "zmbv15.avi",
685 let dst_vinfo = NAVideoInfo {
688 format: RGB555_FORMAT,
692 let enc_params = EncodeParameters {
693 format: NACodecTypeInfo::Video(dst_vinfo),
701 NAOption { name: "range", value: NAValue::Int(16) },
702 NAOption { name: "full_me", value: NAValue::Bool(true) },
704 //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
705 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options,
706 &[0x00311257, 0xd26a0e9e, 0xfd4b003f, 0x7c962d7b]);
710 fn test_zmbv_encoder_16bit() {
711 let mut dmx_reg = RegisteredDemuxers::new();
712 generic_register_all_demuxers(&mut dmx_reg);
713 let mut dec_reg = RegisteredDecoders::new();
714 generic_register_all_decoders(&mut dec_reg);
715 let mut mux_reg = RegisteredMuxers::new();
716 generic_register_all_muxers(&mut mux_reg);
717 let mut enc_reg = RegisteredEncoders::new();
718 generic_register_all_encoders(&mut enc_reg);
720 let dec_config = DecoderTestParams {
722 in_name: "assets/Misc/zmbv_16bit.avi",
723 stream_type: StreamType::Video,
727 let enc_config = EncoderTestParams {
730 out_name: "zmbv16.avi",
733 let dst_vinfo = NAVideoInfo {
736 format: RGB565_FORMAT,
740 let enc_params = EncodeParameters {
741 format: NACodecTypeInfo::Video(dst_vinfo),
749 NAOption { name: "range", value: NAValue::Int(16) },
750 NAOption { name: "full_me", value: NAValue::Bool(true) },
752 //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
753 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options,
754 &[0x4eea104f, 0x2ebe544b, 0x54deb0f9, 0xe5ca88f4]);
758 fn test_zmbv_encoder_32bit() {
759 let mut dmx_reg = RegisteredDemuxers::new();
760 generic_register_all_demuxers(&mut dmx_reg);
761 let mut dec_reg = RegisteredDecoders::new();
762 generic_register_all_decoders(&mut dec_reg);
763 let mut mux_reg = RegisteredMuxers::new();
764 generic_register_all_muxers(&mut mux_reg);
765 let mut enc_reg = RegisteredEncoders::new();
766 generic_register_all_encoders(&mut enc_reg);
768 let dec_config = DecoderTestParams {
770 in_name: "assets/Misc/zmbv_32bit.avi",
771 stream_type: StreamType::Video,
775 let enc_config = EncoderTestParams {
778 out_name: "zmbv32.avi",
781 let dst_vinfo = NAVideoInfo {
784 format: RGB24_0_FORMAT,
788 let enc_params = EncodeParameters {
789 format: NACodecTypeInfo::Video(dst_vinfo),
797 NAOption { name: "range", value: NAValue::Int(16) },
798 NAOption { name: "full_me", value: NAValue::Bool(true) },
800 //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
801 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options,
802 &[0xffceb4bd, 0xb1beccd9, 0x4983e7f6, 0xf46e33ba]);