X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-ms%2Fsrc%2Fcodecs%2Fmsvideo1enc.rs;h=4507e08ec7bfff1f7d3251d4d059b29c494a2bc1;hb=2757a0289c980aeba002609c777815b51ace8e56;hp=9ebc89690bc7adcc089877f89787d5f2fca68b7e;hpb=6f2630992fe340ad1a122ec10c649f756e478185;p=nihav.git diff --git a/nihav-ms/src/codecs/msvideo1enc.rs b/nihav-ms/src/codecs/msvideo1enc.rs index 9ebc896..4507e08 100644 --- a/nihav-ms/src/codecs/msvideo1enc.rs +++ b/nihav-ms/src/codecs/msvideo1enc.rs @@ -1,165 +1,297 @@ use nihav_core::codecs::*; use nihav_core::io::byteio::*; -use nihav_codec_support::vq::*; -#[derive(Default,Clone,Copy,PartialEq)] -struct Pixel16(u16); +type UnpackedPixel = [u16; 4]; -impl Pixel16 { - fn unpack(self) -> (u16, u16, u16) { - ((self.0 >> 10) & 0x1F, (self.0 >> 5) & 0x1F, self.0 & 0x1F) +fn map_quality_pal(quality: u8) -> (u32, u32) { + if quality == 0 { + (0, 0) + } else { + let skip_threshold = (10 - (u32::from(quality) / 10).min(10)) * (8 << 6); + let fill_threshold = (10 - (u32::from(quality) / 10).min(10)) * (16 << 6); + (skip_threshold, fill_threshold) } - fn pack(r: u16, g: u16, b: u16) -> Self { - Pixel16((r << 10) | (g << 5) | b) +} + +fn map_quality_15bit(quality: u8) -> (u32, u32) { + if quality == 0 { + (0, 0) + } else { + let skip_threshold = (10 - (u32::from(quality) / 10).min(10)) * 8; + let fill_threshold = (10 - (u32::from(quality) / 10).min(10)) * 16; + (skip_threshold, fill_threshold) + } +} + +trait PixelOps { + fn unpack(&self) -> UnpackedPixel; + fn dist(&self, val: T) -> u32 { + dist_core(self.unpack(), &val.unpack()) + } +} + +impl PixelOps for u16 { + fn unpack(&self) -> UnpackedPixel { + let val = *self; + let r = (val >> 10) & 0x1F; + let g = (val >> 5) & 0x1F; + let b = val & 0x1F; + [r, g, b, rgb2y(r, g, b)] + } +} + +fn dist_core(val: UnpackedPixel, other: &UnpackedPixel) -> u32 { + let sum = val.iter().zip(other.iter()).take(3).fold(0i32, + |acc, (&a, &b)| { + let diff = i32::from(a) - i32::from(b); + acc + diff * diff + }); + sum as u32 +} + +fn find_nearest(pix: UnpackedPixel, pal: &[UnpackedPixel; 256]) -> usize { + let mut bestidx = 0; + let mut bestdist = std::u32::MAX; + + for (idx, entry) in pal.iter().enumerate() { + let dist = dist_core(pix, entry); + if dist == 0 { + return idx; + } + if bestdist > dist { + bestdist = dist; + bestidx = idx; + } } - fn invalid() -> Self { Self(0x8000) } - fn is_invalid(self) -> bool { self == Self::invalid() } + bestidx +} + +struct LocalSearch { + pal: [UnpackedPixel; 256], + db: Vec>, } -impl VQElement for Pixel16 { - fn dist(&self, rval: Self) -> u32 { - let (r0, g0, b0) = self.unpack(); - let (r1, g1, b1) = rval.unpack(); - let rd = i32::from(r0) - i32::from(r1); - let gd = i32::from(g0) - i32::from(g1); - let bd = i32::from(b0) - i32::from(b1); - (rd * rd + gd * gd + bd * bd) as u32 - } - fn min_cw() -> Self { Pixel16(0x0000) } - fn max_cw() -> Self { Pixel16(0x7FFF) } - fn min(&self, rval: Self) -> Self { - let (r0, g0, b0) = self.unpack(); - let (r1, g1, b1) = rval.unpack(); - Self::pack(r0.min(r1), g0.min(g1), b0.min(b1)) - } - fn max(&self, rval: Self) -> Self { - let (r0, g0, b0) = self.unpack(); - let (r1, g1, b1) = rval.unpack(); - Self::pack(r0.max(r1), g0.max(g1), b0.max(b1)) - } - fn num_components() -> usize { 3 } - fn sort_by_component(arr: &mut [Self], component: usize) { - let mut counts = [0; 32]; - for pix in arr.iter() { - let (r, g, b) = pix.unpack(); - let idx = match component { - 0 => r, - 1 => g, - _ => b, - } as usize; - counts[idx] += 1; - } - let mut offs = [0; 32]; - for i in 0..31 { - offs[i + 1] = offs[i] + counts[i]; - } - 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 { - 0 => r, - 1 => g, - _ => b, - } as usize; - dst[offs[idx]] = *pix; - offs[idx] += 1; - } - 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 (r1, g1, b1) = min.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 { - 0 - } else if bd > rd && bd > gd { - 2 + +impl LocalSearch { + fn quant(key: UnpackedPixel) -> usize { + (((key[0] >> 3) as usize) << 10) | + (((key[1] >> 3) as usize) << 5) | + ((key[2] >> 3) as usize) + } + fn new() -> Self { + let mut db = Vec::with_capacity(1 << 15); + for _ in 0..(1 << 15) { + db.push(Vec::new()); + } + Self { + pal: [UnpackedPixel::default(); 256], + db + } + } + fn recalculate(&mut self, pal: &[UnpackedPixel; 256]) { + self.pal = *pal; + for vec in self.db.iter_mut() { + vec.clear(); + } + for (i, palentry) in pal.iter().enumerate() { + let r0 = (palentry[0] >> 3) as usize; + let g0 = (palentry[1] >> 3) as usize; + let b0 = (palentry[2] >> 3) as usize; + for r in r0.saturating_sub(1)..=(r0 + 1).min(31) { + for g in g0.saturating_sub(1)..=(g0 + 1).min(31) { + for b in b0.saturating_sub(1)..=(b0 + 1).min(31) { + let idx = (r << 10) | (g << 5) | b; + self.db[idx].push([palentry[0], palentry[1], palentry[2], i as u16]); + } + } + } + } + } + fn search(&self, pix: UnpackedPixel) -> usize { + let idx = Self::quant(pix); + let mut best_dist = std::u32::MAX; + let mut best_idx = 0; + let mut count = 0; + for clr in self.db[idx].iter() { + let dist = dist_core(pix, clr); + count += 1; + if best_dist > dist { + best_dist = dist; + best_idx = clr[3] as usize; + if dist == 0 { break; } + } + } + if count > 0 { + best_idx } else { - 1 + find_nearest(pix, &self.pal) } } } -struct Pixel16Sum { - rsum: u16, - gsum: u16, - bsum: u16, - count: u16, +fn rgb2y(r: u16, g: u16, b: u16) -> u16 { + (r * 77 + g * 150 + b * 29) >> 8 +} + +fn pack_rgb555(val: UnpackedPixel) -> u16 { + (val[0] << 10) | (val[1] << 5) | val[2] } -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(); - 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; - let g = (self.gsum + self.count / 2) / self.count; - let b = (self.bsum + self.count / 2) / self.count; - Pixel16::pack(r, g, b) +#[derive(Default)] +struct PixelAverage { + sum: UnpackedPixel, + count: u16, +} + +impl PixelAverage { + fn new() -> Self { Self::default() } + fn add(&mut self, val: &UnpackedPixel) { + for (dst, &src) in self.sum.iter_mut().zip(val.iter()) { + *dst += src; + } + self.count += 1; + } + fn get_avg(&self) -> UnpackedPixel { + if self.count > 0 { + let mut ret = self.sum; + for el in ret.iter_mut() { + *el /= self.count; + } + ret } else { - Pixel16(0x0000) + [0; 4] } } } +macro_rules! quant_template { + ($name:ident, $N:expr) => { + fn $name(pix: &[UnpackedPixel; $N]) -> ([UnpackedPixel; 2], u16, u32) { + let mut avg = PixelAverage::new(); + let mut maxv = [0; 4]; + let mut minv = [255; 4]; + for src in pix.iter() { + avg.add(src); + for ((maxv, minv), &comp) in maxv.iter_mut().zip(minv.iter_mut()).zip(src.iter()) { + *maxv = (*maxv).max(comp); + *minv = (*minv).min(comp); + } + } + let avg = avg.get_avg(); + + let mut best_axis = 3; + let mut best_dist = maxv[3] - minv[3]; + for (comp_no, (&minval, &maxval)) in minv.iter().zip(maxv.iter()).enumerate().take(3) { + if maxval - minval > best_dist { + best_axis = comp_no; + best_dist = maxval - minval; + } + } + if best_dist == 0 { + let mut dist = 0; + for el in pix.iter() { + dist += dist_core(avg, el); + } + return ([avg; 2], 0, dist); + } + + let mut avg1 = PixelAverage::new(); + let mut avg2 = PixelAverage::new(); + let mut mask = 0; + let mut mask_bit = 1; + for clr in pix.iter() { + if clr[best_axis] > avg[best_axis] { + avg2.add(clr); + } else { + avg1.add(clr); + mask |= mask_bit; + } + mask_bit <<= 1; + } + + let clr0 = avg1.get_avg(); + let clr1 = avg2.get_avg(); + let mut dist = 0; + for clr in pix.iter() { + let dist0 = dist_core(clr0, clr); + let dist1 = dist_core(clr1, clr); + dist += dist0.min(dist1); + } + ([clr0, clr1], mask, dist) + } + } +} + +quant_template!(quant2_16pix, 16); +quant_template!(quant2_4pix, 4); + #[derive(Default)] struct BlockState { fill_dist: u32, - fill_val: Pixel16, + fill_val: UnpackedPixel, clr2_dist: u32, clr2_flags: u16, - clr2: [Pixel16; 2], + clr2: [UnpackedPixel; 2], clr8_dist: u32, clr8_flags: u16, - clr8: [[Pixel16; 2]; 4], + clr8: [[UnpackedPixel; 2]; 4], + pal_mode: bool, } impl BlockState { - fn calc_clrs(buf: &[Pixel16; 16]) -> Option<(Pixel16, Pixel16)> { + fn new_pal() -> Self { Self { pal_mode: true, ..Default::default() } } + fn set_fill_val(&mut self, val: UnpackedPixel) { + self.fill_val = val; + if !self.pal_mode { + self.fill_val[0] &= !1; + } + } + fn calc_clrs(buf: &[UnpackedPixel; 16]) -> (Option, Option) { let clr0 = buf[0]; - let mut clr1 = Pixel16::invalid(); + let mut clr1 = clr0; + let mut single = true; for &pix in buf[1..].iter() { - if pix != clr0 && pix != clr1 { - if clr1.is_invalid() { + if pix != clr0 { + if single { clr1 = pix; - } else { - return None; + single = false; + } else if pix != clr1 { + return (None, None); } } } - Some((clr0, clr1)) + if !single { + (Some(clr0), Some(clr1)) + } else { + (Some(clr0), None) + } } - fn calc_stats(&mut self, buf: &[Pixel16; 16]) { + fn calc_stats(&mut self, buf: &[UnpackedPixel; 16]) { 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); + match Self::calc_clrs(buf) { + (Some(clr0), Some(clr1)) => { + self.clr2[0] = clr0; + self.clr2[1] = clr1; + two_clr = true; + }, + (Some(clr0), None) => { + self.clr2[0] = clr0; + self.clr2[1] = clr0; + self.set_fill_val(buf[0]); filled = true; - } - two_clr = true; - } + two_clr = true; + }, + _ => {}, + }; self.fill_dist = 0; if !filled { - let mut avg = Pixel16Sum::zero(); + let mut avg = PixelAverage::new(); for pix in buf.iter() { - avg.add(*pix, 1); + avg.add(pix); } - self.fill_val = Pixel16(avg.get_centroid().0 & !0x400); + self.set_fill_val(avg.get_avg()); for pix in buf.iter() { - self.fill_dist += pix.dist(self.fill_val); + self.fill_dist += dist_core(self.fill_val, pix); } } if self.fill_dist == 0 { @@ -183,109 +315,201 @@ impl BlockState { 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() { - let dist0 = pix.dist(self.clr2[0]); - let dist1 = pix.dist(self.clr2[1]); - if dist0 < dist1 { - self.clr2_flags |= mask; - self.clr2_dist += dist0; - } else { - self.clr2_dist += dist1; - } - mask <<= 1; - } + } else { + let (clrs, mask, dist) = quant2_16pix(&buf); + self.clr2 = clrs; + self.clr2_flags = mask; + self.clr2_dist = dist; if (self.clr2_flags & 0x8000) != 0 { self.clr2_flags = !self.clr2_flags; self.clr2.swap(0, 1); } - } else { - self.clr2_dist = self.fill_dist; - self.clr2 = [self.fill_val; 2]; } if self.clr2_dist == 0 { self.clr8_dist = std::u32::MAX; return; } - self.clr8 = [[Pixel16(0); 2]; 4]; + self.clr8 = [[UnpackedPixel::default(); 2]; 4]; self.clr8_flags = 0; self.clr8_dist = 0; - let mut mask = 1; for i in 0..4 { let off = (i & 1) * 2 + (i & 2) * 4; let src2 = [buf[off], buf[off + 1], buf[off + 4], buf[off + 5]]; - let nc = quantise_median_cut::(&src2, &mut self.clr8[i]); - if nc < 2 { - self.clr8[i][1] = self.clr8[i][0]; - } - for j in 0..4 { - let dist0 = src2[j].dist(self.clr8[i][0]); - let dist1 = src2[j].dist(self.clr8[i][1]); - if dist0 < dist1 { - self.clr8_flags |= mask; - self.clr8_dist += dist0; + let (clrs, mask, dist) = quant2_4pix(&src2); + self.clr8[i] = clrs; + let lo_bits = mask & 0x3; + let hi_bits = (mask & 0xC) << 2; + self.clr8_flags |= (hi_bits | lo_bits) << ((i & 1) * 2 + (i & 2) * 4); + self.clr8_dist += dist; + } + } +} + +struct BlockPainterPal<'a> { + ls: &'a LocalSearch, +} +impl<'a> BlockPainterPal<'a> { + fn new(ls: &'a LocalSearch) -> Self { Self{ ls } } + fn find_index(&self, pix: UnpackedPixel) -> u8 { self.ls.search(pix) as u8 } + fn put_fill(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> u8 { + let fill_val = self.find_index(bstate.fill_val); + for line in dst.chunks_mut(dstride) { + for i in 0..4 { + line[i] = fill_val; + } + } + fill_val + } + fn put_clr2(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> [u8; 2] { + let clr2 = [self.find_index(bstate.clr2[0]), self.find_index(bstate.clr2[1])]; + for j in 0..4 { + for i in 0..4 { + if (bstate.clr2_flags & (1 << (i + j * 4))) == 0 { + dst[i + j * dstride] = clr2[0]; } else { - self.clr8_dist += dist1; + dst[i + j * dstride] = clr2[1]; } - mask <<= 1; } } - if (self.clr8_flags & 0x8000) != 0 { - self.clr8_flags ^= 0xF000; - self.clr8[3].swap(0, 1); + clr2 + } + fn put_clr8(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> [[u8; 4]; 4] { + let mut clr8 = [[0; 4]; 4]; + for (dst, src) in clr8.iter_mut().zip(bstate.clr8.iter()) { + for (dst, &src) in dst.iter_mut().zip(src.iter()) { + *dst = self.find_index(src); + } + } + let mut clr8_flags = bstate.clr8_flags; + let swap = (clr8_flags & 0x8000) == 0; + if swap { + clr8_flags ^= 0xFF00; + } + if clr8_flags < 0x9000 { + clr8_flags |= 0x1000; + } + if swap { + clr8_flags ^= 0xFF00; + } + for (j, line) in dst.chunks_mut(dstride).take(4).enumerate() { + for (i, el) in line.iter_mut().take(4).enumerate() { + let blk_no = (i >> 1) + (j & 2); + *el = clr8[blk_no][(!clr8_flags & 1) as usize]; + clr8_flags >>= 1; + } + } + clr8 + } +} + +struct BlockWriterPal {} +impl BlockWriterPal { + fn write_fill(bw: &mut ByteWriter, fill_val: u8) -> EncoderResult<()> { + bw.write_byte(fill_val)?; + bw.write_byte(0x80)?; + Ok(()) + } + fn write_clr2(bw: &mut ByteWriter, clr2_flags: u16, clr2: [u8; 2]) -> EncoderResult<()> { + bw.write_u16le(clr2_flags)?; + bw.write_byte(clr2[0])?; + bw.write_byte(clr2[1])?; + Ok(()) + } + fn write_clr8(bw: &mut ByteWriter, mut clr8_flags: u16, mut clr8: [[u8; 4]; 4]) -> EncoderResult<()> { + if (clr8_flags & 0x8000) == 0 { + clr8_flags ^= 0xFF00; + clr8[2].swap(0, 1); + clr8[3].swap(0, 1); + } + if clr8_flags < 0x9000 { + clr8_flags |= 0x1000; } + + bw.write_u16le(clr8_flags)?; + bw.write_byte(clr8[0][0])?; + bw.write_byte(clr8[0][1])?; + bw.write_byte(clr8[1][0])?; + bw.write_byte(clr8[1][1])?; + bw.write_byte(clr8[2][0])?; + bw.write_byte(clr8[2][1])?; + bw.write_byte(clr8[3][0])?; + bw.write_byte(clr8[3][1])?; + Ok(()) } - fn put_fill(&self, dst: &mut [u16], dstride: usize) { +} + +struct BlockPainter15 {} +impl BlockPainter15 { + fn new() -> Self { Self{} } + fn put_fill(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> u16 { + let fill_val = pack_rgb555(bstate.fill_val); for line in dst.chunks_mut(dstride) { for i in 0..4 { - line[i] = self.fill_val.0; + line[i] = fill_val; } } + fill_val } - fn put_clr2(&self, dst: &mut [u16], dstride: usize) { + fn put_clr2(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> [u16; 2] { + let clr2 = [pack_rgb555(bstate.clr2[0]), pack_rgb555(bstate.clr2[1])]; for j in 0..4 { for i in 0..4 { - if (self.clr2_flags & (1 << (i + j * 4))) == 0 { - dst[i + j * dstride] = self.clr2[0].0; + if (bstate.clr2_flags & (1 << (i + j * 4))) == 0 { + dst[i + j * dstride] = clr2[0]; } else { - dst[i + j * dstride] = self.clr2[1].0; + dst[i + j * dstride] = clr2[1]; } } } + clr2 } - fn put_clr8(&self, dst: &mut [u16], dstride: usize) { - for i in 0..4 { - let off = (i & 1) * 2 + (i & 2) * dstride; - let cur_flg = (self.clr8_flags >> (i * 4)) & 0xF; - dst[off] = self.clr8[i][( !cur_flg & 1) as usize].0; - dst[off + 1] = self.clr8[i][((!cur_flg >> 1) & 1) as usize].0; - dst[off + dstride] = self.clr8[i][((!cur_flg >> 2) & 1) as usize].0; - dst[off + 1 + dstride] = self.clr8[i][((!cur_flg >> 3) & 1) as usize].0; + fn put_clr8(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> [[u16; 4]; 4] { + let mut clr8 = [[0; 4]; 4]; + for (dst, src) in clr8.iter_mut().zip(bstate.clr8.iter()) { + for (dst, &src) in dst.iter_mut().zip(src.iter()) { + *dst = pack_rgb555(src); + } + } + let mut clr8_flags = bstate.clr8_flags; + for (j, line) in dst.chunks_mut(dstride).take(4).enumerate() { + for (i, el) in line.iter_mut().take(4).enumerate() { + let blk_no = (i >> 1) + (j & 2); + *el = clr8[blk_no][(!clr8_flags & 1) as usize]; + clr8_flags >>= 1; + } } + clr8 } - fn write_fill(&self, bw: &mut ByteWriter) -> EncoderResult<()> { - bw.write_u16le(self.fill_val.0 | 0x8000)?; +} + +struct BlockWriter15 {} +impl BlockWriter15 { + fn write_fill(bw: &mut ByteWriter, fill_val: u16) -> EncoderResult<()> { + bw.write_u16le(fill_val | 0x8000)?; Ok(()) } - fn write_clr2(&self, bw: &mut ByteWriter) -> EncoderResult<()> { - bw.write_u16le(self.clr2_flags)?; - bw.write_u16le(self.clr2[0].0)?; - bw.write_u16le(self.clr2[1].0)?; + fn write_clr2(bw: &mut ByteWriter, clr2_flags: u16, clr2: [u16; 2]) -> EncoderResult<()> { + bw.write_u16le(clr2_flags)?; + bw.write_u16le(clr2[0])?; + bw.write_u16le(clr2[1])?; Ok(()) } - fn write_clr8(&self, bw: &mut ByteWriter) -> EncoderResult<()> { - bw.write_u16le(self.clr8_flags)?; - bw.write_u16le(self.clr8[0][0].0 | 0x8000)?; - bw.write_u16le(self.clr8[0][1].0)?; - bw.write_u16le(self.clr8[1][0].0)?; - bw.write_u16le(self.clr8[1][1].0)?; - bw.write_u16le(self.clr8[2][0].0)?; - bw.write_u16le(self.clr8[2][1].0)?; - bw.write_u16le(self.clr8[3][0].0)?; - bw.write_u16le(self.clr8[3][1].0)?; + fn write_clr8(bw: &mut ByteWriter, mut clr8_flags: u16, mut clr8: [[u16; 4]; 4]) -> EncoderResult<()> { + if (clr8_flags & 0x8000) != 0 { + clr8_flags ^= 0xFF00; + clr8[2].swap(0, 1); + clr8[3].swap(0, 1); + } + bw.write_u16le(clr8_flags)?; + bw.write_u16le(clr8[0][0] | 0x8000)?; + bw.write_u16le(clr8[0][1])?; + bw.write_u16le(clr8[1][0])?; + bw.write_u16le(clr8[1][1])?; + bw.write_u16le(clr8[2][0])?; + bw.write_u16le(clr8[2][1])?; + bw.write_u16le(clr8[3][0])?; + bw.write_u16le(clr8[3][1])?; Ok(()) } } @@ -293,11 +517,17 @@ impl BlockState { struct MSVideo1Encoder { stream: Option, pkt: Option, - pool: NAVideoBufferPool, - lastfrm: Option>, + pool8: NAVideoBufferPool, + pool15: NAVideoBufferPool, + lastfrm8: Option>, + lastfrm15: Option>, quality: u8, frmcount: u8, key_int: u8, + + pal_mode: bool, + pal: [UnpackedPixel; 256], + ls: LocalSearch, } impl MSVideo1Encoder { @@ -305,17 +535,30 @@ impl MSVideo1Encoder { Self { stream: None, pkt: None, - pool: NAVideoBufferPool::new(2), - lastfrm: None, + pool8: NAVideoBufferPool::new(2), + pool15: NAVideoBufferPool::new(2), + lastfrm8: None, + lastfrm15: None, quality: 0, frmcount: 0, key_int: 25, + + pal_mode: false, + pal: [UnpackedPixel::default(); 256], + ls: LocalSearch::new(), } } - fn get_block(src: &[u16], sstride: usize, buf: &mut [Pixel16; 16]) { + fn get_block(src: &[u16], sstride: usize, buf: &mut [UnpackedPixel; 16]) { for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) { - for i in 0..4 { - dst[i] = Pixel16(line[i]); + for (dst, src) in dst.iter_mut().zip(line.iter()) { + *dst = src.unpack(); + } + } + } + fn get_block8(src: &[u8], sstride: usize, buf: &mut [UnpackedPixel; 16], pal: &[UnpackedPixel; 256]) { + for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) { + for (dst, src) in dst.iter_mut().zip(line.iter()) { + *dst = pal[usize::from(*src)]; } } } @@ -323,7 +566,8 @@ impl MSVideo1Encoder { bw.write_u16le((skips as u16) | 0x8400)?; Ok(()) } - fn encode_inter(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer, in_frm: &NAVideoBuffer, prev_frm: &NAVideoBuffer, _quality: u8) -> EncoderResult { + fn encode_inter_rgb555(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer, in_frm: &NAVideoBuffer, prev_frm: &NAVideoBuffer, quality: u8) -> EncoderResult { + let (skip_threshold, fill_threshold) = map_quality_15bit(quality); let mut is_intra = true; let src = in_frm.get_data(); let sstride = in_frm.get_stride(0); @@ -336,20 +580,24 @@ impl MSVideo1Encoder { let doff = cur_frm.get_offset(0); let dst = cur_frm.get_data_mut().unwrap(); let mut skip_run = 0; + let bpainter = BlockPainter15::new(); 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)) { for x in (0..w).step_by(4) { - let mut buf = [Pixel16::min_cw(); 16]; - let mut refbuf = [Pixel16::min_cw(); 16]; + let mut buf = [UnpackedPixel::default(); 16]; + let mut refbuf = [UnpackedPixel::default(); 16]; Self::get_block(&sstrip[x..], sstride, &mut buf); Self::get_block(&rstrip[x..], rstride, &mut refbuf); let mut skip_dist = 0; for (pix, rpix) in buf.iter().zip(refbuf.iter()) { - skip_dist += pix.dist(*rpix); + skip_dist += dist_core(*rpix, pix); } - if skip_dist == 0 { + if skip_dist <= skip_threshold { skip_run += 1; is_intra = false; + for (dst, src) in dstrip[x..].chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) { + dst[..4].copy_from_slice(&src[..4]); + } if skip_run == 1023 { Self::write_skips(bw, skip_run)?; skip_run = 0; @@ -361,34 +609,38 @@ impl MSVideo1Encoder { bstate.calc_stats(&buf); let dst = &mut dstrip[x..]; - if skip_dist <= bstate.fill_dist { + if skip_dist <= bstate.fill_dist && skip_dist * 2 <= bstate.clr2_dist { skip_run += 1; is_intra = false; + for (dst, src) in dst.chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) { + dst[..4].copy_from_slice(&src[..4]); + } if skip_run == 1023 { Self::write_skips(bw, skip_run)?; skip_run = 0; } - } else if bstate.fill_dist <= bstate.clr2_dist { - bstate.put_fill(dst, dstride); + } else if bstate.fill_dist <= fill_threshold || + bstate.fill_dist <= bstate.clr2_dist { + let fill_val = bpainter.put_fill(&bstate, dst, dstride); if skip_run != 0 { Self::write_skips(bw, skip_run)?; skip_run = 0; } - bstate.write_fill(bw)?; + BlockWriter15::write_fill(bw, fill_val)?; } else if bstate.clr8_dist < bstate.clr2_dist { - bstate.put_clr8(dst, dstride); + let clr8 = bpainter.put_clr8(&bstate, dst, dstride); if skip_run != 0 { Self::write_skips(bw, skip_run)?; skip_run = 0; } - bstate.write_clr8(bw)?; + BlockWriter15::write_clr8(bw, bstate.clr8_flags, clr8)?; } else { - bstate.put_clr2(dst, dstride); + let clr2 = bpainter.put_clr2(&bstate, dst, dstride); if skip_run != 0 { Self::write_skips(bw, skip_run)?; skip_run = 0; } - bstate.write_clr2(bw)?; + BlockWriter15::write_clr2(bw, bstate.clr2_flags, clr2)?; } } } @@ -400,7 +652,8 @@ impl MSVideo1Encoder { } //xxx: something for inter? Ok(is_intra) } - fn encode_intra(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer, in_frm: &NAVideoBuffer, _quality: u8) -> EncoderResult { + fn encode_intra_rgb555(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer, in_frm: &NAVideoBuffer, quality: u8) -> EncoderResult { + let (_, fill_threshold) = map_quality_15bit(quality); let src = in_frm.get_data(); let sstride = in_frm.get_stride(0); let soff = in_frm.get_offset(0); @@ -408,23 +661,145 @@ impl MSVideo1Encoder { let dstride = cur_frm.get_stride(0); let doff = cur_frm.get_offset(0); let dst = cur_frm.get_data_mut().unwrap(); + let bpainter = BlockPainter15::new(); for (sstrip, dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) { for x in (0..w).step_by(4) { - let mut buf = [Pixel16::min_cw(); 16]; + let mut buf = [UnpackedPixel::default(); 16]; Self::get_block(&sstrip[x..], sstride, &mut buf); let mut bstate = BlockState::default(); bstate.calc_stats(&buf); let dst = &mut dstrip[x..]; - if bstate.fill_dist <= bstate.clr2_dist { - bstate.put_fill(dst, dstride); - bstate.write_fill(bw)?; + if bstate.fill_dist <= fill_threshold || + bstate.fill_dist <= bstate.clr2_dist { + let fill_val = bpainter.put_fill(&bstate, dst, dstride); + BlockWriter15::write_fill(bw, fill_val)?; } else if bstate.clr8_dist < bstate.clr2_dist { - bstate.put_clr8(dst, dstride); - bstate.write_clr8(bw)?; + let clr8 = bpainter.put_clr8(&bstate, dst, dstride); + BlockWriter15::write_clr8(bw, bstate.clr8_flags, clr8)?; } else { - bstate.put_clr2(dst, dstride); - bstate.write_clr2(bw)?; + let clr2 = bpainter.put_clr2(&bstate, dst, dstride); + BlockWriter15::write_clr2(bw, bstate.clr2_flags, clr2)?; + } + } + } + bw.write_u16le(0)?; + Ok(true) + } + fn encode_inter_pal(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer, in_frm: &NAVideoBuffer, prev_frm: &NAVideoBuffer, quality: u8, pal: &[UnpackedPixel; 256], ls: &LocalSearch) -> EncoderResult { + let (skip_threshold, fill_threshold) = map_quality_pal(quality); + let mut is_intra = true; + let src = in_frm.get_data(); + let sstride = in_frm.get_stride(0); + let soff = in_frm.get_offset(0); + let (w, h) = in_frm.get_dimensions(0); + let rsrc = prev_frm.get_data(); + let rstride = prev_frm.get_stride(0); + let roff = prev_frm.get_offset(0); + let dstride = cur_frm.get_stride(0); + let doff = cur_frm.get_offset(0); + let dst = cur_frm.get_data_mut().unwrap(); + let mut skip_run = 0; + let bpainter = BlockPainterPal::new(ls); + 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)) { + for x in (0..w).step_by(4) { + let mut buf = [UnpackedPixel::default(); 16]; + let mut refbuf = [UnpackedPixel::default(); 16]; + Self::get_block8(&sstrip[x..], sstride, &mut buf, pal); + Self::get_block8(&rstrip[x..], rstride, &mut refbuf, pal); + + let mut skip_dist = 0; + for (pix, rpix) in buf.iter().zip(refbuf.iter()) { + skip_dist += dist_core(*rpix, pix); + } + if skip_dist <= skip_threshold { + skip_run += 1; + is_intra = false; + for (dst, src) in dstrip[x..].chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) { + dst[..4].copy_from_slice(&src[..4]); + } + if skip_run == 1023 { + Self::write_skips(bw, skip_run)?; + skip_run = 0; + } + continue; + } + + let mut bstate = BlockState::new_pal(); + bstate.calc_stats(&buf); + + let dst = &mut dstrip[x..]; + if skip_dist <= bstate.fill_dist && skip_dist * 2 <= bstate.clr2_dist { + skip_run += 1; + is_intra = false; + for (dst, src) in dst.chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) { + dst[..4].copy_from_slice(&src[..4]); + } + if skip_run == 1023 { + Self::write_skips(bw, skip_run)?; + skip_run = 0; + } + } else if bstate.fill_dist <= fill_threshold || + bstate.fill_dist <= bstate.clr2_dist { + let fill_val = bpainter.put_fill(&bstate, dst, dstride); + if skip_run != 0 { + Self::write_skips(bw, skip_run)?; + skip_run = 0; + } + BlockWriterPal::write_fill(bw, fill_val)?; + } else if bstate.clr8_dist < bstate.clr2_dist { + let clr8 = bpainter.put_clr8(&bstate, dst, dstride); + if skip_run != 0 { + Self::write_skips(bw, skip_run)?; + skip_run = 0; + } + BlockWriterPal::write_clr8(bw, bstate.clr8_flags, clr8)?; + } else { + let clr2 = bpainter.put_clr2(&bstate, dst, dstride); + if skip_run != 0 { + Self::write_skips(bw, skip_run)?; + skip_run = 0; + } + BlockWriterPal::write_clr2(bw, bstate.clr2_flags, clr2)?; + } + } + } + if skip_run != 0 { + Self::write_skips(bw, skip_run)?; + } + if is_intra { + bw.write_u16le(0)?; + } //xxx: something for inter? + Ok(is_intra) + } + fn encode_intra_pal(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer, in_frm: &NAVideoBuffer, quality: u8, pal: &[UnpackedPixel; 256], ls: &LocalSearch) -> EncoderResult { + let (_, fill_threshold) = map_quality_pal(quality); + let src = in_frm.get_data(); + let sstride = in_frm.get_stride(0); + let soff = in_frm.get_offset(0); + let (w, h) = in_frm.get_dimensions(0); + let dstride = cur_frm.get_stride(0); + let doff = cur_frm.get_offset(0); + let dst = cur_frm.get_data_mut().unwrap(); + let bpainter = BlockPainterPal::new(ls); + for (sstrip, dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) { + for x in (0..w).step_by(4) { + let mut buf = [UnpackedPixel::default(); 16]; + Self::get_block8(&sstrip[x..], sstride, &mut buf, pal); + let mut bstate = BlockState::new_pal(); + bstate.calc_stats(&buf); + + let dst = &mut dstrip[x..]; + if bstate.fill_dist <= fill_threshold || + bstate.fill_dist <= bstate.clr2_dist { + let fill_val = bpainter.put_fill(&bstate, dst, dstride); + BlockWriterPal::write_fill(bw, fill_val)?; + } else if bstate.clr8_dist < bstate.clr2_dist { + let clr8 = bpainter.put_clr8(&bstate, dst, dstride); + BlockWriterPal::write_clr8(bw, bstate.clr8_flags, clr8)?; + } else { + let clr2 = bpainter.put_clr2(&bstate, dst, dstride); + BlockWriterPal::write_clr2(bw, bstate.clr2_flags, clr2)?; } } } @@ -452,32 +827,42 @@ impl NAEncoder for MSVideo1Encoder { }, NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), NACodecTypeInfo::Video(vinfo) => { - let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, RGB555_FORMAT); + let oformat = if vinfo.format == PAL8_FORMAT { PAL8_FORMAT } else { RGB555_FORMAT }; + let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, oformat); let mut ofmt = *encinfo; ofmt.format = NACodecTypeInfo::Video(outinfo); Ok(ofmt) } } } + fn get_capabilities(&self) -> u64 { ENC_CAPS_SKIPFRAME } fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult { match encinfo.format { NACodecTypeInfo::None => Err(EncoderError::FormatError), NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), NACodecTypeInfo::Video(vinfo) => { - if vinfo.format != RGB555_FORMAT { + if vinfo.format != RGB555_FORMAT && vinfo.format != PAL8_FORMAT { return Err(EncoderError::FormatError); } if ((vinfo.width | vinfo.height) & 3) != 0 { return Err(EncoderError::FormatError); } - let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT); + let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, vinfo.format); 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 self.pool.prealloc_video(out_info, 2).is_err() { - return Err(EncoderError::AllocError); + self.pal_mode = vinfo.format.is_paletted(); + + if !self.pal_mode { + if self.pool15.prealloc_video(out_info, 2).is_err() { + return Err(EncoderError::AllocError); + } + } else { + if self.pool8.prealloc_video(out_info, 2).is_err() { + return Err(EncoderError::AllocError); + } } self.stream = Some(stream.clone()); @@ -489,26 +874,104 @@ impl NAEncoder for MSVideo1Encoder { } fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { let buf = frm.get_buffer(); + if frm.frame_type == FrameType::Skip { + if let Some(ref stream) = self.stream { + let mut dbuf = Vec::with_capacity(4); + let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); + let mut bw = ByteWriter::new(&mut gw); + + let vinfo = stream.get_info().get_properties().get_video_info().unwrap(); + let mut nskips = ((vinfo.get_width() + 3) / 4) * ((vinfo.get_height() + 3) / 4); + while nskips >= 1023 { + Self::write_skips(&mut bw, 1023)?; + nskips -= 1023; + } + if nskips > 0 { + Self::write_skips(&mut bw, nskips)?; + } + self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, false, dbuf)); + self.frmcount += 1; + if self.frmcount == self.key_int { + self.frmcount = 0; + } + return Ok(()); + } else { + return Err(EncoderError::Bug); + } + } if let Some(ref vbuf) = buf.get_vbuf16() { - let mut cur_frm = self.pool.get_free().unwrap(); + if self.pal_mode { + return Err(EncoderError::InvalidParameters); + } + let mut cur_frm = self.pool15.get_free().unwrap(); let mut dbuf = Vec::with_capacity(4); let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); let mut bw = ByteWriter::new(&mut gw); if self.frmcount == 0 { - self.lastfrm = None; + self.lastfrm15 = None; } - let is_intra = if let Some(ref prev_buf) = self.lastfrm { - Self::encode_inter(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)? + let is_intra = if let Some(ref prev_buf) = self.lastfrm15 { + Self::encode_inter_rgb555(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)? } else { - Self::encode_intra(&mut bw, &mut cur_frm, vbuf, self.quality)? + Self::encode_intra_rgb555(&mut bw, &mut cur_frm, vbuf, self.quality)? }; - self.lastfrm = Some(cur_frm); + self.lastfrm15 = Some(cur_frm); self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf)); self.frmcount += 1; if self.frmcount == self.key_int { self.frmcount = 0; } Ok(()) + } else if let Some(ref vbuf) = buf.get_vbuf() { + if !self.pal_mode { + return Err(EncoderError::InvalidParameters); + } + let src = vbuf.get_data(); + let pal = &src[vbuf.get_offset(1)..]; + let mut pal_changed = false; + for (cur_pal, new_pal) in self.pal.iter_mut().zip(pal.chunks_exact(3)) { + let (cur_clr, luma) = cur_pal.split_at_mut(3); + let new_clr = [u16::from(new_pal[0]), u16::from(new_pal[1]), u16::from(new_pal[2])]; + if cur_clr != &new_clr { + pal_changed = true; + cur_clr.copy_from_slice(&new_clr); + luma[0] = rgb2y(cur_clr[0], cur_clr[1], cur_clr[2]); + } + } + + if pal_changed { + self.ls.recalculate(&self.pal); + } + + let mut cur_frm = self.pool8.get_free().unwrap(); + let mut dbuf = Vec::with_capacity(4); + let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); + let mut bw = ByteWriter::new(&mut gw); + if self.frmcount == 0 { + self.lastfrm8 = None; + } + let is_intra = if let Some(ref prev_buf) = self.lastfrm8 { + Self::encode_inter_pal(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality, &self.pal, &self.ls)? + } else { + Self::encode_intra_pal(&mut bw, &mut cur_frm, vbuf, self.quality, &self.pal, &self.ls)? + }; + self.lastfrm8 = Some(cur_frm); + let mut pkt = NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf); + if pal_changed { + let mut epal = [0; 1024]; + for (dst, src) in epal.chunks_mut(4).zip(self.pal.iter()) { + dst[0] = src[0] as u8; + dst[1] = src[1] as u8; + dst[2] = src[2] as u8; + } + pkt.add_side_data(NASideData::Palette(true, Arc::new(epal))); + } + self.pkt = Some(pkt); + self.frmcount += 1; + if self.frmcount == self.key_int { + self.frmcount = 0; + } + Ok(()) } else { Err(EncoderError::InvalidParameters) } @@ -571,7 +1034,53 @@ mod test { use super::RGB555_FORMAT; #[test] - fn test_ms_video1_encoder() { + fn test_ms_video1_encoder_pal() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + 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/RLE/mplayer-msrle-4bit.avi + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/MS/mplayer-msrle-4bit.avi", + stream_type: StreamType::Video, + limit: Some(3), + dmx_reg, dec_reg, + }; + let enc_config = EncoderTestParams { + muxer: "avi", + enc_name: "msvideo1", + out_name: "msvideo1pal.avi", + mux_reg, enc_reg, + }; + let dst_vinfo = NAVideoInfo { + width: 0, + height: 0, + format: PAL8_FORMAT, + flipped: true, + bits: 8, + }; + let enc_params = EncodeParameters { + format: NACodecTypeInfo::Video(dst_vinfo), + quality: 0, + bitrate: 0, + tb_num: 0, + tb_den: 0, + flags: 0, + }; + //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]); + test_encoding_md5(&dec_config, &enc_config, enc_params, &[], + &[0x5afaf853, 0xd53ba9dd, 0x630f53e7, 0x41b33a36]); + } + + #[test] + fn test_ms_video1_encoder_rgb555() { let mut dmx_reg = RegisteredDemuxers::new(); generic_register_all_demuxers(&mut dmx_reg); let mut dec_reg = RegisteredDecoders::new(); @@ -605,7 +1114,7 @@ mod test { }; let enc_params = EncodeParameters { format: NACodecTypeInfo::Video(dst_vinfo), - quality: 0, + quality: 80, bitrate: 0, tb_num: 0, tb_den: 0, @@ -613,6 +1122,6 @@ mod test { }; //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]); test_encoding_md5(&dec_config, &enc_config, enc_params, &[], - &[0x6921e67e, 0x4f2ada95, 0x009ffc62, 0xd4bfab6a]); + &[0xb3175a7b, 0x4a6cb45e, 0x526f3f5d, 0xaa1574cc]); } }