1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
4 type UnpackedPixel = [u16; 4];
6 fn map_quality(quality: u8) -> (u32, u32) {
10 let skip_threshold = (10 - (u32::from(quality) / 10).min(10)) * 8;
11 let fill_threshold = (10 - (u32::from(quality) / 10).min(10)) * 16;
12 (skip_threshold, fill_threshold)
17 fn unpack(&self) -> UnpackedPixel;
18 fn dist<T: PixelOps>(&self, val: T) -> u32 {
19 dist_core(self.unpack(), &val.unpack())
23 impl PixelOps for u16 {
24 fn unpack(&self) -> UnpackedPixel {
26 let r = (val >> 10) & 0x1F;
27 let g = (val >> 5) & 0x1F;
29 [r, g, b, rgb2y(r, g, b)]
33 fn dist_core(val: UnpackedPixel, other: &UnpackedPixel) -> u32 {
34 let sum = val.iter().zip(other.iter()).take(3).fold(0i32,
36 let diff = i32::from(a) - i32::from(b);
43 fn rgb2y(r: u16, g: u16, b: u16) -> u16 {
44 (r * 77 + g * 150 + b * 29) >> 8
47 fn pack_rgb555(val: UnpackedPixel) -> u16 {
48 (val[0] << 10) | (val[1] << 5) | val[2]
58 fn new() -> Self { Self::default() }
59 fn add(&mut self, val: &UnpackedPixel) {
60 for (dst, &src) in self.sum.iter_mut().zip(val.iter()) {
65 fn get_avg(&self) -> UnpackedPixel {
67 let mut ret = self.sum;
68 for el in ret.iter_mut() {
78 macro_rules! quant_template {
79 ($name:ident, $N:expr) => {
80 fn $name(pix: &[UnpackedPixel; $N]) -> ([UnpackedPixel; 2], u16, u32) {
81 let mut avg = PixelAverage::new();
82 let mut maxv = [0; 4];
83 let mut minv = [255; 4];
84 for src in pix.iter() {
86 for ((maxv, minv), &comp) in maxv.iter_mut().zip(minv.iter_mut()).zip(src.iter()) {
87 *maxv = (*maxv).max(comp);
88 *minv = (*minv).min(comp);
91 let avg = avg.get_avg();
93 let mut best_axis = 3;
94 let mut best_dist = maxv[3] - minv[3];
95 for (comp_no, (&minval, &maxval)) in minv.iter().zip(maxv.iter()).enumerate().take(3) {
96 if maxval - minval > best_dist {
98 best_dist = maxval - minval;
103 for el in pix.iter() {
104 dist += dist_core(avg, el);
106 return ([avg; 2], 0, dist);
109 let mut avg1 = PixelAverage::new();
110 let mut avg2 = PixelAverage::new();
112 let mut mask_bit = 1;
113 for clr in pix.iter() {
114 if clr[best_axis] > avg[best_axis] {
123 let clr0 = avg1.get_avg();
124 let clr1 = avg2.get_avg();
126 for clr in pix.iter() {
127 let dist0 = dist_core(clr0, clr);
128 let dist1 = dist_core(clr1, clr);
129 dist += dist0.min(dist1);
131 ([clr0, clr1], mask, dist)
136 quant_template!(quant2_16pix, 16);
137 quant_template!(quant2_4pix, 4);
142 fill_val: UnpackedPixel,
145 clr2: [UnpackedPixel; 2],
148 clr8: [[UnpackedPixel; 2]; 4],
153 fn set_fill_val(&mut self, val: UnpackedPixel) {
156 self.fill_val[0] &= !1;
159 fn calc_clrs(buf: &[UnpackedPixel; 16]) -> (Option<UnpackedPixel>, Option<UnpackedPixel>) {
162 let mut single = true;
163 for &pix in buf[1..].iter() {
168 } else if pix != clr1 {
174 (Some(clr0), Some(clr1))
179 fn calc_stats(&mut self, buf: &[UnpackedPixel; 16]) {
180 let mut filled = false;
181 let mut two_clr = false;
182 match Self::calc_clrs(buf) {
183 (Some(clr0), Some(clr1)) => {
188 (Some(clr0), None) => {
191 self.set_fill_val(buf[0]);
199 let mut avg = PixelAverage::new();
200 for pix in buf.iter() {
203 self.set_fill_val(avg.get_avg());
204 for pix in buf.iter() {
205 self.fill_dist += dist_core(self.fill_val, pix);
208 if self.fill_dist == 0 {
209 self.clr2_dist = std::u32::MAX;
210 self.clr8_dist = std::u32::MAX;
214 self.clr2_flags = 0u16;
218 for &pix in buf.iter() {
219 if pix == self.clr2[0] {
220 self.clr2_flags |= mask;
225 if (self.clr2_flags & 0x8000) != 0 {
226 self.clr2_flags = !self.clr2_flags;
227 self.clr2.swap(0, 1);
230 let (clrs, mask, dist) = quant2_16pix(&buf);
232 self.clr2_flags = mask;
233 self.clr2_dist = dist;
234 if (self.clr2_flags & 0x8000) != 0 {
235 self.clr2_flags = !self.clr2_flags;
236 self.clr2.swap(0, 1);
239 if self.clr2_dist == 0 {
240 self.clr8_dist = std::u32::MAX;
244 self.clr8 = [[UnpackedPixel::default(); 2]; 4];
248 let off = (i & 1) * 2 + (i & 2) * 4;
249 let src2 = [buf[off], buf[off + 1], buf[off + 4], buf[off + 5]];
250 let (clrs, mask, dist) = quant2_4pix(&src2);
252 let lo_bits = (mask & 0x3) ^ 0x3;
253 let hi_bits = ((mask & 0xC) ^ 0xC) << 2;
254 self.clr8_flags |= (hi_bits | lo_bits) << ((i & 1) * 2 + (i & 2) * 4);
255 self.clr8_dist += dist;
260 struct BlockPainter15 {}
261 impl BlockPainter15 {
262 fn new() -> Self { Self{} }
263 fn put_fill(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> u16 {
264 let fill_val = pack_rgb555(bstate.fill_val);
265 for line in dst.chunks_mut(dstride) {
272 fn put_clr2(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> [u16; 2] {
273 let clr2 = [pack_rgb555(bstate.clr2[0]), pack_rgb555(bstate.clr2[1])];
276 if (bstate.clr2_flags & (1 << (i + j * 4))) == 0 {
277 dst[i + j * dstride] = clr2[0];
279 dst[i + j * dstride] = clr2[1];
285 fn put_clr8(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> [[u16; 4]; 4] {
286 let mut clr8 = [[0; 4]; 4];
287 for (dst, src) in clr8.iter_mut().zip(bstate.clr8.iter()) {
288 for (dst, &src) in dst.iter_mut().zip(src.iter()) {
289 *dst = pack_rgb555(src);
292 let mut clr8_flags = bstate.clr8_flags;
293 for (j, line) in dst.chunks_mut(dstride).take(4).enumerate() {
294 for (i, el) in line.iter_mut().take(4).enumerate() {
295 let blk_no = (i >> 1) + (j & 2);
296 *el = clr8[blk_no][(!clr8_flags & 1) as usize];
304 struct BlockWriter15 {}
306 fn write_fill(bw: &mut ByteWriter, fill_val: u16) -> EncoderResult<()> {
307 bw.write_u16le(fill_val | 0x8000)?;
310 fn write_clr2(bw: &mut ByteWriter, clr2_flags: u16, clr2: [u16; 2]) -> EncoderResult<()> {
311 bw.write_u16le(clr2_flags)?;
312 bw.write_u16le(clr2[0])?;
313 bw.write_u16le(clr2[1])?;
316 fn write_clr8(bw: &mut ByteWriter, mut clr8_flags: u16, mut clr8: [[u16; 4]; 4]) -> EncoderResult<()> {
317 if (clr8_flags & 0x8000) != 0 {
318 clr8_flags ^= 0xFF00;
322 bw.write_u16le(clr8_flags)?;
323 bw.write_u16le(clr8[0][0] | 0x8000)?;
324 bw.write_u16le(clr8[0][1])?;
325 bw.write_u16le(clr8[1][0])?;
326 bw.write_u16le(clr8[1][1])?;
327 bw.write_u16le(clr8[2][0])?;
328 bw.write_u16le(clr8[2][1])?;
329 bw.write_u16le(clr8[3][0])?;
330 bw.write_u16le(clr8[3][1])?;
335 struct MSVideo1Encoder {
336 stream: Option<NAStreamRef>,
337 pkt: Option<NAPacket>,
338 pool: NAVideoBufferPool<u16>,
339 lastfrm: Option<NAVideoBufferRef<u16>>,
345 impl MSVideo1Encoder {
350 pool: NAVideoBufferPool::new(2),
357 fn get_block(src: &[u16], sstride: usize, buf: &mut [UnpackedPixel; 16]) {
358 for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) {
359 for (dst, src) in dst.iter_mut().zip(line.iter()) {
364 fn write_skips(bw: &mut ByteWriter, skips: usize) -> EncoderResult<()> {
365 bw.write_u16le((skips as u16) | 0x8400)?;
368 fn encode_inter(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, prev_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
369 let (skip_threshold, fill_threshold) = map_quality(quality);
370 let mut is_intra = true;
371 let src = in_frm.get_data();
372 let sstride = in_frm.get_stride(0);
373 let soff = in_frm.get_offset(0);
374 let (w, h) = in_frm.get_dimensions(0);
375 let rsrc = prev_frm.get_data();
376 let rstride = prev_frm.get_stride(0);
377 let roff = prev_frm.get_offset(0);
378 let dstride = cur_frm.get_stride(0);
379 let doff = cur_frm.get_offset(0);
380 let dst = cur_frm.get_data_mut().unwrap();
381 let mut skip_run = 0;
382 let bpainter = BlockPainter15::new();
383 for ((sstrip, rstrip), dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&rsrc[roff..]).chunks(rstride * 4)).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
384 for x in (0..w).step_by(4) {
385 let mut buf = [UnpackedPixel::default(); 16];
386 let mut refbuf = [UnpackedPixel::default(); 16];
387 Self::get_block(&sstrip[x..], sstride, &mut buf);
388 Self::get_block(&rstrip[x..], rstride, &mut refbuf);
390 let mut skip_dist = 0;
391 for (pix, rpix) in buf.iter().zip(refbuf.iter()) {
392 skip_dist += dist_core(*rpix, pix);
394 if skip_dist <= skip_threshold {
397 for (dst, src) in dstrip[x..].chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
398 dst[..4].copy_from_slice(&src[..4]);
400 if skip_run == 1023 {
401 Self::write_skips(bw, skip_run)?;
407 let mut bstate = BlockState::default();
408 bstate.calc_stats(&buf);
410 let dst = &mut dstrip[x..];
411 if skip_dist <= bstate.fill_dist && skip_dist * 2 <= bstate.clr2_dist {
414 for (dst, src) in dst.chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
415 dst[..4].copy_from_slice(&src[..4]);
417 if skip_run == 1023 {
418 Self::write_skips(bw, skip_run)?;
421 } else if bstate.fill_dist <= fill_threshold ||
422 bstate.fill_dist <= bstate.clr2_dist {
423 let fill_val = bpainter.put_fill(&bstate, dst, dstride);
425 Self::write_skips(bw, skip_run)?;
428 BlockWriter15::write_fill(bw, fill_val)?;
429 } else if bstate.clr8_dist < bstate.clr2_dist {
430 let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
432 Self::write_skips(bw, skip_run)?;
435 BlockWriter15::write_clr8(bw, bstate.clr8_flags, clr8)?;
437 let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
439 Self::write_skips(bw, skip_run)?;
442 BlockWriter15::write_clr2(bw, bstate.clr2_flags, clr2)?;
447 Self::write_skips(bw, skip_run)?;
451 } //xxx: something for inter?
454 fn encode_intra(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
455 let (_, fill_threshold) = map_quality(quality);
456 let src = in_frm.get_data();
457 let sstride = in_frm.get_stride(0);
458 let soff = in_frm.get_offset(0);
459 let (w, h) = in_frm.get_dimensions(0);
460 let dstride = cur_frm.get_stride(0);
461 let doff = cur_frm.get_offset(0);
462 let dst = cur_frm.get_data_mut().unwrap();
463 let bpainter = BlockPainter15::new();
464 for (sstrip, dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
465 for x in (0..w).step_by(4) {
466 let mut buf = [UnpackedPixel::default(); 16];
467 Self::get_block(&sstrip[x..], sstride, &mut buf);
468 let mut bstate = BlockState::default();
469 bstate.calc_stats(&buf);
471 let dst = &mut dstrip[x..];
472 if bstate.fill_dist <= fill_threshold ||
473 bstate.fill_dist <= bstate.clr2_dist {
474 let fill_val = bpainter.put_fill(&bstate, dst, dstride);
475 BlockWriter15::write_fill(bw, fill_val)?;
476 } else if bstate.clr8_dist < bstate.clr2_dist {
477 let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
478 BlockWriter15::write_clr8(bw, bstate.clr8_flags, clr8)?;
480 let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
481 BlockWriter15::write_clr2(bw, bstate.clr2_flags, clr2)?;
490 const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton {
491 model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
493 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
494 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }),
495 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }),
497 elem_size: 2, be: false, alpha: false, palette: false };
499 impl NAEncoder for MSVideo1Encoder {
500 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
501 match encinfo.format {
502 NACodecTypeInfo::None => {
503 Ok(EncodeParameters {
504 format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, RGB555_FORMAT)),
505 ..Default::default() })
507 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
508 NACodecTypeInfo::Video(vinfo) => {
509 let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, RGB555_FORMAT);
510 let mut ofmt = *encinfo;
511 ofmt.format = NACodecTypeInfo::Video(outinfo);
516 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
517 match encinfo.format {
518 NACodecTypeInfo::None => Err(EncoderError::FormatError),
519 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
520 NACodecTypeInfo::Video(vinfo) => {
521 if vinfo.format != RGB555_FORMAT {
522 return Err(EncoderError::FormatError);
524 if ((vinfo.width | vinfo.height) & 3) != 0 {
525 return Err(EncoderError::FormatError);
528 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT);
529 let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info), None);
530 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
531 stream.set_num(stream_id as usize);
532 let stream = stream.into_ref();
533 if self.pool.prealloc_video(out_info, 2).is_err() {
534 return Err(EncoderError::AllocError);
537 self.stream = Some(stream.clone());
538 self.quality = encinfo.quality;
544 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
545 let buf = frm.get_buffer();
546 if let Some(ref vbuf) = buf.get_vbuf16() {
547 let mut cur_frm = self.pool.get_free().unwrap();
548 let mut dbuf = Vec::with_capacity(4);
549 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
550 let mut bw = ByteWriter::new(&mut gw);
551 if self.frmcount == 0 {
554 let is_intra = if let Some(ref prev_buf) = self.lastfrm {
555 Self::encode_inter(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)?
557 Self::encode_intra(&mut bw, &mut cur_frm, vbuf, self.quality)?
559 self.lastfrm = Some(cur_frm);
560 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
562 if self.frmcount == self.key_int {
567 Err(EncoderError::InvalidParameters)
570 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
572 std::mem::swap(&mut self.pkt, &mut npkt);
575 fn flush(&mut self) -> EncoderResult<()> {
581 const ENCODER_OPTS: &[NAOptionDefinition] = &[
583 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
584 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
587 impl NAOptionHandler for MSVideo1Encoder {
588 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
589 fn set_options(&mut self, options: &[NAOption]) {
590 for option in options.iter() {
591 for opt_def in ENCODER_OPTS.iter() {
592 if opt_def.check(option).is_ok() {
595 if let NAValue::Int(intval) = option.value {
596 self.key_int = intval as u8;
605 fn query_option_value(&self, name: &str) -> Option<NAValue> {
607 KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
613 pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
614 Box::new(MSVideo1Encoder::new())
619 use nihav_core::codecs::*;
620 use nihav_core::demuxers::*;
621 use nihav_core::muxers::*;
623 use nihav_commonfmt::*;
624 use nihav_codec_support::test::enc_video::*;
625 use super::RGB555_FORMAT;
628 fn test_ms_video1_encoder() {
629 let mut dmx_reg = RegisteredDemuxers::new();
630 generic_register_all_demuxers(&mut dmx_reg);
631 let mut dec_reg = RegisteredDecoders::new();
632 generic_register_all_decoders(&mut dec_reg);
633 ms_register_all_decoders(&mut dec_reg);
634 let mut mux_reg = RegisteredMuxers::new();
635 generic_register_all_muxers(&mut mux_reg);
636 let mut enc_reg = RegisteredEncoders::new();
637 ms_register_all_encoders(&mut enc_reg);
639 // sample: https://samples.mplayerhq.hu/V-codecs/UCOD/TalkingHead_352x288.avi
640 let dec_config = DecoderTestParams {
642 in_name: "assets/Misc/TalkingHead_352x288.avi",
643 stream_type: StreamType::Video,
647 let enc_config = EncoderTestParams {
649 enc_name: "msvideo1",
650 out_name: "msvideo1.avi",
653 let dst_vinfo = NAVideoInfo {
656 format: RGB555_FORMAT,
660 let enc_params = EncodeParameters {
661 format: NACodecTypeInfo::Video(dst_vinfo),
668 //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
669 test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
670 &[0xe570a206, 0xc223063f, 0x44a0b70e, 0xa71fed1f]);