aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKostya Shishkov <kostya.shishkov@gmail.com>2023-02-08 17:36:49 +0100
committerKostya Shishkov <kostya.shishkov@gmail.com>2023-02-08 17:36:49 +0100
commit608a923fdacd068d33f904a5181691bbae221b94 (patch)
tree7fbcaa365e02e5e60d33abd9e849a7b30408cbe8
parent3c4707815eabbbb185639734bc7411747cb91066 (diff)
downloadnihav-608a923fdacd068d33f904a5181691bbae221b94.tar.gz
msvideo1enc: add paletted input support
-rw-r--r--nihav-ms/src/codecs/msvideo1enc.rs471
1 files changed, 450 insertions, 21 deletions
diff --git a/nihav-ms/src/codecs/msvideo1enc.rs b/nihav-ms/src/codecs/msvideo1enc.rs
index 0733215..3c89f7a 100644
--- a/nihav-ms/src/codecs/msvideo1enc.rs
+++ b/nihav-ms/src/codecs/msvideo1enc.rs
@@ -3,7 +3,17 @@ use nihav_core::io::byteio::*;
type UnpackedPixel = [u16; 4];
-fn map_quality(quality: u8) -> (u32, u32) {
+fn map_quality_pal(quality: u8) -> (u32, u32) {
+ if quality == 0 {
+ (0, 0)
+ } else {
+ let skip_threshold = (10 - (u32::from(quality) / 10).min(10)) * (8 << 6);
+ let fill_threshold = (10 - (u32::from(quality) / 10).min(10)) * (16 << 6);
+ (skip_threshold, fill_threshold)
+ }
+}
+
+fn map_quality_15bit(quality: u8) -> (u32, u32) {
if quality == 0 {
(0, 0)
} else {
@@ -39,6 +49,84 @@ fn dist_core(val: UnpackedPixel, other: &UnpackedPixel) -> u32 {
sum as u32
}
+fn find_nearest(pix: UnpackedPixel, pal: &[UnpackedPixel; 256]) -> usize {
+ let mut bestidx = 0;
+ let mut bestdist = std::u32::MAX;
+
+ for (idx, entry) in pal.iter().enumerate() {
+ let dist = dist_core(pix, entry);
+ if dist == 0 {
+ return idx;
+ }
+ if bestdist > dist {
+ bestdist = dist;
+ bestidx = idx;
+ }
+ }
+ bestidx
+}
+
+struct LocalSearch {
+ pal: [UnpackedPixel; 256],
+ db: Vec<Vec<UnpackedPixel>>,
+}
+
+impl LocalSearch {
+ fn quant(key: UnpackedPixel) -> usize {
+ (((key[0] >> 3) as usize) << 10) |
+ (((key[1] >> 3) as usize) << 5) |
+ ((key[2] >> 3) as usize)
+ }
+ fn new() -> Self {
+ let mut db = Vec::with_capacity(1 << 15);
+ for _ in 0..(1 << 15) {
+ db.push(Vec::new());
+ }
+ Self {
+ pal: [UnpackedPixel::default(); 256],
+ db
+ }
+ }
+ fn recalculate(&mut self, pal: &[UnpackedPixel; 256]) {
+ self.pal = *pal;
+ for vec in self.db.iter_mut() {
+ vec.clear();
+ }
+ for (i, palentry) in pal.iter().enumerate() {
+ let r0 = (palentry[0] >> 3) as usize;
+ let g0 = (palentry[1] >> 3) as usize;
+ let b0 = (palentry[2] >> 3) as usize;
+ for r in r0.saturating_sub(1)..=(r0 + 1).min(31) {
+ for g in g0.saturating_sub(1)..=(g0 + 1).min(31) {
+ for b in b0.saturating_sub(1)..=(b0 + 1).min(31) {
+ let idx = (r << 10) | (g << 5) | b;
+ self.db[idx].push([palentry[0], palentry[1], palentry[2], i as u16]);
+ }
+ }
+ }
+ }
+ }
+ fn search(&self, pix: UnpackedPixel) -> usize {
+ let idx = Self::quant(pix);
+ let mut best_dist = std::u32::MAX;
+ let mut best_idx = 0;
+ let mut count = 0;
+ for clr in self.db[idx].iter() {
+ let dist = dist_core(pix, clr);
+ count += 1;
+ if best_dist > dist {
+ best_dist = dist;
+ best_idx = clr[3] as usize;
+ if dist == 0 { break; }
+ }
+ }
+ if count > 0 {
+ best_idx
+ } else {
+ find_nearest(pix, &self.pal)
+ }
+ }
+}
fn rgb2y(r: u16, g: u16, b: u16) -> u16 {
(r * 77 + g * 150 + b * 29) >> 8
@@ -150,6 +238,7 @@ struct BlockState {
}
impl BlockState {
+ fn new_pal() -> Self { Self { pal_mode: true, ..Default::default() } }
fn set_fill_val(&mut self, val: UnpackedPixel) {
self.fill_val = val;
if !self.pal_mode {
@@ -257,6 +346,99 @@ impl BlockState {
}
}
+struct BlockPainterPal<'a> {
+ ls: &'a LocalSearch,
+}
+impl<'a> BlockPainterPal<'a> {
+ fn new(ls: &'a LocalSearch) -> Self { Self{ ls } }
+ fn find_index(&self, pix: UnpackedPixel) -> u8 { self.ls.search(pix) as u8 }
+ fn put_fill(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> u8 {
+ let fill_val = self.find_index(bstate.fill_val);
+ for line in dst.chunks_mut(dstride) {
+ for i in 0..4 {
+ line[i] = fill_val;
+ }
+ }
+ fill_val
+ }
+ fn put_clr2(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> [u8; 2] {
+ let clr2 = [self.find_index(bstate.clr2[0]), self.find_index(bstate.clr2[1])];
+ for j in 0..4 {
+ for i in 0..4 {
+ if (bstate.clr2_flags & (1 << (i + j * 4))) == 0 {
+ dst[i + j * dstride] = clr2[0];
+ } else {
+ dst[i + j * dstride] = clr2[1];
+ }
+ }
+ }
+ clr2
+ }
+ fn put_clr8(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> [[u8; 4]; 4] {
+ let mut clr8 = [[0; 4]; 4];
+ for (dst, src) in clr8.iter_mut().zip(bstate.clr8.iter()) {
+ for (dst, &src) in dst.iter_mut().zip(src.iter()) {
+ *dst = self.find_index(src);
+ }
+ }
+ let mut clr8_flags = bstate.clr8_flags;
+ let swap = (clr8_flags & 0x8000) == 0;
+ if swap {
+ clr8_flags ^= 0xFF00;
+ }
+ if clr8_flags < 0x9000 {
+ clr8_flags |= 0x1000;
+ }
+ if swap {
+ clr8_flags ^= 0xFF00;
+ }
+ for (j, line) in dst.chunks_mut(dstride).take(4).enumerate() {
+ for (i, el) in line.iter_mut().take(4).enumerate() {
+ let blk_no = (i >> 1) + (j & 2);
+ *el = clr8[blk_no][(!clr8_flags & 1) as usize];
+ clr8_flags >>= 1;
+ }
+ }
+ clr8
+ }
+}
+
+struct BlockWriterPal {}
+impl BlockWriterPal {
+ fn write_fill(bw: &mut ByteWriter, fill_val: u8) -> EncoderResult<()> {
+ bw.write_byte(fill_val)?;
+ bw.write_byte(0x80)?;
+ Ok(())
+ }
+ fn write_clr2(bw: &mut ByteWriter, clr2_flags: u16, clr2: [u8; 2]) -> EncoderResult<()> {
+ bw.write_u16le(clr2_flags)?;
+ bw.write_byte(clr2[0])?;
+ bw.write_byte(clr2[1])?;
+ Ok(())
+ }
+ fn write_clr8(bw: &mut ByteWriter, mut clr8_flags: u16, mut clr8: [[u8; 4]; 4]) -> EncoderResult<()> {
+ if (clr8_flags & 0x8000) == 0 {
+ clr8_flags ^= 0xFF00;
+ clr8[2].swap(0, 1);
+ clr8[3].swap(0, 1);
+ }
+ if clr8_flags < 0x9000 {
+ clr8_flags |= 0x1000;
+ }
+
+ bw.write_u16le(clr8_flags)?;
+ bw.write_byte(clr8[0][0])?;
+ bw.write_byte(clr8[0][1])?;
+ bw.write_byte(clr8[1][0])?;
+ bw.write_byte(clr8[1][1])?;
+ bw.write_byte(clr8[2][0])?;
+ bw.write_byte(clr8[2][1])?;
+ bw.write_byte(clr8[3][0])?;
+ bw.write_byte(clr8[3][1])?;
+ Ok(())
+ }
+}
+
struct BlockPainter15 {}
impl BlockPainter15 {
fn new() -> Self { Self{} }
@@ -335,11 +517,17 @@ impl BlockWriter15 {
struct MSVideo1Encoder {
stream: Option<NAStreamRef>,
pkt: Option<NAPacket>,
- pool: NAVideoBufferPool<u16>,
- lastfrm: Option<NAVideoBufferRef<u16>>,
+ pool8: NAVideoBufferPool<u8>,
+ pool15: NAVideoBufferPool<u16>,
+ lastfrm8: Option<NAVideoBufferRef<u8>>,
+ lastfrm15: Option<NAVideoBufferRef<u16>>,
quality: u8,
frmcount: u8,
key_int: u8,
+
+ pal_mode: bool,
+ pal: [UnpackedPixel; 256],
+ ls: LocalSearch,
}
impl MSVideo1Encoder {
@@ -347,11 +535,17 @@ impl MSVideo1Encoder {
Self {
stream: None,
pkt: None,
- pool: NAVideoBufferPool::new(2),
- lastfrm: None,
+ pool8: NAVideoBufferPool::new(2),
+ pool15: NAVideoBufferPool::new(2),
+ lastfrm8: None,
+ lastfrm15: None,
quality: 0,
frmcount: 0,
key_int: 25,
+
+ pal_mode: false,
+ pal: [UnpackedPixel::default(); 256],
+ ls: LocalSearch::new(),
}
}
fn get_block(src: &[u16], sstride: usize, buf: &mut [UnpackedPixel; 16]) {
@@ -361,12 +555,19 @@ impl MSVideo1Encoder {
}
}
}
+ fn get_block8(src: &[u8], sstride: usize, buf: &mut [UnpackedPixel; 16], pal: &[UnpackedPixel; 256]) {
+ for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) {
+ for (dst, src) in dst.iter_mut().zip(line.iter()) {
+ *dst = pal[usize::from(*src)];
+ }
+ }
+ }
fn write_skips(bw: &mut ByteWriter, skips: usize) -> EncoderResult<()> {
bw.write_u16le((skips as u16) | 0x8400)?;
Ok(())
}
- fn encode_inter(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, prev_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
- let (skip_threshold, fill_threshold) = map_quality(quality);
+ fn encode_inter_rgb555(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, prev_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
+ let (skip_threshold, fill_threshold) = map_quality_15bit(quality);
let mut is_intra = true;
let src = in_frm.get_data();
let sstride = in_frm.get_stride(0);
@@ -451,8 +652,8 @@ impl MSVideo1Encoder {
} //xxx: something for inter?
Ok(is_intra)
}
- fn encode_intra(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
- let (_, fill_threshold) = map_quality(quality);
+ fn encode_intra_rgb555(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
+ let (_, fill_threshold) = map_quality_15bit(quality);
let src = in_frm.get_data();
let sstride = in_frm.get_stride(0);
let soff = in_frm.get_offset(0);
@@ -485,6 +686,126 @@ impl MSVideo1Encoder {
bw.write_u16le(0)?;
Ok(true)
}
+ fn encode_inter_pal(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u8>, in_frm: &NAVideoBuffer<u8>, prev_frm: &NAVideoBuffer<u8>, quality: u8, pal: &[UnpackedPixel; 256], ls: &LocalSearch) -> EncoderResult<bool> {
+ let (skip_threshold, fill_threshold) = map_quality_pal(quality);
+ let mut is_intra = true;
+ let src = in_frm.get_data();
+ let sstride = in_frm.get_stride(0);
+ let soff = in_frm.get_offset(0);
+ let (w, h) = in_frm.get_dimensions(0);
+ let rsrc = prev_frm.get_data();
+ let rstride = prev_frm.get_stride(0);
+ let roff = prev_frm.get_offset(0);
+ let dstride = cur_frm.get_stride(0);
+ let doff = cur_frm.get_offset(0);
+ let dst = cur_frm.get_data_mut().unwrap();
+ let mut skip_run = 0;
+ let bpainter = BlockPainterPal::new(ls);
+ for ((sstrip, rstrip), dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&rsrc[roff..]).chunks(rstride * 4)).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
+ for x in (0..w).step_by(4) {
+ let mut buf = [UnpackedPixel::default(); 16];
+ let mut refbuf = [UnpackedPixel::default(); 16];
+ Self::get_block8(&sstrip[x..], sstride, &mut buf, pal);
+ Self::get_block8(&rstrip[x..], rstride, &mut refbuf, pal);
+
+ let mut skip_dist = 0;
+ for (pix, rpix) in buf.iter().zip(refbuf.iter()) {
+ skip_dist += dist_core(*rpix, pix);
+ }
+ if skip_dist <= skip_threshold {
+ skip_run += 1;
+ is_intra = false;
+ for (dst, src) in dstrip[x..].chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
+ dst[..4].copy_from_slice(&src[..4]);
+ }
+ if skip_run == 1023 {
+ Self::write_skips(bw, skip_run)?;
+ skip_run = 0;
+ }
+ continue;
+ }
+
+ let mut bstate = BlockState::new_pal();
+ bstate.calc_stats(&buf);
+
+ let dst = &mut dstrip[x..];
+ if skip_dist <= bstate.fill_dist && skip_dist * 2 <= bstate.clr2_dist {
+ skip_run += 1;
+ is_intra = false;
+ for (dst, src) in dst.chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
+ dst[..4].copy_from_slice(&src[..4]);
+ }
+ if skip_run == 1023 {
+ Self::write_skips(bw, skip_run)?;
+ skip_run = 0;
+ }
+ } else if bstate.fill_dist <= fill_threshold ||
+ bstate.fill_dist <= bstate.clr2_dist {
+ let fill_val = bpainter.put_fill(&bstate, dst, dstride);
+ if skip_run != 0 {
+ Self::write_skips(bw, skip_run)?;
+ skip_run = 0;
+ }
+ BlockWriterPal::write_fill(bw, fill_val)?;
+ } else if bstate.clr8_dist < bstate.clr2_dist {
+ let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
+ if skip_run != 0 {
+ Self::write_skips(bw, skip_run)?;
+ skip_run = 0;
+ }
+ BlockWriterPal::write_clr8(bw, bstate.clr8_flags, clr8)?;
+ } else {
+ let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
+ if skip_run != 0 {
+ Self::write_skips(bw, skip_run)?;
+ skip_run = 0;
+ }
+ BlockWriterPal::write_clr2(bw, bstate.clr2_flags, clr2)?;
+ }
+ }
+ }
+ if skip_run != 0 {
+ Self::write_skips(bw, skip_run)?;
+ }
+ if is_intra {
+ bw.write_u16le(0)?;
+ } //xxx: something for inter?
+ Ok(is_intra)
+ }
+ fn encode_intra_pal(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u8>, in_frm: &NAVideoBuffer<u8>, quality: u8, pal: &[UnpackedPixel; 256], ls: &LocalSearch) -> EncoderResult<bool> {
+ let (_, fill_threshold) = map_quality_pal(quality);
+ let src = in_frm.get_data();
+ let sstride = in_frm.get_stride(0);
+ let soff = in_frm.get_offset(0);
+ let (w, h) = in_frm.get_dimensions(0);
+ let dstride = cur_frm.get_stride(0);
+ let doff = cur_frm.get_offset(0);
+ let dst = cur_frm.get_data_mut().unwrap();
+ let bpainter = BlockPainterPal::new(ls);
+ for (sstrip, dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
+ for x in (0..w).step_by(4) {
+ let mut buf = [UnpackedPixel::default(); 16];
+ Self::get_block8(&sstrip[x..], sstride, &mut buf, pal);
+ let mut bstate = BlockState::new_pal();
+ bstate.calc_stats(&buf);
+
+ let dst = &mut dstrip[x..];
+ if bstate.fill_dist <= fill_threshold ||
+ bstate.fill_dist <= bstate.clr2_dist {
+ let fill_val = bpainter.put_fill(&bstate, dst, dstride);
+ BlockWriterPal::write_fill(bw, fill_val)?;
+ } else if bstate.clr8_dist < bstate.clr2_dist {
+ let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
+ BlockWriterPal::write_clr8(bw, bstate.clr8_flags, clr8)?;
+ } else {
+ let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
+ BlockWriterPal::write_clr2(bw, bstate.clr2_flags, clr2)?;
+ }
+ }
+ }
+ bw.write_u16le(0)?;
+ Ok(true)
+ }
}
const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton {
@@ -506,7 +827,8 @@ impl NAEncoder for MSVideo1Encoder {
},
NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
NACodecTypeInfo::Video(vinfo) => {
- let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, RGB555_FORMAT);
+ let oformat = if vinfo.format == PAL8_FORMAT { PAL8_FORMAT } else { RGB555_FORMAT };
+ let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, oformat);
let mut ofmt = *encinfo;
ofmt.format = NACodecTypeInfo::Video(outinfo);
Ok(ofmt)
@@ -518,20 +840,28 @@ impl NAEncoder for MSVideo1Encoder {
NACodecTypeInfo::None => Err(EncoderError::FormatError),
NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
NACodecTypeInfo::Video(vinfo) => {
- if vinfo.format != RGB555_FORMAT {
+ if vinfo.format != RGB555_FORMAT && vinfo.format != PAL8_FORMAT {
return Err(EncoderError::FormatError);
}
if ((vinfo.width | vinfo.height) & 3) != 0 {
return Err(EncoderError::FormatError);
}
- let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT);
+ let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, vinfo.format);
let info = NACodecInfo::new("msvideo1", 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();
- if self.pool.prealloc_video(out_info, 2).is_err() {
- return Err(EncoderError::AllocError);
+ self.pal_mode = vinfo.format.is_paletted();
+
+ if !self.pal_mode {
+ if self.pool15.prealloc_video(out_info, 2).is_err() {
+ return Err(EncoderError::AllocError);
+ }
+ } else {
+ if self.pool8.prealloc_video(out_info, 2).is_err() {
+ return Err(EncoderError::AllocError);
+ }
}
self.stream = Some(stream.clone());
@@ -544,25 +874,78 @@ impl NAEncoder for MSVideo1Encoder {
fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
let buf = frm.get_buffer();
if let Some(ref vbuf) = buf.get_vbuf16() {
- let mut cur_frm = self.pool.get_free().unwrap();
+ if self.pal_mode {
+ return Err(EncoderError::InvalidParameters);
+ }
+ let mut cur_frm = self.pool15.get_free().unwrap();
let mut dbuf = Vec::with_capacity(4);
let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
let mut bw = ByteWriter::new(&mut gw);
if self.frmcount == 0 {
- self.lastfrm = None;
+ self.lastfrm15 = None;
}
- let is_intra = if let Some(ref prev_buf) = self.lastfrm {
- Self::encode_inter(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)?
+ let is_intra = if let Some(ref prev_buf) = self.lastfrm15 {
+ Self::encode_inter_rgb555(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)?
} else {
- Self::encode_intra(&mut bw, &mut cur_frm, vbuf, self.quality)?
+ Self::encode_intra_rgb555(&mut bw, &mut cur_frm, vbuf, self.quality)?
};
- self.lastfrm = Some(cur_frm);
+ self.lastfrm15 = Some(cur_frm);
self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
self.frmcount += 1;
if self.frmcount == self.key_int {
self.frmcount = 0;
}
Ok(())
+ } else if let Some(ref vbuf) = buf.get_vbuf() {
+ if !self.pal_mode {
+ return Err(EncoderError::InvalidParameters);
+ }
+ let src = vbuf.get_data();
+ let pal = &src[vbuf.get_offset(1)..];
+ let mut pal_changed = false;
+ for (cur_pal, new_pal) in self.pal.iter_mut().zip(pal.chunks_exact(3)) {
+ let (cur_clr, luma) = cur_pal.split_at_mut(3);
+ let new_clr = [u16::from(new_pal[0]), u16::from(new_pal[1]), u16::from(new_pal[2])];
+ if cur_clr != &new_clr {
+ pal_changed = true;
+ cur_clr.copy_from_slice(&new_clr);
+ luma[0] = rgb2y(cur_clr[0], cur_clr[1], cur_clr[2]);
+ }
+ }
+
+ if pal_changed {
+ self.ls.recalculate(&self.pal);
+ }
+
+ let mut cur_frm = self.pool8.get_free().unwrap();
+ let mut dbuf = Vec::with_capacity(4);
+ let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
+ let mut bw = ByteWriter::new(&mut gw);
+ if self.frmcount == 0 {
+ self.lastfrm8 = None;
+ }
+ let is_intra = if let Some(ref prev_buf) = self.lastfrm8 {
+ Self::encode_inter_pal(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality, &self.pal, &self.ls)?
+ } else {
+ Self::encode_intra_pal(&mut bw, &mut cur_frm, vbuf, self.quality, &self.pal, &self.ls)?
+ };
+ self.lastfrm8 = Some(cur_frm);
+ let mut pkt = NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf);
+ if pal_changed {
+ let mut epal = [0; 1024];
+ for (dst, src) in epal.chunks_mut(4).zip(self.pal.iter()) {
+ dst[0] = src[0] as u8;
+ dst[1] = src[1] as u8;
+ dst[2] = src[2] as u8;
+ }
+ pkt.add_side_data(NASideData::Palette(true, Arc::new(epal)));
+ }
+ self.pkt = Some(pkt);
+ self.frmcount += 1;
+ if self.frmcount == self.key_int {
+ self.frmcount = 0;
+ }
+ Ok(())
} else {
Err(EncoderError::InvalidParameters)
}
@@ -625,7 +1008,53 @@ mod test {
use super::RGB555_FORMAT;
#[test]
- fn test_ms_video1_encoder() {
+ fn test_ms_video1_encoder_pal() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ generic_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ generic_register_all_decoders(&mut dec_reg);
+ ms_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();
+ ms_register_all_encoders(&mut enc_reg);
+
+ // sample: https://samples.mplayerhq.hu/V-codecs/RLE/mplayer-msrle-4bit.avi
+ let dec_config = DecoderTestParams {
+ demuxer: "avi",
+ in_name: "assets/MS/mplayer-msrle-4bit.avi",
+ stream_type: StreamType::Video,
+ limit: Some(3),
+ dmx_reg, dec_reg,
+ };
+ let enc_config = EncoderTestParams {
+ muxer: "avi",
+ enc_name: "msvideo1",
+ out_name: "msvideo1pal.avi",
+ mux_reg, enc_reg,
+ };
+ let dst_vinfo = NAVideoInfo {
+ width: 0,
+ height: 0,
+ format: PAL8_FORMAT,
+ flipped: true,
+ bits: 8,
+ };
+ 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, &[]);
+ test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
+ &[0x27a9db38, 0x74f1000a, 0x38818c05, 0x99d692ba]);
+ }
+
+ #[test]
+ fn test_ms_video1_encoder_rgb555() {
let mut dmx_reg = RegisteredDemuxers::new();
generic_register_all_demuxers(&mut dmx_reg);
let mut dec_reg = RegisteredDecoders::new();