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{ 0: (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 {
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 {
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();
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
}
struct Pixel16Sum {
- rsum: u64,
- gsum: u64,
- bsum: u64,
- count: u64,
+ rsum: u16,
+ gsum: u16,
+ bsum: u16,
+ count: u16,
}
impl VQElementSum<Pixel16> 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)
}
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::<Pixel16, Pixel16Sum>(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 { 0: 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);
+ for pix in buf.iter() {
+ self.fill_dist += pix.dist(self.fill_val);
+ }
}
if self.fill_dist == 0 {
self.clr2_dist = std::u32::MAX;
}
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::<Pixel16, Pixel16Sum>(buf, &mut self.clr2) == 2 {
let mut mask = 1;
self.clr2_dist = 0;
for pix in buf.iter() {
lastfrm: Option<NAVideoBufferRef<u16>>,
quality: u8,
frmcount: u8,
+ key_int: u8,
}
impl MSVideo1Encoder {
lastfrm: None,
quality: 0,
frmcount: 0,
+ key_int: 25,
}
}
fn get_block(src: &[u16], sstride: usize, buf: &mut [Pixel16; 16]) {
ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, RGB555_FORMAT));
Ok(ofmt)
},
- 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;
}
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)
},
}
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(())
}
}
+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<NAValue> { 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<NAValue> {
+ match name {
+ KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
+ _ => None,
+ }
+ }
}
pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
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 {
height: 0,
format: RGB555_FORMAT,
flipped: true,
+ bits: 16,
};
let enc_params = EncodeParameters {
format: NACodecTypeInfo::Video(dst_vinfo),
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]);
}
}