}
}
+#[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,
v1_entries: Vec<YUVCode>,
v4_cb: [YUVCode; 256],
v1_cur_cb: [YUVCode; 256],
v4_cur_cb: [YUVCode; 256],
+ v1_len: usize,
+ v4_len: usize,
v1_idx: Vec<u8>,
v4_idx: Vec<u8>,
grayscale: bool,
pkt: None,
lastfrm: None,
frmcount: 0,
+ qmode: QuantMode::MedianCut,
+ key_int: 25,
quality: 0,
nstrips: 2,
v1_entries: Vec::new(),
v4_cb: [YUVCode::default(); 256],
v1_cur_cb: [YUVCode::default(); 256],
v4_cur_cb: [YUVCode::default(); 256],
+ v1_len: 0,
+ v4_len: 0,
grayscale: false,
rng: RNG::new(),
v1_idx: Vec::new(),
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() {
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;
unreachable!();
}
}
+ fn quant_vectors(&mut self) {
+ match self.qmode {
+ QuantMode::ELBG => {
+ let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cb);
+ let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cb);
+
+ for entry in self.v1_cb.iter_mut().skip(self.v1_len) {
+ self.rng.fill_entry(entry);
+ }
+ for entry in self.v4_cb.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.v4_len = elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb);
+ },
+ QuantMode::Hybrid => {
+ 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);
+ let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cur_cb);
+ let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cur_cb);
+ self.v1_len = elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb);
+ self.v4_len = elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb);
+ },
+ QuantMode::MedianCut => {
+ self.v1_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb);
+ self.v4_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb);
+ },
+ };
+
+ for e in self.v1_cur_cb.iter_mut().skip(self.v1_len) { *e = YUVCode::default(); }
+ for e in self.v4_cur_cb.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;
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);
+ self.quant_vectors();
if self.grayscale {
for cw in self.v1_cur_cb.iter_mut() {
cw.u = 128;
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.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.v4_len], v4_entries[0]);
+ let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb[..self.v4_len], v4_entries[1]);
+ let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb[..self.v4_len], v4_entries[2]);
+ let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb[..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);
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)?;
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() {
cw.u = 128;
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.v1_len], *v1_entry);
if skip_dist < v1_dist {
self.masks.put_inter(true);
continue;
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.v4_len], v4_entries[0]);
+ let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb[..self.v4_len], v4_entries[1]);
+ let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb[..self.v4_len], v4_entries[2]);
+ let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb[..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);
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 mut ofmt = EncodeParameters::default();
+ 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)
}
}
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;
let buf = alloc_video_buffer(out_info, 2)?;
self.lastfrm = Some(buf.get_vbuf().unwrap());
-
+
Ok(stream)
},
}
};
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(())
}
}
+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> {
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();
height: 0,
format: YUV420_FORMAT,
flipped: true,
+ bits: 12,
};
let enc_params = EncodeParameters {
format: NACodecTypeInfo::Video(dst_vinfo),
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,
+ &[0x1a71b529, 0x9453fe1a, 0xab2be3f5, 0x55623bab]);
}
}