From a178c22cac8a9eea22eba4d2e3ebc34294054c6c Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Wed, 3 Jun 2020 11:45:07 +0200 Subject: [PATCH] experimental Cinepak encoder --- nihav-commonfmt/Cargo.toml | 10 +- nihav-commonfmt/src/codecs/cinepakenc.rs | 1008 ++++++++++++++++++++++ nihav-commonfmt/src/codecs/mod.rs | 16 + nihav-commonfmt/src/lib.rs | 2 + 4 files changed, 1034 insertions(+), 2 deletions(-) create mode 100644 nihav-commonfmt/src/codecs/cinepakenc.rs diff --git a/nihav-commonfmt/Cargo.toml b/nihav-commonfmt/Cargo.toml index 5bd8990..6e572d3 100644 --- a/nihav-commonfmt/Cargo.toml +++ b/nihav-commonfmt/Cargo.toml @@ -12,15 +12,16 @@ path = "../nihav-registry" [dependencies.nihav_codec_support] path = "../nihav-codec-support" -features = ["h263", "mdct", "fft", "dsp_window"] +features = ["h263", "mdct", "fft", "dsp_window", "vq"] [dev-dependencies] nihav_realmedia = { path = "../nihav-realmedia" } [features] -default = ["all_decoders", "all_demuxers", "all_muxers"] +default = ["all_decoders", "all_demuxers", "all_encoders", "all_muxers"] decoders = [] demuxers = [] +encoders = [] muxers = [] all_demuxers = ["demuxer_avi", "demuxer_mov", "demuxer_wav"] demuxer_avi = ["demuxers"] @@ -42,3 +43,8 @@ decoder_ts102366 = ["decoders"] decoder_sipro = ["decoders"] decoder_atrac3 = ["decoders"] decoder_aac = ["decoders"] + +all_encoders = ["all_video_encoders"] + +all_video_encoders = ["encoder_cinepak"] +encoder_cinepak = ["encoders"] diff --git a/nihav-commonfmt/src/codecs/cinepakenc.rs b/nihav-commonfmt/src/codecs/cinepakenc.rs new file mode 100644 index 0000000..d40f504 --- /dev/null +++ b/nihav-commonfmt/src/codecs/cinepakenc.rs @@ -0,0 +1,1008 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; +use nihav_codec_support::vq::*; + +#[derive(Default,Clone,Copy,PartialEq,Debug)] +struct YUVCode { + y: [u8; 4], + u: u8, + v: u8, +} +impl VQElement for YUVCode { + fn dist(&self, rval: Self) -> u32 { + let mut ysum = 0; + for (y0, y1) in self.y.iter().zip(rval.y.iter()) { + let yd = i32::from(*y0) - i32::from(*y1); + ysum += yd * yd; + } + let ud = i32::from(self.u) - i32::from(rval.u); + let vd = i32::from(self.v) - i32::from(rval.v); + (ysum + ud * ud + vd * vd) as u32 + } + fn min_cw() -> Self { YUVCode { y: [0; 4], u: 0, v: 0 } } + fn max_cw() -> Self { YUVCode { y: [255; 4], u: 255, v: 255 } } + fn min(&self, rval: Self) -> Self { + let mut ycode = YUVCode::default(); + for i in 0..4 { + ycode.y[i] = self.y[i].min(rval.y[i]); + } + ycode.u = self.u.min(rval.u); + ycode.v = self.v.min(rval.v); + ycode + } + fn max(&self, rval: Self) -> Self { + let mut ycode = YUVCode::default(); + for i in 0..4 { + ycode.y[i] = self.y[i].max(rval.y[i]); + } + ycode.u = self.u.max(rval.u); + ycode.v = self.v.max(rval.v); + ycode + } + fn num_components() -> usize { 6 } + fn sort_by_component(arr: &mut [Self], component: usize) { + let mut counts = [0; 256]; + for entry in arr.iter() { + let idx = match component { + 0 | 1 | 2 | 3 => entry.y[component], + 4 => entry.u, + _ => entry.v, + } as usize; + counts[idx] += 1; + } + let mut offs = [0; 256]; + for i in 0..255 { + offs[i + 1] = offs[i] + counts[i]; + } + let mut dst = vec![YUVCode::default(); arr.len()]; + for entry in arr.iter() { + let idx = match component { + 0 | 1 | 2 | 3 => entry.y[component], + 4 => entry.u, + _ => entry.v, + } as usize; + dst[offs[idx]] = *entry; + offs[idx] += 1; + } + arr.copy_from_slice(dst.as_slice()); + } + fn max_dist_component(min: &Self, max: &Self) -> usize { + let mut comp = 0; + let mut diff = 0; + for i in 0..4 { + let d = u32::from(max.y[i]) - u32::from(min.y[i]); + if d > diff { + diff = d; + comp = i; + } + } + let ud = u32::from(max.u) - u32::from(min.u); + if ud > diff { + diff = ud; + comp = 4; + } + let vd = u32::from(max.v) - u32::from(min.v); + if vd > diff { + comp = 5; + } + comp + } +} + +#[derive(Default)] +struct YUVCodeSum { + ysum: [u64; 4], + usum: u64, + vsum: u64, + count: u64, +} + +impl VQElementSum for YUVCodeSum { + fn zero() -> Self { Self::default() } + fn add(&mut self, rval: YUVCode, count: u64) { + for i in 0..4 { + self.ysum[i] += u64::from(rval.y[i]) * count; + } + self.usum += u64::from(rval.u) * count; + self.vsum += u64::from(rval.v) * count; + self.count += count; + } + fn get_centroid(&self) -> YUVCode { + if self.count != 0 { + let mut ycode = YUVCode::default(); + for i in 0..4 { + ycode.y[i] = ((self.ysum[i] + self.count / 2) / self.count) as u8; + } + ycode.u = ((self.usum + self.count / 2) / self.count) as u8; + ycode.v = ((self.vsum + self.count / 2) / self.count) as u8; + ycode + } else { + YUVCode::default() + } + } +} + +struct RNG { + seed: u32, +} + +impl RNG { + fn new() -> Self { Self { seed: 0x12345678 } } + fn next(&mut self) -> u8 { + let mut x = self.seed; + x ^= x.wrapping_shl(13); + x ^= x >> 17; + self.seed = x; + (self.seed >> 24) as u8 + } + fn fill_entry(&mut self, entry: &mut YUVCode) { + for y in entry.y.iter_mut() { + *y = self.next(); + } + entry.u = self.next(); + entry.v = self.next(); + } +} + +const GRAY_FORMAT: NAPixelFormaton = NAPixelFormaton { + model: ColorModel::YUV(YUVSubmodel::YUVJ), + components: 1, + comp_info: [Some(NAPixelChromaton{h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 0, next_elem: 1}), None, None, None, None], + elem_size: 1, + be: true, + alpha: false, + palette: false, + }; + +struct MaskWriter { + masks: Vec, + mask: u32, + pos: u8, +} + +impl MaskWriter { + fn new() -> Self { + Self { + masks: Vec::new(), + mask: 0, + pos: 0, + } + } + fn reset(&mut self) { + self.masks.truncate(0); + self.mask = 0; + self.pos = 0; + } + fn put_v1(&mut self) { + self.mask <<= 1; + self.pos += 1; + if self.pos == 32 { + self.flush(); + } + } + fn put_v4(&mut self) { + self.mask <<= 1; + self.mask |= 1; + self.pos += 1; + if self.pos == 32 { + self.flush(); + } + } + fn put_inter(&mut self, skip: bool) { + self.mask <<= 1; + self.mask |= !skip as u32; + self.pos += 1; + if self.pos == 32 { + self.flush(); + } + } + fn flush(&mut self) { + self.masks.push(self.mask); + self.mask = 0; + self.pos = 0; + } + fn end(&mut self) { + if self.pos == 0 { return; } + while self.pos < 32 { + self.mask <<= 1; + self.pos += 1; + } + self.flush(); + } +} + +struct CinepakEncoder { + stream: Option, + lastfrm: Option>, + pkt: Option, + frmcount: u8, + quality: u8, + nstrips: usize, + v1_entries: Vec, + v4_entries: Vec, + v1_cb: [YUVCode; 256], + v4_cb: [YUVCode; 256], + v1_cur_cb: [YUVCode; 256], + v4_cur_cb: [YUVCode; 256], + v1_idx: Vec, + v4_idx: Vec, + grayscale: bool, + rng: RNG, + masks: MaskWriter, + skip_dist: Vec, +} + +fn avg4(a: u8, b: u8, c: u8, d: u8) -> u8 { + ((u16::from(a) + u16::from(b) + u16::from(c) + u16::from(d) + 3) >> 2) as u8 +} + +fn patch_size(bw: &mut ByteWriter, pos: u64) -> EncoderResult<()> { + let size = bw.tell() - pos; + bw.seek(SeekFrom::Current(-((size + 3) as i64)))?; + bw.write_u24be((size + 4) as u32)?; + bw.seek(SeekFrom::End(0))?; + Ok(()) +} + +impl CinepakEncoder { + fn new() -> Self { + Self { + stream: None, + pkt: None, + lastfrm: None, + frmcount: 0, + quality: 0, + nstrips: 2, + v1_entries: Vec::new(), + v4_entries: Vec::new(), + v1_cb: [YUVCode::default(); 256], + v4_cb: [YUVCode::default(); 256], + v1_cur_cb: [YUVCode::default(); 256], + v4_cur_cb: [YUVCode::default(); 256], + grayscale: false, + rng: RNG::new(), + v1_idx: Vec::new(), + v4_idx: Vec::new(), + masks: MaskWriter::new(), + skip_dist: Vec::new(), + } + } + fn read_strip(&mut self, in_frm: &NAVideoBuffer, start: usize, end: usize) { + let ystride = in_frm.get_stride(0); + let mut yoff = in_frm.get_offset(0) + start * ystride; + let ustride = in_frm.get_stride(1); + let mut uoff = in_frm.get_offset(1) + start / 2 * ustride; + let vstride = in_frm.get_stride(2); + let mut voff = in_frm.get_offset(2) + start / 2 * vstride; + let (width, _) = in_frm.get_dimensions(0); + let data = in_frm.get_data(); + self.v1_entries.truncate(0); + self.v4_entries.truncate(0); + for _ in (start..end).step_by(4) { + for x in (0..width).step_by(4) { + let mut yblk = [0; 16]; + let mut ublk = [128; 4]; + let mut vblk = [128; 4]; + for j in 0..4 { + for i in 0..4 { + yblk[i + j * 4] = data[yoff + x + i + j * ystride]; + } + } + if !self.grayscale { + for j in 0..2 { + for i in 0..2 { + ublk[i + j * 2] = data[uoff + x / 2 + i + j * ustride]; + vblk[i + j * 2] = data[voff + x / 2 + i + j * vstride]; + } + } + } + self.v1_entries.push(YUVCode { + y: [avg4(yblk[ 0], yblk[ 1], yblk[ 4], yblk[ 5]), + avg4(yblk[ 2], yblk[ 3], yblk[ 6], yblk[ 7]), + avg4(yblk[ 8], yblk[ 9], yblk[12], yblk[13]), + avg4(yblk[10], yblk[11], yblk[14], yblk[15])], + u: avg4(ublk[0], ublk[1], ublk[2], ublk[3]), + v: avg4(vblk[0], vblk[1], vblk[2], vblk[3]), + }); + for i in 0..4 { + let yidx = (i & 1) * 2 + (i & 2) * 4; + self.v4_entries.push(YUVCode { + y: [ yblk[yidx], yblk[yidx + 1], yblk[yidx + 4], yblk[yidx + 5] ], + u: ublk[i], + v: vblk[i], + }); + } + } + yoff += ystride * 4; + uoff += ustride * 2; + voff += vstride * 2; + } + } + fn find_nearest(codebook: &[YUVCode; 256], code: YUVCode) -> (u8, u32) { + let mut min_dist = std::u32::MAX; + let mut idx = 0; + for (i, cw) in codebook.iter().enumerate() { + let dist = cw.dist(code); + if dist < min_dist { + min_dist = dist; + idx = i; + if dist == 0 { + break; + } + } + } + (idx as u8, min_dist) + } + fn can_update_cb(new_cb: &[YUVCode; 256], old_cb: &[YUVCode; 256], cb_size: usize) -> bool { + let mut skip_count = 0; + for (new, old) in new_cb.iter().zip(old_cb.iter()) { + if new == old { + skip_count += 1; + } + } + let full_size = cb_size * 256; + let upd_size = cb_size * (256 - skip_count) + 64; + upd_size < full_size + } + fn write_cb(bw: &mut ByteWriter, mut id: u8, new_cb: &[YUVCode; 256], old_cb: &[YUVCode; 256], grayscale: bool, update: bool) -> EncoderResult<()> { + if grayscale { + id |= 4; + } + if update { + id |= 1; + } + bw.write_byte(id)?; + bw.write_u24be(0)?; + let chunk_pos = bw.tell(); + if !update { + for entry in new_cb.iter() { + bw.write_buf(&entry.y)?; + if !grayscale { + bw.write_byte(entry.u ^ 0x80)?; + bw.write_byte(entry.v ^ 0x80)?; + } + } + } else { + let mut end = 256; + for (i, (ncw, ocw)) in new_cb.iter().rev().zip(old_cb.iter().rev()).enumerate() { + if ncw == ocw { + end = i; + } else { + break; + } + } + for i in (0..end).step_by(32) { + let mut mask = 0; + for j in 0..32 { + mask <<= 1; + if new_cb[i + j] != old_cb[i + j] { + mask |= 1; + } + } + bw.write_u32be(mask)?; + for j in 0..32 { + if new_cb[i + j] == old_cb[i + j] { continue; } + bw.write_buf(&new_cb[i + j].y)?; + if !grayscale { + bw.write_byte(new_cb[i + j].u ^ 0x80)?; + bw.write_byte(new_cb[i + j].v ^ 0x80)?; + } + } + } + } + patch_size(bw, chunk_pos)?; + Ok(()) + } + fn render_stripe(&mut self, intra: bool, start: usize, end: usize) { + if let Some(ref mut dst_frm) = self.lastfrm { + let ystride = dst_frm.get_stride(0); + let mut yoff = dst_frm.get_offset(0) + start * ystride; + let ustride = dst_frm.get_stride(1); + let mut uoff = dst_frm.get_offset(1) + start / 2 * ustride; + let vstride = dst_frm.get_stride(2); + let mut voff = dst_frm.get_offset(2) + start / 2 * vstride; + let (width, _) = dst_frm.get_dimensions(0); + let data = dst_frm.get_data_mut().unwrap(); + let mut miter = self.masks.masks.iter(); + let mut v1_iter = self.v1_idx.iter(); + let mut v4_iter = self.v4_idx.iter(); + let mut cur_mask = 0; + let mut cur_bit = 0; + for _ in (start..end).step_by(4) { + for x in (0..width).step_by(4) { + if cur_bit == 0 { + if !intra || self.v1_idx.len() > 0 { + cur_mask = *miter.next().unwrap(); + } else { + cur_mask = 0xFFFFFFFF; + } + cur_bit = 1 << 31; + } + if !intra { + if (cur_mask & cur_bit) == 0 { + cur_bit >>= 1; + continue; + } + cur_bit >>= 1; + if cur_bit == 0 { + cur_mask = *miter.next().unwrap(); + cur_bit = 1 << 31; + } + } + if (cur_mask & cur_bit) == 0 { + let idx = *v1_iter.next().unwrap() as usize; + let cb = &self.v1_cur_cb[idx]; + + let mut coff = yoff + x; + data[coff] = cb.y[0]; data[coff + 1] = cb.y[0]; + data[coff + 2] = cb.y[1]; data[coff + 3] = cb.y[1]; + coff += ystride; + data[coff] = cb.y[0]; data[coff + 1] = cb.y[0]; + data[coff + 2] = cb.y[1]; data[coff + 3] = cb.y[1]; + coff += ystride; + data[coff] = cb.y[2]; data[coff + 1] = cb.y[2]; + data[coff + 2] = cb.y[3]; data[coff + 3] = cb.y[3]; + coff += ystride; + data[coff] = cb.y[2]; data[coff + 1] = cb.y[2]; + data[coff + 2] = cb.y[3]; data[coff + 3] = cb.y[3]; + + if !self.grayscale { + let mut coff = uoff + x / 2; + data[coff] = cb.u; data[coff + 1] = cb.u; + coff += ustride; + data[coff] = cb.u; data[coff + 1] = cb.u; + + let mut coff = voff + x / 2; + data[coff] = cb.v; data[coff + 1] = cb.v; + coff += vstride; + data[coff] = cb.v; data[coff + 1] = cb.v; + } + } else { + let idx0 = *v4_iter.next().unwrap() as usize; + let cb0 = &self.v4_cur_cb[idx0]; + let idx1 = *v4_iter.next().unwrap() as usize; + let cb1 = &self.v4_cur_cb[idx1]; + let idx2 = *v4_iter.next().unwrap() as usize; + let cb2 = &self.v4_cur_cb[idx2]; + let idx3 = *v4_iter.next().unwrap() as usize; + let cb3 = &self.v4_cur_cb[idx3]; + + let mut coff = yoff + x; + data[coff] = cb0.y[0]; data[coff + 1] = cb0.y[1]; + data[coff + 2] = cb1.y[0]; data[coff + 3] = cb1.y[1]; + coff += ystride; + data[coff] = cb0.y[2]; data[coff + 1] = cb0.y[3]; + data[coff + 2] = cb1.y[2]; data[coff + 3] = cb1.y[3]; + coff += ystride; + data[coff] = cb2.y[0]; data[coff + 1] = cb2.y[1]; + data[coff + 2] = cb3.y[0]; data[coff + 3] = cb3.y[1]; + coff += ystride; + data[coff] = cb2.y[2]; data[coff + 1] = cb2.y[3]; + data[coff + 2] = cb3.y[2]; data[coff + 3] = cb3.y[3]; + + if !self.grayscale { + let mut coff = uoff + x / 2; + data[coff] = cb0.u; data[coff + 1] = cb1.u; + coff += ustride; + data[coff] = cb2.u; data[coff + 1] = cb3.u; + + let mut coff = voff + x / 2; + data[coff] = cb0.v; data[coff + 1] = cb1.v; + coff += vstride; + data[coff] = cb2.v; data[coff + 1] = cb3.v; + } + } + cur_bit >>= 1; + } + yoff += ystride * 4; + uoff += ustride * 2; + voff += vstride * 2; + } + } else { + unreachable!(); + } + } + fn calc_skip_dist(&mut self, in_frm: &NAVideoBuffer, start: usize, end: usize) { + self.skip_dist.truncate(0); + if let Some(ref ref_frm) = self.lastfrm { + let rystride = ref_frm.get_stride(0); + let mut ryoff = ref_frm.get_offset(0) + start * rystride; + let rustride = ref_frm.get_stride(1); + let mut ruoff = ref_frm.get_offset(1) + start / 2 * rustride; + let rvstride = ref_frm.get_stride(2); + let mut rvoff = ref_frm.get_offset(2) + start / 2 * rvstride; + let (width, _) = ref_frm.get_dimensions(0); + let rdata = ref_frm.get_data(); + + let iystride = in_frm.get_stride(0); + let mut iyoff = in_frm.get_offset(0) + start * iystride; + let iustride = in_frm.get_stride(1); + let mut iuoff = in_frm.get_offset(1) + start / 2 * iustride; + let ivstride = in_frm.get_stride(2); + let mut ivoff = in_frm.get_offset(2) + start / 2 * ivstride; + let idata = in_frm.get_data(); + + for _ in (start..end).step_by(4) { + for x in (0..width).step_by(4) { + let mut dist = 0; + let mut roff = ryoff + x; + let mut ioff = iyoff + x; + for _ in 0..4 { + for i in 0..4 { + let d = i32::from(rdata[roff + i]) - i32::from(idata[ioff + i]); + dist += d * d; + } + roff += rystride; + ioff += iystride; + } + if !self.grayscale { + let mut roff = ruoff + x / 2; + let mut ioff = iuoff + x / 2; + let ud = i32::from(rdata[roff]) - i32::from(idata[ioff]); + dist += ud * ud; + let ud = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]); + dist += ud * ud; + roff += rustride; ioff += iustride; + let ud = i32::from(rdata[roff]) - i32::from(idata[ioff]); + dist += ud * ud; + let ud = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]); + dist += ud * ud; + + let mut roff = rvoff + x / 2; + let mut ioff = ivoff + x / 2; + let vd = i32::from(rdata[roff]) - i32::from(idata[ioff]); + dist += vd * vd; + let vd = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]); + dist += vd * vd; + roff += rvstride; ioff += ivstride; + let vd = i32::from(rdata[roff]) - i32::from(idata[ioff]); + dist += vd * vd; + let vd = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]); + dist += vd * vd; + } + self.skip_dist.push(dist as u32); + } + + iyoff += iystride * 4; + iuoff += iustride * 2; + ivoff += ivstride * 2; + ryoff += rystride * 4; + ruoff += rustride * 2; + rvoff += rvstride * 2; + } + } else { + unreachable!(); + } + } + fn encode_intra(&mut self, bw: &mut ByteWriter, in_frm: &NAVideoBuffer) -> EncoderResult { + let (width, height) = in_frm.get_dimensions(0); + let mut strip_h = (height / self.nstrips + 3) & !3; + if strip_h == 0 { + self.nstrips = 1; + strip_h = height; + } + let mut start_line = 0; + let mut end_line = strip_h; + + bw.write_byte(0)?; // intra flag + bw.write_u24be(0)?; // frame size + let frame_data_pos = bw.tell(); + bw.write_u16be(width as u16)?; + bw.write_u16be(height as u16)?; + bw.write_u16be(self.nstrips as u16)?; + + for entry in self.v1_cb.iter_mut() { + self.rng.fill_entry(entry); + } + for entry in self.v4_cb.iter_mut() { + self.rng.fill_entry(entry); + } + while start_line < height { + self.read_strip(in_frm, start_line, end_line); + +// let mut elbg_v1: ELBG = ELBG::new(&self.v1_cb); +// let mut elbg_v4: ELBG = ELBG::new(&self.v4_cb); +// elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb); +// elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb); +quantise_median_cut::(&self.v1_entries, &mut self.v1_cur_cb); +quantise_median_cut::(&self.v4_entries, &mut self.v4_cur_cb); + if self.grayscale { + for cw in self.v1_cur_cb.iter_mut() { + cw.u = 128; + cw.v = 128; + } + for cw in self.v4_cur_cb.iter_mut() { + cw.u = 128; + cw.v = 128; + } + } + + self.v1_idx.truncate(0); + self.v4_idx.truncate(0); + self.masks.reset(); + + for (v1_entry, v4_entries) in self.v1_entries.iter().zip(self.v4_entries.chunks(4)) { + let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb, *v1_entry); + if v1_dist == 0 { + self.masks.put_v1(); + self.v1_idx.push(v1_idx); + continue; + } + let (v40_idx, v40_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[0]); + let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[1]); + let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[2]); + let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[3]); + if v40_dist + v41_dist + v42_dist + v43_dist > v1_dist { + self.masks.put_v4(); + self.v4_idx.push(v40_idx); + self.v4_idx.push(v41_idx); + self.v4_idx.push(v42_idx); + self.v4_idx.push(v43_idx); + } else { + self.masks.put_v1(); + self.v1_idx.push(v1_idx); + } + } + self.masks.end(); + + let mut is_intra_strip = start_line == 0; + let (upd_v1, upd_v4) = if !is_intra_strip { + let cb_size = if self.grayscale { 4 } else { 6 }; + (Self::can_update_cb(&self.v1_cur_cb, &self.v1_cb, cb_size), + Self::can_update_cb(&self.v4_cur_cb, &self.v4_cb, cb_size)) + } else { + (false, false) + }; + if !is_intra_strip && !upd_v1 && !upd_v4 { + is_intra_strip = true; + } + bw.write_byte(if is_intra_strip { 0x10 } else { 0x11 })?; + bw.write_u24be(0)?; // strip size + let strip_data_pos = bw.tell(); + bw.write_u16be(0)?; // yoff + bw.write_u16be(0)?; // xoff + bw.write_u16be((end_line - start_line) as u16)?; + bw.write_u16be(width as u16)?; + + Self::write_cb(bw, 0x20, &self.v4_cur_cb, &self.v4_cb, self.grayscale, upd_v4)?; + Self::write_cb(bw, 0x22, &self.v1_cur_cb, &self.v1_cb, self.grayscale, upd_v1)?; + + self.render_stripe(true, start_line, end_line); + + if self.v1_idx.len() == 0 { + bw.write_byte(0x32)?; + bw.write_u24be((self.v4_idx.len() + 4) as u32)?; + bw.write_buf(self.v4_idx.as_slice())?; + } else { + bw.write_byte(0x30)?; + bw.write_u24be(0)?; + let chunk_pos = bw.tell(); + let mut v1_pos = 0; + let mut v4_pos = 0; + for _ in 0..32 { + self.v1_idx.push(0); + self.v4_idx.push(0); + self.v4_idx.push(0); + self.v4_idx.push(0); + self.v4_idx.push(0); + } + for mask in self.masks.masks.iter() { + bw.write_u32be(*mask)?; + for j in (0..32).rev() { + if (mask & (1 << j)) == 0 { + bw.write_byte(self.v1_idx[v1_pos])?; + v1_pos += 1; + } else { + bw.write_byte(self.v4_idx[v4_pos])?; + bw.write_byte(self.v4_idx[v4_pos + 1])?; + bw.write_byte(self.v4_idx[v4_pos + 2])?; + bw.write_byte(self.v4_idx[v4_pos + 3])?; + v4_pos += 4; + } + } + } + patch_size(bw, chunk_pos)?; + } + + patch_size(bw, strip_data_pos)?; + + self.v1_cb.copy_from_slice(&self.v1_cur_cb); + self.v4_cb.copy_from_slice(&self.v4_cur_cb); + start_line = end_line; + end_line = (end_line + strip_h).min(height); + } + patch_size(bw, frame_data_pos)?; + Ok(true) + } + fn encode_inter(&mut self, bw: &mut ByteWriter, in_frm: &NAVideoBuffer) -> EncoderResult { + let (width, height) = in_frm.get_dimensions(0); + let mut strip_h = (height / self.nstrips + 3) & !3; + if strip_h == 0 { + self.nstrips = 1; + strip_h = height; + } + let mut start_line = 0; + let mut end_line = strip_h; + + bw.write_byte(1)?; // intra flag + bw.write_u24be(0)?; // frame size + let frame_data_pos = bw.tell(); + bw.write_u16be(width as u16)?; + bw.write_u16be(height as u16)?; + bw.write_u16be(self.nstrips as u16)?; + + while start_line < height { + self.read_strip(in_frm, start_line, end_line); + self.calc_skip_dist(in_frm, start_line, end_line); + +// let mut elbg_v1: ELBG = ELBG::new(&self.v1_cb); +// let mut elbg_v4: ELBG = ELBG::new(&self.v4_cb); +// elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb); +// elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb); +quantise_median_cut::(&self.v1_entries, &mut self.v1_cur_cb); +quantise_median_cut::(&self.v4_entries, &mut self.v4_cur_cb); + if self.grayscale { + for cw in self.v1_cur_cb.iter_mut() { + cw.u = 128; + cw.v = 128; + } + for cw in self.v4_cur_cb.iter_mut() { + cw.u = 128; + cw.v = 128; + } + } + + self.v1_idx.truncate(0); + self.v4_idx.truncate(0); + self.masks.reset(); + + let mut skip_iter = self.skip_dist.iter(); + for (v1_entry, v4_entries) in self.v1_entries.iter().zip(self.v4_entries.chunks(4)) { + let skip_dist = *skip_iter.next().unwrap(); + if skip_dist == 0 { + self.masks.put_inter(true); + continue; + } + let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb, *v1_entry); + if skip_dist < v1_dist { + self.masks.put_inter(true); + continue; + } else { + self.masks.put_inter(false); + } + if v1_dist == 0 { + self.masks.put_v1(); + self.v1_idx.push(v1_idx); + continue; + } + let (v40_idx, v40_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[0]); + let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[1]); + let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[2]); + let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb, v4_entries[3]); + if v40_dist + v41_dist + v42_dist + v43_dist > v1_dist { + self.masks.put_v4(); + self.v4_idx.push(v40_idx); + self.v4_idx.push(v41_idx); + self.v4_idx.push(v42_idx); + self.v4_idx.push(v43_idx); + } else { + self.masks.put_v1(); + self.v1_idx.push(v1_idx); + } + } + self.masks.end(); + + let (upd_v1, upd_v4) = { + let cb_size = if self.grayscale { 4 } else { 6 }; + (Self::can_update_cb(&self.v1_cur_cb, &self.v1_cb, cb_size), + Self::can_update_cb(&self.v4_cur_cb, &self.v4_cb, cb_size)) + }; + bw.write_byte(0x11)?; + bw.write_u24be(0)?; // strip size + let strip_data_pos = bw.tell(); + bw.write_u16be(0)?; // yoff + bw.write_u16be(0)?; // xoff + bw.write_u16be((end_line - start_line) as u16)?; + bw.write_u16be(width as u16)?; + + Self::write_cb(bw, 0x20, &self.v4_cur_cb, &self.v4_cb, self.grayscale, upd_v4)?; + Self::write_cb(bw, 0x22, &self.v1_cur_cb, &self.v1_cb, self.grayscale, upd_v1)?; + + self.render_stripe(false, start_line, end_line); + + bw.write_byte(0x31)?; + bw.write_u24be(0)?; + let chunk_pos = bw.tell(); + let mut v1_pos = 0; + let mut v4_pos = 0; + for _ in 0..32 { + self.v1_idx.push(0); + self.v4_idx.push(0); + self.v4_idx.push(0); + self.v4_idx.push(0); + self.v4_idx.push(0); + } + let mut skip = true; + for mask in self.masks.masks.iter() { + bw.write_u32be(*mask)?; + if *mask == 0 { continue; } + let mut bit = 1 << 31; + while bit > 0 { + if skip { + skip = (mask & bit) == 0; + bit >>= 1; + } else { + if (mask & bit) == 0 { + bw.write_byte(self.v1_idx[v1_pos])?; + v1_pos += 1; + } else { + bw.write_byte(self.v4_idx[v4_pos])?; + bw.write_byte(self.v4_idx[v4_pos + 1])?; + bw.write_byte(self.v4_idx[v4_pos + 2])?; + bw.write_byte(self.v4_idx[v4_pos + 3])?; + v4_pos += 4; + } + bit >>= 1; + skip = true; + } + } + } + patch_size(bw, chunk_pos)?; + + patch_size(bw, strip_data_pos)?; + + self.v1_cb.copy_from_slice(&self.v1_cur_cb); + self.v4_cb.copy_from_slice(&self.v4_cur_cb); + start_line = end_line; + end_line = (end_line + strip_h).min(height); + } + patch_size(bw, frame_data_pos)?; + Ok(true) + } +} + +impl NAEncoder for CinepakEncoder { + 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, YUV420_FORMAT)); + Ok(ofmt) + }, + NACodecTypeInfo::Audio(_) => return Err(EncoderError::FormatError), + NACodecTypeInfo::Video(vinfo) => { + let pix_fmt = if vinfo.format == GRAY_FORMAT { GRAY_FORMAT } else { YUV420_FORMAT }; + let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, pix_fmt); + let mut ofmt = EncodeParameters::default(); + ofmt.format = NACodecTypeInfo::Video(outinfo); + Ok(ofmt) + } + } + } + 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 != YUV420_FORMAT && vinfo.format != GRAY_FORMAT { + return Err(EncoderError::FormatError); + } + if ((vinfo.width | vinfo.height) & 3) != 0 { + return Err(EncoderError::FormatError); + } + if (vinfo.width | vinfo.height) >= (1 << 16) { + return Err(EncoderError::FormatError); + } + + let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format); + let info = NACodecInfo::new("cinepak", NACodecTypeInfo::Video(out_info.clone()), None); + let stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den).into_ref(); + + self.stream = Some(stream.clone()); + self.quality = encinfo.quality; + self.grayscale = vinfo.format != YUV420_FORMAT; + let num_blocks = vinfo.width / 2 * vinfo.height / 2; + self.v1_entries = Vec::with_capacity(num_blocks); + self.v4_entries = Vec::with_capacity(num_blocks * 4); + self.v1_idx = Vec::with_capacity(num_blocks); + self.v4_idx = Vec::with_capacity(num_blocks * 4); + self.skip_dist = Vec::with_capacity(vinfo.width / 4 * vinfo.height / 4); + + let buf = alloc_video_buffer(out_info, 2)?; + self.lastfrm = Some(buf.get_vbuf().unwrap()); + + Ok(stream) + }, + } + } + fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { + let buf = frm.get_buffer(); + if let Some(ref vbuf) = buf.get_vbuf() { + let mut dbuf = Vec::with_capacity(4); + let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); + let mut bw = ByteWriter::new(&mut gw); + let is_intra = if self.frmcount == 0 { + self.encode_intra(&mut bw, vbuf)? + } else { + self.encode_inter(&mut bw, vbuf)? + }; + self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf)); + self.frmcount += 1; + if self.frmcount == 25 { + self.frmcount = 0; + } + Ok(()) + } else { + Err(EncoderError::InvalidParameters) + } + } + fn get_packet(&mut self) -> EncoderResult> { + let mut npkt = None; + std::mem::swap(&mut self.pkt, &mut npkt); + Ok(npkt) + } + fn flush(&mut self) -> EncoderResult<()> { + self.frmcount = 0; + Ok(()) + } +} + +impl NAOptionHandler for CinepakEncoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + +pub fn get_encoder() -> Box { + Box::new(CinepakEncoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::*; + use nihav_core::demuxers::*; + use nihav_core::muxers::*; + use crate::*; + use nihav_codec_support::test::enc_video::*; + + #[test] + fn test_cinepak_encoder() { + 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); + let mut mux_reg = RegisteredMuxers::new(); + generic_register_all_muxers(&mut mux_reg); + let mut enc_reg = RegisteredEncoders::new(); + generic_register_all_encoders(&mut enc_reg); + + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/Misc/TalkingHead_352x288.avi", + stream_type: StreamType::Video, + limit: Some(2), + dmx_reg, dec_reg, + }; + let enc_config = EncoderTestParams { + muxer: "avi", + enc_name: "cinepak", + out_name: "cinepak.avi", + mux_reg, enc_reg, + }; + let dst_vinfo = NAVideoInfo { + width: 0, + height: 0, + format: YUV420_FORMAT, + flipped: true, + }; + 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); + } +} diff --git a/nihav-commonfmt/src/codecs/mod.rs b/nihav-commonfmt/src/codecs/mod.rs index 9e2a094..32fbc7f 100644 --- a/nihav-commonfmt/src/codecs/mod.rs +++ b/nihav-commonfmt/src/codecs/mod.rs @@ -46,3 +46,19 @@ pub fn generic_register_all_codecs(rd: &mut RegisteredDecoders) { rd.add_decoder(decoder.clone()); } } + +#[cfg(feature="encoder_cinepak")] +mod cinepakenc; + +const ENCODERS: &[EncoderInfo] = &[ +#[cfg(feature="encoder_cinepak")] + EncoderInfo { name: "cinepak", get_encoder: cinepakenc::get_encoder }, +]; + +/// Registers all available encoders provided by this crate. +pub fn generic_register_all_encoders(re: &mut RegisteredEncoders) { + for encoder in ENCODERS.iter() { + re.add_encoder(encoder.clone()); + } +} + diff --git a/nihav-commonfmt/src/lib.rs b/nihav-commonfmt/src/lib.rs index 82bfd49..c4a2ca7 100644 --- a/nihav-commonfmt/src/lib.rs +++ b/nihav-commonfmt/src/lib.rs @@ -10,6 +10,8 @@ mod codecs; #[cfg(feature="decoders")] pub use crate::codecs::generic_register_all_codecs; +#[cfg(feature="encoders")] +pub use crate::codecs::generic_register_all_encoders; #[cfg(feature="demuxers")] mod demuxers; -- 2.39.5