]> git.nihav.org Git - nihav.git/blobdiff - nihav-commonfmt/src/codecs/cinepakenc.rs
cinepakenc: make ELBG mode to behave like hybrid mode (and drop that one)
[nihav.git] / nihav-commonfmt / src / codecs / cinepakenc.rs
index 4729cd6050fb3f4a228ad20064189f597f78dfa6..99194ceae268f82af995ce0d0d22a712794c525e 100644 (file)
@@ -214,7 +214,6 @@ impl MaskWriter {
 #[derive(Clone,Copy,PartialEq)]
 enum QuantMode {
     ELBG,
-    Hybrid,
     MedianCut,
 }
 
@@ -222,7 +221,6 @@ 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(),
         }
     }
@@ -237,6 +235,7 @@ struct CinepakEncoder {
     qmode:      QuantMode,
     quality:    u8,
     nstrips:    usize,
+    force_v1:   bool,
     cur_strip:  usize,
     v1_entries: Vec<YUVCode>,
     v4_entries: Vec<YUVCode>,
@@ -277,6 +276,7 @@ impl CinepakEncoder {
             key_int:    25,
             quality:    0,
             nstrips:    2,
+            force_v1:   false,
             cur_strip:  0,
             v1_entries: Vec::new(),
             v4_entries: Vec::new(),
@@ -360,18 +360,18 @@ impl CinepakEncoder {
         }
         (idx as u8, min_dist)
     }
-    fn can_update_cb(new_cb: &[YUVCode; 256], old_cb: &[YUVCode; 256], cb_size: usize) -> bool {
+    fn can_update_cb(new_cb: &[YUVCode], old_cb: &[YUVCode], 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;
+        let full_size = cb_size * new_cb.len();
+        let upd_size = cb_size * (new_cb.len() - skip_count) + (new_cb.len() + 31) / 32 * 4;
         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<()> {
+    fn write_cb(bw: &mut ByteWriter, mut id: u8, new_cb: &[YUVCode], old_cb: &[YUVCode], grayscale: bool, update: bool, num_elem: usize) -> EncoderResult<()> {
         if grayscale {
             id |= 4;
         }
@@ -382,7 +382,7 @@ impl CinepakEncoder {
         bw.write_u24be(0)?;
         let chunk_pos = bw.tell();
         if !update {
-            for entry in new_cb.iter() {
+            for entry in new_cb.iter().take(num_elem) {
                 bw.write_buf(&entry.y)?;
                 if !grayscale {
                     bw.write_byte(entry.u ^ 0x80)?;
@@ -390,8 +390,8 @@ impl CinepakEncoder {
                 }
             }
         } else {
-            let mut end = 256;
-            for (i, (ncw, ocw)) in new_cb.iter().rev().zip(old_cb.iter().rev()).enumerate() {
+            let mut end = num_elem;
+            for (i, (ncw, ocw)) in new_cb.iter().zip(old_cb.iter()).enumerate().take(num_elem).rev() {
                 if ncw == ocw {
                     end = i;
                 } else {
@@ -604,30 +604,32 @@ impl CinepakEncoder {
     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]);
+                let v1_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
+                let v4_len = if !self.force_v1 {
+                        quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip])
+                    } else {
+                        0
+                    };
+                self.v1_len = if v1_len < 256 {
+                        v1_len
+                    } else {
+                        let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cur_cb[self.cur_strip]);
+                        elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip])
+                    };
+                self.v4_len = if v4_len < 256 {
+                        v4_len
+                    } else {
+                        let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cur_cb[self.cur_strip]);
+                        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]);
+                if !self.force_v1 {
+                    self.v4_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]);
+                } else {
+                    self.v4_len = 0;
+                }
             },
         };
 
@@ -683,7 +685,7 @@ impl CinepakEncoder {
 
             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[self.cur_strip][..self.v1_len], *v1_entry);
-                if v1_dist == 0 {
+                if v1_dist == 0 || self.force_v1 {
                     self.masks.put_v1();
                     self.v1_idx.push(v1_idx);
                     continue;
@@ -708,8 +710,8 @@ impl CinepakEncoder {
             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.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))
+                    (Self::can_update_cb(&self.v1_cur_cb[self.cur_strip][..self.v1_len], &self.v1_cb[self.cur_strip][..self.v1_len], cb_size),
+                     Self::can_update_cb(&self.v4_cur_cb[self.cur_strip][..self.v4_len], &self.v4_cb[self.cur_strip][..self.v4_len], cb_size))
                 } else {
                     (false, false)
                 };
@@ -724,8 +726,8 @@ impl CinepakEncoder {
             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.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::write_cb(bw, 0x20, &self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], self.grayscale, upd_v4, self.v4_len)?;
+            Self::write_cb(bw, 0x22, &self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], self.grayscale, upd_v1, self.v1_len)?;
 
             self.render_stripe(true, start_line, end_line);
 
@@ -828,7 +830,7 @@ impl CinepakEncoder {
                 } else {
                     self.masks.put_inter(false);
                 }
-                if v1_dist == 0 {
+                if v1_dist == 0 || self.force_v1 {
                     self.masks.put_v1();
                     self.v1_idx.push(v1_idx);
                     continue;
@@ -852,8 +854,8 @@ impl CinepakEncoder {
 
             let (upd_v1, upd_v4) = {
                     let cb_size = if self.grayscale { 4 } else { 6 };
-                    (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))
+                    (Self::can_update_cb(&self.v1_cur_cb[self.cur_strip][..self.v1_len], &self.v1_cb[self.cur_strip][..self.v1_len], cb_size),
+                     Self::can_update_cb(&self.v4_cur_cb[self.cur_strip][..self.v4_len], &self.v4_cb[self.cur_strip][..self.v4_len], cb_size))
                 };
             bw.write_byte(0x11)?;
             bw.write_u24be(0)?; // strip size
@@ -863,8 +865,8 @@ impl CinepakEncoder {
             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.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::write_cb(bw, 0x20, &self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], self.grayscale, upd_v4, self.v4_len)?;
+            Self::write_cb(bw, 0x22, &self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], self.grayscale, upd_v1, self.v1_len)?;
 
             self.render_stripe(false, start_line, end_line);
 
@@ -940,6 +942,7 @@ impl NAEncoder for CinepakEncoder {
             }
         }
     }
+    fn get_capabilities(&self) -> u64 { 0 }
     fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
         match encinfo.format {
             NACodecTypeInfo::None => Err(EncoderError::FormatError),
@@ -981,6 +984,12 @@ impl NAEncoder for CinepakEncoder {
     fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
         let buf = frm.get_buffer();
         if let Some(ref vbuf) = buf.get_vbuf() {
+            if self.nstrips == 0 {
+                let (w, h) = vbuf.get_dimensions(0);
+                self.nstrips = ((((w * h) >> 4) + 1200) / 2400).max(1).min(3);
+                let strip_h = ((h + self.nstrips - 1) / self.nstrips + 3) & !3;
+                self.nstrips = (h + strip_h - 1) / strip_h;
+            }
             let cur_strips = self.v1_cb.len();
             if cur_strips != self.nstrips {
                 self.frmcount = 0;
@@ -1032,7 +1041,10 @@ const ENCODER_OPTS: &[NAOptionDefinition] = &[
         opt_type: NAOptionDefinitionType::Int(Some(0), Some(16)) },
     NAOptionDefinition {
         name: "quant_mode", description: "Quantisation mode",
-        opt_type: NAOptionDefinitionType::String(Some(&["elbg", "hybrid", "mediancut"])) },
+        opt_type: NAOptionDefinitionType::String(Some(&["elbg", "mediancut"])) },
+    NAOptionDefinition {
+        name: "force_v1", description: "Force coarse (V1-only) mode",
+        opt_type: NAOptionDefinitionType::Bool },
 ];
 
 impl NAOptionHandler for CinepakEncoder {
@@ -1056,12 +1068,16 @@ impl NAOptionHandler for CinepakEncoder {
                             if let NAValue::String(ref strval) = option.value {
                                 match strval.as_str() {
                                     "elbg"      => self.qmode = QuantMode::ELBG,
-                                    "hybrid"    => self.qmode = QuantMode::Hybrid,
                                     "mediancut" => self.qmode = QuantMode::MedianCut,
                                     _ => {},
                                 };
                             }
                         },
+                        "force_v1" => {
+                            if let NAValue::Bool(val) = option.value {
+                                self.force_v1 = val;
+                            }
+                        },
                         _ => {},
                     };
                 }
@@ -1073,6 +1089,7 @@ impl NAOptionHandler for CinepakEncoder {
             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())),
+            "force_v1" => Some(NAValue::Bool(self.force_v1)),
             _ => None,
         }
     }
@@ -1132,6 +1149,6 @@ mod test {
             };
         //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
         test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
-                          &[0xd73cb3c7, 0x30d59f90, 0x1d6e0e28, 0x5b72cc0c]);
+                          &[0x1d4690c8, 0x3b15b4b3, 0xc2df3c7b, 0x1a25b159]);
     }
 }