nentries: 0,
}
}
+ fn reset(&mut self) {
+ for bkt in self.buckets.iter_mut() {
+ bkt.clrs.clear();
+ }
+ self.nentries = 0;
+ }
fn add(&mut self, clr: [u8; 3]) {
let idx = Bucket::key(clr);
if self.buckets[idx].add(clr) {
}
}
+pub type PalSegment = (usize, [[u8; 3]; 256]);
+
+struct MultiCount {
+ glbl_hist: [u64; 1 << 15],
+ cur_hist: [u64; 1 << 15],
+ frameno: usize,
+ fstart: usize,
+ min_grp: usize,
+ max_grp: usize,
+ pals: Vec<PalSegment>,
+}
+
+impl MultiCount {
+ fn new() -> Option<Box<Self>> {
+ Some(Box::new(Self {
+ glbl_hist: [0; 1 << 15],
+ cur_hist: [0; 1 << 15],
+ frameno: 0,
+ fstart: 0,
+ min_grp: 32,
+ max_grp: 0,
+ pals: Vec::new()
+ }))
+ }
+ fn calc_hist(&mut self, src: &[u8], stride: usize, width: usize, height: usize) {
+ for el in self.cur_hist.iter_mut() {
+ *el = 0;
+ }
+ for line in src.chunks_exact(stride).take(height) {
+ for pix in line.chunks_exact(3).take(width) {
+ let idx = (usize::from(pix[0] >> 3) << 10) |
+ (usize::from(pix[1] >> 3) << 5) |
+ usize::from(pix[2] >> 3);
+ self.cur_hist[idx] += 1;
+ }
+ }
+ }
+ fn merge_hist(&mut self) {
+ for (dst, &src) in self.glbl_hist.iter_mut().zip(self.cur_hist.iter()) {
+ *dst += src;
+ }
+ }
+ fn replace_hist(&mut self) {
+ self.glbl_hist.copy_from_slice(&self.cur_hist);
+ }
+ fn hist_differs(&self) -> bool {
+ if self.max_grp != 0 && self.frameno >= self.fstart + self.max_grp {
+ true
+ } else if self.frameno >= self.fstart + self.min_grp {
+ let scale = (self.frameno - self.fstart) as u64;
+ let (corr, acorr) = self.glbl_hist.iter().zip(self.cur_hist.iter())
+ .fold((0u64, 0u64), |acc, (&gg, &cc)|
+ (acc.0 + (gg / scale) * cc, acc.1 + cc * cc));
+ corr < acorr / 4
+ } else {
+ false
+ }
+ }
+}
+
enum CounterType {
Buckets(BucketCounter),
Brawn(Vec<u64>),
oinfo: NAVideoInfo,
ctype: CounterType,
debug: bool,
+ multi: Option<Box<MultiCount>>,
}
impl ColourCounter {
pub fn new(options: &[OptionArgs]) -> Self {
let mut counter_type = "full";
let mut debug = false;
+ let mut multi = None;
for opt in options.iter() {
match opt.name.as_str() {
"counter" => {
"debug" => {
debug = true;
},
+ "multi" => {
+ if multi.is_none() {
+ multi = MultiCount::new();
+ }
+ },
+ "min_group" => {
+ if let Some(val) = opt.value.as_deref() {
+ if let Ok(gsize) = val.parse::<usize>() {
+ if gsize >= 8 {
+ if multi.is_none() {
+ multi = MultiCount::new();
+ }
+ if let Some(ref mut mlti) = multi {
+ mlti.min_grp = gsize;
+ }
+ } else {
+ println!("too small group size, ignoring");
+ }
+ } else {
+ println!("group size requires a numeric argument");
+ }
+ } else {
+ println!("group size requires a numeric argument");
+ }
+ },
+ "max_group" => {
+ if let Some(val) = opt.value.as_deref() {
+ if let Ok(gsize) = val.parse::<usize>() {
+ if gsize >= 8 {
+ if multi.is_none() {
+ multi = MultiCount::new();
+ }
+ if let Some(ref mut mlti) = multi {
+ mlti.max_grp = gsize;
+ }
+ } else {
+ println!("too small group size, ignoring");
+ }
+ } else {
+ println!("group size requires a numeric argument");
+ }
+ } else {
+ println!("group size requires a numeric argument");
+ }
+ },
_ => {},
}
}
scaler: None,
sc_buf: NABufferType::None,
oinfo: NAVideoInfo{ width: 0, height: 0, flipped: false, format: RGB24_FORMAT, bits: 24 },
- ctype, debug,
+ ctype, debug, multi,
}
}
pub fn add(&mut self, clr: [u8; 3]) {
},
}
}
+ fn reset(&mut self) {
+ match self.ctype {
+ CounterType::Buckets(ref mut bkt) => { bkt.reset(); },
+ CounterType::Brawn(ref mut hist) => {
+ for el in hist.iter_mut() {
+ *el = 0;
+ }
+ },
+ }
+ }
fn add_rgb24(&mut self, buf: &NAVideoBuffer<u8>, width: usize, height: usize) {
let src = buf.get_data();
let stride = buf.get_stride(0);
+ let mut new_pal = false;
+ let mut pal_start = 0;
+ let mut pal_end = 0;
+ if let Some(ref mut multi) = self.multi {
+ multi.calc_hist(src, stride, width, height);
+ if multi.hist_differs() {
+ new_pal = true;
+ pal_start = multi.fstart;
+ pal_end = multi.frameno;
+ multi.replace_hist();
+ } else {
+ multi.merge_hist();
+ }
+ multi.frameno += 1;
+ }
+ if new_pal {
+ if self.debug {
+ println!(" new pal segment {pal_start}..{pal_end}");
+ }
+ let pal = self.get_pal();
+ self.reset();
+ if let Some(ref mut multi) = self.multi {
+ multi.pals.push((multi.fstart, pal));
+ multi.fstart = multi.frameno - 1;
+ }
+ }
for line in src.chunks_exact(stride).take(height) {
for pix in line.chunks_exact(3).take(width) {
let clr = pix.try_into().unwrap();
},
}
}
+ pub fn get_multi_pals(&self) -> Vec<PalSegment> {
+ let last_pal = self.get_pal();
+ if let Some(ref multi) = self.multi {
+ let mut pals = multi.pals.clone();
+ if multi.fstart < multi.frameno {
+ pals.push((multi.fstart, last_pal));
+ }
+ return pals;
+ }
+ vec![(0, last_pal)]
+ }
}
pub last_ts: Option<u64>,
pub plt: Option<Palettiser>,
pub plt_buf: NABufferType,
+ pub pals: Vec<PalSegment>,
+ pub pal_frm: usize,
}
impl EncoderInterface for VideoEncodeContext {
fn encode_frame(&mut self, dst_id: u32, frm: NAFrameRef, scale_opts: &[(String, String)], queue: &mut OutputQueue, dbg: &mut Option<DebugLog>) -> EncoderResult<bool> {
let buf = frm.get_buffer();
+ if let Some(ref mut plt) = self.plt {
+ if !self.pals.is_empty() && !matches!(buf, NABufferType::None) {
+ if self.pal_frm >= self.pals[0].0 {
+ let (_, pal) = self.pals.remove(0);
+ plt.set_pal(&pal);
+ }
+ self.pal_frm += 1;
+ }
+ }
if let Some(vinfo) = buf.get_video_info() {
let mut tgt_vinfo = self.vinfo;
if self.plt.is_some() {
pub nframes: Vec<usize>,
pub global_tb: (u32, u32),
pub fixed_rate: bool,
- pub glbl_pal: Vec<(usize, [[u8; 3]; 256])>,
+ pub pals: Vec<(usize, Vec<PalSegment>)>,
}
#[derive(Default)]
let mut plt = create_palettiser(&oopts.enc_opts);
oopts.enc_opts.retain(|opt| !opt.name.starts_with("pal."));
- for (src_id, gpal) in qsupp.glbl_pal.iter() {
- if *src_id == iidx {
+ for (src_id, pals) in qsupp.pals.iter() {
+ if *src_id == iidx && !pals.is_empty() {
if let Some(ref mut p) = plt {
- p.set_pal(gpal);
+ p.set_pal(&pals[0].1);
} else {
- plt = Some(Palettiser::new(Palettiser::get_default_mode(), gpal));
+ plt = Some(Palettiser::new(Palettiser::get_default_mode(), &pals[0].1));
}
}
}
println!("Timebase {}/{} is too much for constant framerate!", enc_stream.tb_num, enc_stream.tb_den);
return None;
}
+ let cur_pals = if let Some((idx, _)) = qsupp.pals.iter().enumerate().find(|(_, (sidx, _))| *sidx == iidx) {
+ let (_, pals) = qsupp.pals.swap_remove(idx);
+ pals
+ } else {
+ Vec::new()
+ };
if svinfo == dvinfo && !forced_out {
Box::new(VideoEncodeContext {
encoder,
tb_den: enc_stream.tb_den,
plt,
plt_buf: NABufferType::None,
+ pals: cur_pals,
+ pal_frm: 0,
})
} else {
let tgt_fmt = if plt.is_some() { RGB24_FORMAT } else { dvinfo.format };
tb_den: enc_stream.tb_den,
plt,
plt_buf: NABufferType::None,
+ pals: cur_pals,
+ pal_frm: 0
})
}
},