]> git.nihav.org Git - nihav.git/commitdiff
experimental Cinepak encoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 3 Jun 2020 09:45:07 +0000 (11:45 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 3 Jun 2020 09:45:07 +0000 (11:45 +0200)
nihav-commonfmt/Cargo.toml
nihav-commonfmt/src/codecs/cinepakenc.rs [new file with mode: 0644]
nihav-commonfmt/src/codecs/mod.rs
nihav-commonfmt/src/lib.rs

index 5bd89901063b422dc3da361ec963fbd5fa1953d1..6e572d39808c6d587d11f449487889508205df36 100644 (file)
@@ -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 (file)
index 0000000..d40f504
--- /dev/null
@@ -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<YUVCode> 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<u32>,
+    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<NAStreamRef>,
+    lastfrm:    Option<NAVideoBufferRef<u8>>,
+    pkt:        Option<NAPacket>,
+    frmcount:   u8,
+    quality:    u8,
+    nstrips:    usize,
+    v1_entries: Vec<YUVCode>,
+    v4_entries: Vec<YUVCode>,
+    v1_cb:      [YUVCode; 256],
+    v4_cb:      [YUVCode; 256],
+    v1_cur_cb:  [YUVCode; 256],
+    v4_cur_cb:  [YUVCode; 256],
+    v1_idx:     Vec<u8>,
+    v4_idx:     Vec<u8>,
+    grayscale:  bool,
+    rng:        RNG,
+    masks:      MaskWriter,
+    skip_dist:  Vec<u32>,
+}
+
+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<u8>, 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<u8>, 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<u8>) -> EncoderResult<bool> {
+        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<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cb);
+//            let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = 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::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb);
+quantise_median_cut::<YUVCode, YUVCodeSum>(&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<u8>) -> EncoderResult<bool> {
+        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<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cb);
+//            let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = 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::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb);
+quantise_median_cut::<YUVCode, YUVCodeSum>(&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<EncodeParameters> {
+        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<NAStreamRef> {
+        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<Option<NAPacket>> {
+        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<NAValue> { None }
+}
+
+pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
+    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);
+    }
+}
index 9e2a0943447ccb81f1656a45ac94e700e4fb5769..32fbc7fe0e53167f0d988eb568b80c95b69b8890 100644 (file)
@@ -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());
+    }
+}
+
index 82bfd493784b1a474b4c55ca1d3fd99728e80a31..c4a2ca72ab46e7d6818b771c1474755251fc733b 100644 (file)
@@ -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;