X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-ms%2Fsrc%2Fcodecs%2Fmsvideo1enc.rs;h=9ebc89690bc7adcc089877f89787d5f2fca68b7e;hb=6f2630992fe340ad1a122ec10c649f756e478185;hp=41d5ff7168127b2ed229316a9cdfae3e52ac10cd;hpb=2ff5620166d8ce8b838b251d1fdd8de73f3f857c;p=nihav.git diff --git a/nihav-ms/src/codecs/msvideo1enc.rs b/nihav-ms/src/codecs/msvideo1enc.rs index 41d5ff7..9ebc896 100644 --- a/nihav-ms/src/codecs/msvideo1enc.rs +++ b/nihav-ms/src/codecs/msvideo1enc.rs @@ -6,12 +6,14 @@ use nihav_codec_support::vq::*; struct Pixel16(u16); impl Pixel16 { - fn unpack(&self) -> (u8, u8, u8) { - (((self.0 >> 10) & 0x1F) as u8, ((self.0 >> 5) & 0x1F) as u8, (self.0 & 0x1F) as u8) + fn unpack(self) -> (u16, u16, u16) { + ((self.0 >> 10) & 0x1F, (self.0 >> 5) & 0x1F, self.0 & 0x1F) } - fn pack(r: u8, g: u8, b: u8) -> Self { - Pixel16{ 0: (u16::from(r) << 10) | (u16::from(g) << 5) | u16::from(b) } + fn pack(r: u16, g: u16, b: u16) -> Self { + Pixel16((r << 10) | (g << 5) | b) } + fn invalid() -> Self { Self(0x8000) } + fn is_invalid(self) -> bool { self == Self::invalid() } } impl VQElement for Pixel16 { fn dist(&self, rval: Self) -> u32 { @@ -50,7 +52,8 @@ impl VQElement for Pixel16 { for i in 0..31 { offs[i + 1] = offs[i] + counts[i]; } - let mut dst = vec![Pixel16(0); arr.len()]; + let mut dst = [Pixel16(0); 16]; + assert!(dst.len() >= arr.len()); for pix in arr.iter() { let (r, g, b) = pix.unpack(); let idx = match component { @@ -61,7 +64,8 @@ impl VQElement for Pixel16 { dst[offs[idx]] = *pix; offs[idx] += 1; } - arr.copy_from_slice(dst.as_slice()); + let len = arr.len(); + arr.copy_from_slice(&dst[..len]); } fn max_dist_component(min: &Self, max: &Self) -> usize { let (r0, g0, b0) = max.unpack(); @@ -69,7 +73,7 @@ impl VQElement for Pixel16 { let rd = u32::from(r0) - u32::from(r1); let gd = u32::from(g0) - u32::from(g1); let bd = u32::from(b0) - u32::from(b1); - if rd > gd && rd > bd { + if rd > gd && rd >= bd { 0 } else if bd > rd && bd > gd { 2 @@ -80,26 +84,27 @@ impl VQElement for Pixel16 { } struct Pixel16Sum { - rsum: u64, - gsum: u64, - bsum: u64, - count: u64, + rsum: u16, + gsum: u16, + bsum: u16, + count: u16, } impl VQElementSum for Pixel16Sum { fn zero() -> Self { Pixel16Sum { rsum: 0, gsum: 0, bsum: 0, count: 0 } } fn add(&mut self, rval: Pixel16, count: u64) { let (r, g, b) = rval.unpack(); - self.rsum += u64::from(r) * count; - self.gsum += u64::from(g) * count; - self.bsum += u64::from(b) * count; + let count = count as u16; + self.rsum += r * count; + self.gsum += g * count; + self.bsum += b * count; self.count += count; } fn get_centroid(&self) -> Pixel16 { if self.count != 0 { - let r = ((self.rsum + self.count / 2) / self.count) as u8; - let g = ((self.gsum + self.count / 2) / self.count) as u8; - let b = ((self.bsum + self.count / 2) / self.count) as u8; + let r = (self.rsum + self.count / 2) / self.count; + let g = (self.gsum + self.count / 2) / self.count; + let b = (self.bsum + self.count / 2) / self.count; Pixel16::pack(r, g, b) } else { Pixel16(0x0000) @@ -120,20 +125,42 @@ struct BlockState { } impl BlockState { + fn calc_clrs(buf: &[Pixel16; 16]) -> Option<(Pixel16, Pixel16)> { + let clr0 = buf[0]; + let mut clr1 = Pixel16::invalid(); + for &pix in buf[1..].iter() { + if pix != clr0 && pix != clr1 { + if clr1.is_invalid() { + clr1 = pix; + } else { + return None; + } + } + } + Some((clr0, clr1)) + } fn calc_stats(&mut self, buf: &[Pixel16; 16]) { - let num_cw = quantise_median_cut::(buf, &mut self.clr2); - if num_cw == 1 { - self.fill_val = Pixel16 { 0: buf[0].0 & !0x400 }; - } else { + let mut filled = false; + let mut two_clr = false; + if let Some((clr0, clr1)) = Self::calc_clrs(buf) { + self.clr2[0] = clr0; + self.clr2[1] = if !clr1.is_invalid() { clr1 } else { clr0 }; + if clr0 == clr1 { + self.fill_val = Pixel16(buf[0].0 & !0x400); + filled = true; + } + two_clr = true; + } + self.fill_dist = 0; + if !filled { let mut avg = Pixel16Sum::zero(); for pix in buf.iter() { avg.add(*pix, 1); } - self.fill_val = Pixel16 { 0: avg.get_centroid().0 & !0x400 }; - } - self.fill_dist = 0; - for pix in buf.iter() { - self.fill_dist += pix.dist(self.fill_val); + self.fill_val = Pixel16(avg.get_centroid().0 & !0x400); + for pix in buf.iter() { + self.fill_dist += pix.dist(self.fill_val); + } } if self.fill_dist == 0 { self.clr2_dist = std::u32::MAX; @@ -142,7 +169,21 @@ impl BlockState { } self.clr2_flags = 0u16; - if num_cw == 2 { + if two_clr { + let mut mask = 1; + self.clr2_dist = 0; + for &pix in buf.iter() { + if pix == self.clr2[0] { + self.clr2_flags |= mask; + } else { + } + mask <<= 1; + } + if (self.clr2_flags & 0x8000) != 0 { + self.clr2_flags = !self.clr2_flags; + self.clr2.swap(0, 1); + } + } else if quantise_median_cut::(buf, &mut self.clr2) == 2 { let mut mask = 1; self.clr2_dist = 0; for pix in buf.iter() { @@ -169,7 +210,7 @@ impl BlockState { return; } - self.clr8 = [[Pixel16 { 0: 0}; 2]; 4]; + self.clr8 = [[Pixel16(0); 2]; 4]; self.clr8_flags = 0; self.clr8_dist = 0; let mut mask = 1; @@ -256,6 +297,7 @@ struct MSVideo1Encoder { lastfrm: Option>, quality: u8, frmcount: u8, + key_int: u8, } impl MSVideo1Encoder { @@ -267,12 +309,13 @@ impl MSVideo1Encoder { lastfrm: None, quality: 0, frmcount: 0, + key_int: 25, } } fn get_block(src: &[u16], sstride: usize, buf: &mut [Pixel16; 16]) { for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) { for i in 0..4 { - dst[i] = Pixel16 { 0: line[i] }; + dst[i] = Pixel16(line[i]); } } } @@ -403,11 +446,11 @@ impl NAEncoder for MSVideo1Encoder { 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, RGB555_FORMAT)); - Ok(ofmt) + Ok(EncodeParameters { + format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, RGB555_FORMAT)), + ..Default::default() }) }, - NACodecTypeInfo::Audio(_) => return Err(EncoderError::FormatError), + NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), NACodecTypeInfo::Video(vinfo) => { let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, RGB555_FORMAT); let mut ofmt = *encinfo; @@ -429,17 +472,17 @@ impl NAEncoder for MSVideo1Encoder { } let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT); - let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info.clone()), None); - let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den); + let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info), None); + let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); stream.set_num(stream_id as usize); let stream = stream.into_ref(); - if let Err(_) = self.pool.prealloc_video(out_info, 2) { + if self.pool.prealloc_video(out_info, 2).is_err() { return Err(EncoderError::AllocError); } self.stream = Some(stream.clone()); self.quality = encinfo.quality; - + Ok(stream) }, } @@ -462,7 +505,7 @@ impl NAEncoder for MSVideo1Encoder { self.lastfrm = Some(cur_frm); self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf)); self.frmcount += 1; - if self.frmcount == 25 { + if self.frmcount == self.key_int { self.frmcount = 0; } Ok(()) @@ -481,10 +524,36 @@ impl NAEncoder for MSVideo1Encoder { } } +const ENCODER_OPTS: &[NAOptionDefinition] = &[ + NAOptionDefinition { + name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC, + opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, +]; + impl NAOptionHandler for MSVideo1Encoder { - fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } - fn set_options(&mut self, _options: &[NAOption]) { } - fn query_option_value(&self, _name: &str) -> Option { None } + fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } + fn set_options(&mut self, options: &[NAOption]) { + for option in options.iter() { + for opt_def in ENCODER_OPTS.iter() { + if opt_def.check(option).is_ok() { + match option.name { + KEYFRAME_OPTION => { + if let NAValue::Int(intval) = option.value { + self.key_int = intval as u8; + } + }, + _ => {}, + }; + } + } + } + } + fn query_option_value(&self, name: &str) -> Option { + match name { + KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))), + _ => None, + } + } } pub fn get_encoder() -> Box { @@ -506,18 +575,19 @@ mod test { let mut dmx_reg = RegisteredDemuxers::new(); generic_register_all_demuxers(&mut dmx_reg); let mut dec_reg = RegisteredDecoders::new(); - generic_register_all_codecs(&mut dec_reg); - ms_register_all_codecs(&mut dec_reg); + generic_register_all_decoders(&mut dec_reg); + ms_register_all_decoders(&mut dec_reg); let mut mux_reg = RegisteredMuxers::new(); generic_register_all_muxers(&mut mux_reg); let mut enc_reg = RegisteredEncoders::new(); ms_register_all_encoders(&mut enc_reg); + // sample: https://samples.mplayerhq.hu/V-codecs/UCOD/TalkingHead_352x288.avi let dec_config = DecoderTestParams { demuxer: "avi", in_name: "assets/Misc/TalkingHead_352x288.avi", stream_type: StreamType::Video, - limit: Some(32), + limit: Some(3), dmx_reg, dec_reg, }; let enc_config = EncoderTestParams { @@ -531,6 +601,7 @@ mod test { height: 0, format: RGB555_FORMAT, flipped: true, + bits: 16, }; let enc_params = EncodeParameters { format: NACodecTypeInfo::Video(dst_vinfo), @@ -540,6 +611,8 @@ mod test { tb_den: 0, flags: 0, }; - test_encoding_to_file(&dec_config, &enc_config, enc_params); + //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]); + test_encoding_md5(&dec_config, &enc_config, enc_params, &[], + &[0x6921e67e, 0x4f2ada95, 0x009ffc62, 0xd4bfab6a]); } }