replace vec.truncate(0) with vec.clear()
[nihav.git] / nihav-commonfmt / src / codecs / cinepakenc.rs
index 10e0a3670530c77ab7fba835d87ecb38a7b96311..ddf2584321afa3881787c4680bc4f559de7fc07f 100644 (file)
@@ -169,7 +169,7 @@ impl MaskWriter {
         }
     }
     fn reset(&mut self) {
-        self.masks.truncate(0);
+        self.masks.clear();
         self.mask = 0;
         self.pos = 0;
     }
@@ -211,19 +211,41 @@ impl MaskWriter {
     }
 }
 
+#[derive(Clone,Copy,PartialEq)]
+enum QuantMode {
+    ELBG,
+    Hybrid,
+    MedianCut,
+}
+
+impl std::string::ToString for QuantMode {
+    fn to_string(&self) -> String {
+        match *self {
+            QuantMode::ELBG => "elbg".to_string(),
+            QuantMode::Hybrid => "hybrid".to_string(),
+            QuantMode::MedianCut => "mediancut".to_string(),
+        }
+    }
+}
+
 struct CinepakEncoder {
     stream:     Option<NAStreamRef>,
     lastfrm:    Option<NAVideoBufferRef<u8>>,
     pkt:        Option<NAPacket>,
     frmcount:   u8,
+    key_int:    u8,
+    qmode:      QuantMode,
     quality:    u8,
     nstrips:    usize,
+    cur_strip:  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_cb:      Vec<[YUVCode; 256]>,
+    v4_cb:      Vec<[YUVCode; 256]>,
+    v1_cur_cb:  Vec<[YUVCode; 256]>,
+    v4_cur_cb:  Vec<[YUVCode; 256]>,
+    v1_len:     usize,
+    v4_len:     usize,
     v1_idx:     Vec<u8>,
     v4_idx:     Vec<u8>,
     grayscale:  bool,
@@ -251,14 +273,19 @@ impl CinepakEncoder {
             pkt:        None,
             lastfrm:    None,
             frmcount:   0,
+            qmode:      QuantMode::MedianCut,
+            key_int:    25,
             quality:    0,
             nstrips:    2,
+            cur_strip:  0,
             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],
+            v1_cb:      Vec::with_capacity(2),
+            v4_cb:      Vec::with_capacity(2),
+            v1_cur_cb:  Vec::with_capacity(2),
+            v4_cur_cb:  Vec::with_capacity(2),
+            v1_len:     0,
+            v4_len:     0,
             grayscale:  false,
             rng:        RNG::new(),
             v1_idx:     Vec::new(),
@@ -276,8 +303,8 @@ impl CinepakEncoder {
         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);
+        self.v1_entries.clear();
+        self.v4_entries.clear();
         for _ in (start..end).step_by(4) {
             for x in (0..width).step_by(4) {
                 let mut yblk = [0; 16];
@@ -318,7 +345,7 @@ impl CinepakEncoder {
             voff += vstride * 2;
         }
     }
-    fn find_nearest(codebook: &[YUVCode; 256], code: YUVCode) -> (u8, u32) {
+    fn find_nearest(codebook: &[YUVCode], code: YUVCode) -> (u8, u32) {
         let mut min_dist = std::u32::MAX;
         let mut idx = 0;
         for (i, cw) in codebook.iter().enumerate() {
@@ -411,7 +438,7 @@ impl CinepakEncoder {
             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 {
+                        if !intra || !self.v1_idx.is_empty() {
                             cur_mask = *miter.next().unwrap();
                         } else {
                             cur_mask = 0xFFFFFFFF;
@@ -431,7 +458,7 @@ impl CinepakEncoder {
                     }
                     if (cur_mask & cur_bit) == 0 {
                         let idx = *v1_iter.next().unwrap() as usize;
-                        let cb = &self.v1_cur_cb[idx];
+                        let cb = &self.v1_cur_cb[self.cur_strip][idx];
 
                         let mut coff = yoff + x;
                         data[coff]     = cb.y[0]; data[coff + 1] = cb.y[0];
@@ -459,13 +486,13 @@ impl CinepakEncoder {
                         }
                     } else {
                         let idx0 = *v4_iter.next().unwrap() as usize;
-                        let cb0 = &self.v4_cur_cb[idx0];
+                        let cb0 = &self.v4_cur_cb[self.cur_strip][idx0];
                         let idx1 = *v4_iter.next().unwrap() as usize;
-                        let cb1 = &self.v4_cur_cb[idx1];
+                        let cb1 = &self.v4_cur_cb[self.cur_strip][idx1];
                         let idx2 = *v4_iter.next().unwrap() as usize;
-                        let cb2 = &self.v4_cur_cb[idx2];
+                        let cb2 = &self.v4_cur_cb[self.cur_strip][idx2];
                         let idx3 = *v4_iter.next().unwrap() as usize;
-                        let cb3 = &self.v4_cur_cb[idx3];
+                        let cb3 = &self.v4_cur_cb[self.cur_strip][idx3];
 
                         let mut coff = yoff + x;
                         data[coff]     = cb0.y[0]; data[coff + 1] = cb0.y[1];
@@ -503,7 +530,7 @@ impl CinepakEncoder {
         }
     }
     fn calc_skip_dist(&mut self, in_frm: &NAVideoBuffer<u8>, start: usize, end: usize) {
-        self.skip_dist.truncate(0);
+        self.skip_dist.clear();
         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;
@@ -574,6 +601,39 @@ impl CinepakEncoder {
             unreachable!();
         }
     }
+    fn quant_vectors(&mut self) {
+        match self.qmode {
+            QuantMode::ELBG => {
+                let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cb[self.cur_strip]);
+                let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cb[self.cur_strip]);
+
+                for entry in self.v1_cb[self.cur_strip].iter_mut().skip(self.v1_len) {
+                    self.rng.fill_entry(entry);
+                }
+                for entry in self.v4_cb[self.cur_strip].iter_mut().skip(self.v4_len) {
+                    self.rng.fill_entry(entry);
+                }
+
+                self.v1_len = elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
+                self.v4_len = elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]);
+            },
+            QuantMode::Hybrid => {
+                quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
+                quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]);
+                let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cur_cb[self.cur_strip]);
+                let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cur_cb[self.cur_strip]);
+                self.v1_len = elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
+                self.v4_len = elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]);
+            },
+            QuantMode::MedianCut => {
+                self.v1_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
+                self.v4_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]);
+            },
+        };
+
+        for e in self.v1_cur_cb[self.cur_strip].iter_mut().skip(self.v1_len) { *e = YUVCode::default(); }
+        for e in self.v4_cur_cb[self.cur_strip].iter_mut().skip(self.v4_len) { *e = YUVCode::default(); }
+    }
     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;
@@ -591,47 +651,47 @@ impl CinepakEncoder {
         bw.write_u16be(height as u16)?;
         bw.write_u16be(self.nstrips as u16)?;
 
-        for entry in self.v1_cb.iter_mut() {
+        self.cur_strip = 0;
+        for entry in self.v1_cb[self.cur_strip].iter_mut() {
             self.rng.fill_entry(entry);
         }
-        for entry in self.v4_cb.iter_mut() {
+        for entry in self.v4_cb[self.cur_strip].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.cur_strip > 0 {
+                self.v1_cb[self.cur_strip] = self.v1_cb[self.cur_strip - 1];
+                self.v4_cb[self.cur_strip] = self.v4_cb[self.cur_strip - 1];
+            }
+            self.quant_vectors();
             if self.grayscale {
-                for cw in self.v1_cur_cb.iter_mut() {
+                for cw in self.v1_cur_cb[self.cur_strip].iter_mut() {
                     cw.u = 128;
                     cw.v = 128;
                 }
-                for cw in self.v4_cur_cb.iter_mut() {
+                for cw in self.v4_cur_cb[self.cur_strip].iter_mut() {
                     cw.u = 128;
                     cw.v = 128;
                 }
             }
 
-            self.v1_idx.truncate(0);
-            self.v4_idx.truncate(0);
+            self.v1_idx.clear();
+            self.v4_idx.clear();
             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);
+                let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb[self.cur_strip][..self.v1_len], *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]);
+                let (v40_idx, v40_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[0]);
+                let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[1]);
+                let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[2]);
+                let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[3]);
                 if v40_dist + v41_dist + v42_dist + v43_dist > v1_dist {
                     self.masks.put_v4();
                     self.v4_idx.push(v40_idx);
@@ -648,8 +708,8 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
             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))
+                    (Self::can_update_cb(&self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], cb_size),
+                     Self::can_update_cb(&self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], cb_size))
                 } else {
                     (false, false)
                 };
@@ -664,15 +724,15 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
             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::write_cb(bw, 0x20, &self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], self.grayscale, upd_v4)?;
+            Self::write_cb(bw, 0x22, &self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], self.grayscale, upd_v1)?;
 
             self.render_stripe(true, start_line, end_line);
 
-            if self.v1_idx.len() == 0 {
+            if self.v4_idx.is_empty() {
                 bw.write_byte(0x32)?;
-                bw.write_u24be((self.v4_idx.len() + 4) as u32)?;
-                bw.write_buf(self.v4_idx.as_slice())?;
+                bw.write_u24be((self.v1_idx.len() + 4) as u32)?;
+                bw.write_buf(self.v1_idx.as_slice())?;
             } else {
                 bw.write_byte(0x30)?;
                 bw.write_u24be(0)?;
@@ -706,10 +766,12 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
 
             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);
+            self.v1_cb[self.cur_strip].copy_from_slice(&self.v1_cur_cb[self.cur_strip]);
+            self.v4_cb[self.cur_strip].copy_from_slice(&self.v4_cur_cb[self.cur_strip]);
             start_line = end_line;
             end_line = (end_line + strip_h).min(height);
+
+            self.cur_strip += 1;
         }
         patch_size(bw, frame_data_pos)?;
         Ok(true)
@@ -731,29 +793,25 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
         bw.write_u16be(height as u16)?;
         bw.write_u16be(self.nstrips as u16)?;
 
+        self.cur_strip = 0;
         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);
+            self.quant_vectors();
             if self.grayscale {
-                for cw in self.v1_cur_cb.iter_mut() {
+                for cw in self.v1_cur_cb[self.cur_strip].iter_mut() {
                     cw.u = 128;
                     cw.v = 128;
                 }
-                for cw in self.v4_cur_cb.iter_mut() {
+                for cw in self.v4_cur_cb[self.cur_strip].iter_mut() {
                     cw.u = 128;
                     cw.v = 128;
                 }
             }
 
-            self.v1_idx.truncate(0);
-            self.v4_idx.truncate(0);
+            self.v1_idx.clear();
+            self.v4_idx.clear();
             self.masks.reset();
 
             let mut skip_iter = self.skip_dist.iter();
@@ -763,7 +821,7 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
                     self.masks.put_inter(true);
                     continue;
                 }
-                let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb, *v1_entry);
+                let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb[self.cur_strip][..self.v1_len], *v1_entry);
                 if skip_dist < v1_dist {
                     self.masks.put_inter(true);
                     continue;
@@ -775,10 +833,10 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
                     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]);
+                let (v40_idx, v40_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[0]);
+                let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[1]);
+                let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[2]);
+                let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[3]);
                 if v40_dist + v41_dist + v42_dist + v43_dist > v1_dist {
                     self.masks.put_v4();
                     self.v4_idx.push(v40_idx);
@@ -794,8 +852,8 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
 
             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))
+                    (Self::can_update_cb(&self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], cb_size),
+                     Self::can_update_cb(&self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], cb_size))
                 };
             bw.write_byte(0x11)?;
             bw.write_u24be(0)?; // strip size
@@ -805,8 +863,8 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
             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::write_cb(bw, 0x20, &self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], self.grayscale, upd_v4)?;
+            Self::write_cb(bw, 0x22, &self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], self.grayscale, upd_v1)?;
 
             self.render_stripe(false, start_line, end_line);
 
@@ -851,10 +909,12 @@ quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb
 
             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);
+            self.v1_cb[self.cur_strip].copy_from_slice(&self.v1_cur_cb[self.cur_strip]);
+            self.v4_cb[self.cur_strip].copy_from_slice(&self.v4_cur_cb[self.cur_strip]);
             start_line = end_line;
             end_line = (end_line + strip_h).min(height);
+
+            self.cur_strip += 1;
         }
         patch_size(bw, frame_data_pos)?;
         Ok(true)
@@ -869,10 +929,10 @@ impl NAEncoder for CinepakEncoder {
                 ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT));
                 Ok(ofmt)
             },
-            NACodecTypeInfo::Audio(_) => return Err(EncoderError::FormatError),
+            NACodecTypeInfo::Audio(_) => 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 outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, false, pix_fmt);
                 let mut ofmt = *encinfo;
                 ofmt.format = NACodecTypeInfo::Video(outinfo);
                 Ok(ofmt)
@@ -895,8 +955,10 @@ impl NAEncoder for CinepakEncoder {
                 }
 
                 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();
+                let info = NACodecInfo::new("cinepak", 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();
 
                 self.stream = Some(stream.clone());
                 self.quality = encinfo.quality;
@@ -910,7 +972,7 @@ impl NAEncoder for CinepakEncoder {
 
                 let buf = alloc_video_buffer(out_info, 2)?;
                 self.lastfrm = Some(buf.get_vbuf().unwrap());
-                
+
                 Ok(stream)
             },
         }
@@ -918,6 +980,19 @@ impl NAEncoder for CinepakEncoder {
     fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
         let buf = frm.get_buffer();
         if let Some(ref vbuf) = buf.get_vbuf() {
+            let cur_strips = self.v1_cb.len();
+            if cur_strips != self.nstrips {
+                self.frmcount = 0;
+            }
+            if cur_strips < self.nstrips {
+                for _ in cur_strips..self.nstrips {
+                    self.v1_cb.push([YUVCode::default(); 256]);
+                    self.v4_cb.push([YUVCode::default(); 256]);
+                    self.v1_cur_cb.push([YUVCode::default(); 256]);
+                    self.v4_cur_cb.push([YUVCode::default(); 256]);
+                }
+            }
+
             let mut dbuf = Vec::with_capacity(4);
             let mut gw   = GrowableMemoryWriter::new_write(&mut dbuf);
             let mut bw   = ByteWriter::new(&mut gw);
@@ -928,7 +1003,7 @@ impl NAEncoder for CinepakEncoder {
                 };
             self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
             self.frmcount += 1;
-            if self.frmcount == 25 {
+            if self.frmcount == self.key_int {
                 self.frmcount = 0;
             }
             Ok(())
@@ -947,10 +1022,59 @@ impl NAEncoder for CinepakEncoder {
     }
 }
 
+const ENCODER_OPTS: &[NAOptionDefinition] = &[
+    NAOptionDefinition {
+        name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
+        opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
+    NAOptionDefinition {
+        name: "nstrips", description: "Number of strips per frame (0 - automatic)",
+        opt_type: NAOptionDefinitionType::Int(Some(0), Some(16)) },
+    NAOptionDefinition {
+        name: "quant_mode", description: "Quantisation mode",
+        opt_type: NAOptionDefinitionType::String(Some(&["elbg", "hybrid", "mediancut"])) },
+];
+
 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 }
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
+    fn set_options(&mut self, options: &[NAOption]) {
+        for option in options.iter() {
+            for opt_def in ENCODER_OPTS.iter() {
+                if opt_def.check(option).is_ok() {
+                    match option.name {
+                        KEYFRAME_OPTION => {
+                            if let NAValue::Int(intval) = option.value {
+                                self.key_int = intval as u8;
+                            }
+                        },
+                        "nstrips" => {
+                            if let NAValue::Int(intval) = option.value {
+                                self.nstrips = intval as usize;
+                            }
+                        },
+                        "quant_mode" => {
+                            if let NAValue::String(ref str) = option.value {
+                                match str.as_str() {
+                                    "elbg"      => self.qmode = QuantMode::ELBG,
+                                    "hybrid"    => self.qmode = QuantMode::Hybrid,
+                                    "mediancut" => self.qmode = QuantMode::MedianCut,
+                                    _ => {},
+                                };
+                            }
+                        },
+                        _ => {},
+                    };
+                }
+            }
+        }
+    }
+    fn query_option_value(&self, name: &str) -> Option<NAValue> {
+        match name {
+            KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
+            "nstrips" => Some(NAValue::Int(self.nstrips as i64)),
+            "quant_mode" => Some(NAValue::String(self.qmode.to_string())),
+            _ => None,
+        }
+    }
 }
 
 pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
@@ -970,7 +1094,7 @@ mod test {
         let mut dmx_reg = RegisteredDemuxers::new();
         generic_register_all_demuxers(&mut dmx_reg);
         let mut dec_reg = RegisteredDecoders::new();
-        generic_register_all_codecs(&mut dec_reg);
+        generic_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();
@@ -994,6 +1118,7 @@ mod test {
                 height:  0,
                 format:  YUV420_FORMAT,
                 flipped: true,
+                bits:    12,
             };
         let enc_params = EncodeParameters {
                 format:  NACodecTypeInfo::Video(dst_vinfo),
@@ -1003,6 +1128,8 @@ mod test {
                 tb_den:  0,
                 flags:   0,
             };
-        test_encoding_to_file(&dec_config, &enc_config, enc_params);
+        //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
+        test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
+                          &[0xd73cb3c7, 0x30d59f90, 0x1d6e0e28, 0x5b72cc0c]);
     }
 }