]> git.nihav.org Git - nihav.git/blame_incremental - nihav-ms/src/codecs/msvideo1enc.rs
avimux: do not record palette change chunks in OpenDML index
[nihav.git] / nihav-ms / src / codecs / msvideo1enc.rs
... / ...
CommitLineData
1use nihav_core::codecs::*;
2use nihav_core::io::byteio::*;
3
4type UnpackedPixel = [u16; 4];
5
6fn map_quality_pal(quality: u8) -> (u32, u32) {
7 if quality == 0 {
8 (0, 0)
9 } else {
10 let skip_threshold = (10 - (u32::from(quality) / 10).min(10)) * (8 << 6);
11 let fill_threshold = (10 - (u32::from(quality) / 10).min(10)) * (16 << 6);
12 (skip_threshold, fill_threshold)
13 }
14}
15
16fn map_quality_15bit(quality: u8) -> (u32, u32) {
17 if quality == 0 {
18 (0, 0)
19 } else {
20 let skip_threshold = (10 - (u32::from(quality) / 10).min(10)) * 8;
21 let fill_threshold = (10 - (u32::from(quality) / 10).min(10)) * 16;
22 (skip_threshold, fill_threshold)
23 }
24}
25
26trait PixelOps {
27 fn unpack(&self) -> UnpackedPixel;
28}
29
30impl PixelOps for u16 {
31 fn unpack(&self) -> UnpackedPixel {
32 let val = *self;
33 let r = (val >> 10) & 0x1F;
34 let g = (val >> 5) & 0x1F;
35 let b = val & 0x1F;
36 [r, g, b, rgb2y(r, g, b)]
37 }
38}
39
40fn dist_core(val: UnpackedPixel, other: &UnpackedPixel) -> u32 {
41 let sum = val.iter().zip(other.iter()).take(3).fold(0i32,
42 |acc, (&a, &b)| {
43 let diff = i32::from(a) - i32::from(b);
44 acc + diff * diff
45 });
46 sum as u32
47}
48
49fn find_nearest(pix: UnpackedPixel, pal: &[UnpackedPixel; 256]) -> usize {
50 let mut bestidx = 0;
51 let mut bestdist = u32::MAX;
52
53 for (idx, entry) in pal.iter().enumerate() {
54 let dist = dist_core(pix, entry);
55 if dist == 0 {
56 return idx;
57 }
58 if bestdist > dist {
59 bestdist = dist;
60 bestidx = idx;
61 }
62 }
63 bestidx
64}
65
66struct LocalSearch {
67 pal: [UnpackedPixel; 256],
68 db: Vec<Vec<UnpackedPixel>>,
69}
70
71impl LocalSearch {
72 fn quant(key: UnpackedPixel) -> usize {
73 (((key[0] >> 3) as usize) << 10) |
74 (((key[1] >> 3) as usize) << 5) |
75 ((key[2] >> 3) as usize)
76 }
77 fn new() -> Self {
78 let mut db = Vec::with_capacity(1 << 15);
79 for _ in 0..(1 << 15) {
80 db.push(Vec::new());
81 }
82 Self {
83 pal: [UnpackedPixel::default(); 256],
84 db
85 }
86 }
87 fn recalculate(&mut self, pal: &[UnpackedPixel; 256]) {
88 self.pal = *pal;
89 for vec in self.db.iter_mut() {
90 vec.clear();
91 }
92 for (i, palentry) in pal.iter().enumerate() {
93 let r0 = (palentry[0] >> 3) as usize;
94 let g0 = (palentry[1] >> 3) as usize;
95 let b0 = (palentry[2] >> 3) as usize;
96 for r in r0.saturating_sub(1)..=(r0 + 1).min(31) {
97 for g in g0.saturating_sub(1)..=(g0 + 1).min(31) {
98 for b in b0.saturating_sub(1)..=(b0 + 1).min(31) {
99 let idx = (r << 10) | (g << 5) | b;
100 self.db[idx].push([palentry[0], palentry[1], palentry[2], i as u16]);
101 }
102 }
103 }
104 }
105 }
106 fn search(&self, pix: UnpackedPixel) -> usize {
107 let idx = Self::quant(pix);
108 let mut best_dist = u32::MAX;
109 let mut best_idx = 0;
110 let mut count = 0;
111 for clr in self.db[idx].iter() {
112 let dist = dist_core(pix, clr);
113 count += 1;
114 if best_dist > dist {
115 best_dist = dist;
116 best_idx = clr[3] as usize;
117 if dist == 0 { break; }
118 }
119 }
120 if count > 0 {
121 best_idx
122 } else {
123 find_nearest(pix, &self.pal)
124 }
125 }
126}
127
128fn rgb2y(r: u16, g: u16, b: u16) -> u16 {
129 (r * 77 + g * 150 + b * 29) >> 8
130}
131
132fn pack_rgb555(val: UnpackedPixel) -> u16 {
133 (val[0] << 10) | (val[1] << 5) | val[2]
134}
135
136#[derive(Default)]
137struct PixelAverage {
138 sum: UnpackedPixel,
139 count: u16,
140}
141
142impl PixelAverage {
143 fn new() -> Self { Self::default() }
144 fn add(&mut self, val: &UnpackedPixel) {
145 for (dst, &src) in self.sum.iter_mut().zip(val.iter()) {
146 *dst += src;
147 }
148 self.count += 1;
149 }
150 fn get_avg(&self) -> UnpackedPixel {
151 if self.count > 0 {
152 let mut ret = self.sum;
153 for el in ret.iter_mut() {
154 *el /= self.count;
155 }
156 ret
157 } else {
158 [0; 4]
159 }
160 }
161}
162
163macro_rules! quant_template {
164 ($name:ident, $N:expr) => {
165 fn $name(pix: &[UnpackedPixel; $N]) -> ([UnpackedPixel; 2], u16, u32) {
166 let mut avg = PixelAverage::new();
167 let mut maxv = [0; 4];
168 let mut minv = [255; 4];
169 for src in pix.iter() {
170 avg.add(src);
171 for ((maxv, minv), &comp) in maxv.iter_mut().zip(minv.iter_mut()).zip(src.iter()) {
172 *maxv = (*maxv).max(comp);
173 *minv = (*minv).min(comp);
174 }
175 }
176 let avg = avg.get_avg();
177
178 let mut best_axis = 3;
179 let mut best_dist = maxv[3] - minv[3];
180 for (comp_no, (&minval, &maxval)) in minv.iter().zip(maxv.iter()).enumerate().take(3) {
181 if maxval - minval > best_dist {
182 best_axis = comp_no;
183 best_dist = maxval - minval;
184 }
185 }
186 if best_dist == 0 {
187 let mut dist = 0;
188 for el in pix.iter() {
189 dist += dist_core(avg, el);
190 }
191 return ([avg; 2], 0, dist);
192 }
193
194 let mut avg1 = PixelAverage::new();
195 let mut avg2 = PixelAverage::new();
196 let mut mask = 0;
197 let mut mask_bit = 1;
198 for clr in pix.iter() {
199 if clr[best_axis] > avg[best_axis] {
200 avg2.add(clr);
201 } else {
202 avg1.add(clr);
203 mask |= mask_bit;
204 }
205 mask_bit <<= 1;
206 }
207
208 let clr0 = avg1.get_avg();
209 let clr1 = avg2.get_avg();
210 let mut dist = 0;
211 for clr in pix.iter() {
212 let dist0 = dist_core(clr0, clr);
213 let dist1 = dist_core(clr1, clr);
214 dist += dist0.min(dist1);
215 }
216 ([clr0, clr1], mask, dist)
217 }
218 }
219}
220
221quant_template!(quant2_16pix, 16);
222quant_template!(quant2_4pix, 4);
223
224#[derive(Default)]
225struct BlockState {
226 fill_dist: u32,
227 fill_val: UnpackedPixel,
228 clr2_dist: u32,
229 clr2_flags: u16,
230 clr2: [UnpackedPixel; 2],
231 clr8_dist: u32,
232 clr8_flags: u16,
233 clr8: [[UnpackedPixel; 2]; 4],
234 pal_mode: bool,
235}
236
237impl BlockState {
238 fn new_pal() -> Self { Self { pal_mode: true, ..Default::default() } }
239 fn set_fill_val(&mut self, val: UnpackedPixel) {
240 self.fill_val = val;
241 if !self.pal_mode {
242 self.fill_val[0] &= !1;
243 }
244 }
245 fn calc_clrs(buf: &[UnpackedPixel; 16]) -> (Option<UnpackedPixel>, Option<UnpackedPixel>) {
246 let clr0 = buf[0];
247 let mut clr1 = clr0;
248 let mut single = true;
249 for &pix in buf[1..].iter() {
250 if pix != clr0 {
251 if single {
252 clr1 = pix;
253 single = false;
254 } else if pix != clr1 {
255 return (None, None);
256 }
257 }
258 }
259 if !single {
260 (Some(clr0), Some(clr1))
261 } else {
262 (Some(clr0), None)
263 }
264 }
265 fn calc_stats(&mut self, buf: &[UnpackedPixel; 16]) {
266 let mut filled = false;
267 let mut two_clr = false;
268 match Self::calc_clrs(buf) {
269 (Some(clr0), Some(clr1)) => {
270 self.clr2[0] = clr0;
271 self.clr2[1] = clr1;
272 two_clr = true;
273 },
274 (Some(clr0), None) => {
275 self.clr2[0] = clr0;
276 self.clr2[1] = clr0;
277 self.set_fill_val(buf[0]);
278 filled = true;
279 two_clr = true;
280 },
281 _ => {},
282 };
283 self.fill_dist = 0;
284 if !filled {
285 let mut avg = PixelAverage::new();
286 for pix in buf.iter() {
287 avg.add(pix);
288 }
289 self.set_fill_val(avg.get_avg());
290 for pix in buf.iter() {
291 self.fill_dist += dist_core(self.fill_val, pix);
292 }
293 }
294 if self.fill_dist == 0 {
295 self.clr2_dist = u32::MAX;
296 self.clr8_dist = u32::MAX;
297 return;
298 }
299
300 self.clr2_flags = 0u16;
301 if two_clr {
302 let mut mask = 1;
303 self.clr2_dist = 0;
304 for &pix in buf.iter() {
305 if pix == self.clr2[0] {
306 self.clr2_flags |= mask;
307 } else {
308 }
309 mask <<= 1;
310 }
311 if (self.clr2_flags & 0x8000) != 0 {
312 self.clr2_flags = !self.clr2_flags;
313 self.clr2.swap(0, 1);
314 }
315 } else {
316 let (clrs, mask, dist) = quant2_16pix(buf);
317 self.clr2 = clrs;
318 self.clr2_flags = mask;
319 self.clr2_dist = dist;
320 if (self.clr2_flags & 0x8000) != 0 {
321 self.clr2_flags = !self.clr2_flags;
322 self.clr2.swap(0, 1);
323 }
324 }
325 if self.clr2_dist == 0 {
326 self.clr8_dist = u32::MAX;
327 return;
328 }
329
330 self.clr8 = [[UnpackedPixel::default(); 2]; 4];
331 self.clr8_flags = 0;
332 self.clr8_dist = 0;
333 for i in 0..4 {
334 let off = (i & 1) * 2 + (i & 2) * 4;
335 let src2 = [buf[off], buf[off + 1], buf[off + 4], buf[off + 5]];
336 let (clrs, mask, dist) = quant2_4pix(&src2);
337 self.clr8[i] = clrs;
338 let lo_bits = mask & 0x3;
339 let hi_bits = (mask & 0xC) << 2;
340 self.clr8_flags |= (hi_bits | lo_bits) << ((i & 1) * 2 + (i & 2) * 4);
341 self.clr8_dist += dist;
342 }
343 }
344}
345
346struct BlockPainterPal<'a> {
347 ls: &'a LocalSearch,
348}
349impl<'a> BlockPainterPal<'a> {
350 fn new(ls: &'a LocalSearch) -> Self { Self{ ls } }
351 fn find_index(&self, pix: UnpackedPixel) -> u8 { self.ls.search(pix) as u8 }
352 fn put_fill(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> u8 {
353 let fill_val = self.find_index(bstate.fill_val);
354 for line in dst.chunks_mut(dstride).take(4) {
355 for pix in line[..4].iter_mut() {
356 *pix = fill_val;
357 }
358 }
359 fill_val
360 }
361 fn put_clr2(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> [u8; 2] {
362 let clr2 = [self.find_index(bstate.clr2[0]), self.find_index(bstate.clr2[1])];
363 for (j, dline) in dst.chunks_mut(dstride).take(4).enumerate() {
364 for (i, pix) in dline[..4].iter_mut().enumerate() {
365 if (bstate.clr2_flags & (1 << (i + j * 4))) == 0 {
366 *pix = clr2[0];
367 } else {
368 *pix = clr2[1];
369 }
370 }
371 }
372 clr2
373 }
374 fn put_clr8(&self, bstate: &BlockState, dst: &mut [u8], dstride: usize) -> [[u8; 4]; 4] {
375 let mut clr8 = [[0; 4]; 4];
376 for (dst, src) in clr8.iter_mut().zip(bstate.clr8.iter()) {
377 for (dst, &src) in dst.iter_mut().zip(src.iter()) {
378 *dst = self.find_index(src);
379 }
380 }
381 let mut clr8_flags = bstate.clr8_flags;
382 let swap = (clr8_flags & 0x8000) == 0;
383 if swap {
384 clr8_flags ^= 0xFF00;
385 }
386 if clr8_flags < 0x9000 {
387 clr8_flags |= 0x1000;
388 }
389 if swap {
390 clr8_flags ^= 0xFF00;
391 }
392 for (j, line) in dst.chunks_mut(dstride).take(4).enumerate() {
393 for (i, el) in line.iter_mut().take(4).enumerate() {
394 let blk_no = (i >> 1) + (j & 2);
395 *el = clr8[blk_no][(!clr8_flags & 1) as usize];
396 clr8_flags >>= 1;
397 }
398 }
399 clr8
400 }
401}
402
403struct BlockWriterPal {}
404impl BlockWriterPal {
405 fn write_fill(bw: &mut ByteWriter, fill_val: u8) -> EncoderResult<()> {
406 bw.write_byte(fill_val)?;
407 bw.write_byte(0x80)?;
408 Ok(())
409 }
410 fn write_clr2(bw: &mut ByteWriter, clr2_flags: u16, clr2: [u8; 2]) -> EncoderResult<()> {
411 bw.write_u16le(clr2_flags)?;
412 bw.write_byte(clr2[0])?;
413 bw.write_byte(clr2[1])?;
414 Ok(())
415 }
416 fn write_clr8(bw: &mut ByteWriter, mut clr8_flags: u16, mut clr8: [[u8; 4]; 4]) -> EncoderResult<()> {
417 if (clr8_flags & 0x8000) == 0 {
418 clr8_flags ^= 0xFF00;
419 clr8[2].swap(0, 1);
420 clr8[3].swap(0, 1);
421 }
422 if clr8_flags < 0x9000 {
423 clr8_flags |= 0x1000;
424 }
425
426 bw.write_u16le(clr8_flags)?;
427 bw.write_byte(clr8[0][0])?;
428 bw.write_byte(clr8[0][1])?;
429 bw.write_byte(clr8[1][0])?;
430 bw.write_byte(clr8[1][1])?;
431 bw.write_byte(clr8[2][0])?;
432 bw.write_byte(clr8[2][1])?;
433 bw.write_byte(clr8[3][0])?;
434 bw.write_byte(clr8[3][1])?;
435 Ok(())
436 }
437}
438
439struct BlockPainter15 {}
440impl BlockPainter15 {
441 fn new() -> Self { Self{} }
442 fn put_fill(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> u16 {
443 let fill_val = pack_rgb555(bstate.fill_val);
444 for line in dst.chunks_mut(dstride) {
445 for pix in line[..4].iter_mut() {
446 *pix = fill_val;
447 }
448 }
449 fill_val
450 }
451 fn put_clr2(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> [u16; 2] {
452 let clr2 = [pack_rgb555(bstate.clr2[0]), pack_rgb555(bstate.clr2[1])];
453 for (j, dline) in dst.chunks_mut(dstride).take(4).enumerate() {
454 for (i, pix) in dline[..4].iter_mut().enumerate() {
455 if (bstate.clr2_flags & (1 << (i + j * 4))) == 0 {
456 *pix = clr2[0];
457 } else {
458 *pix = clr2[1];
459 }
460 }
461 }
462 clr2
463 }
464 fn put_clr8(&self, bstate: &BlockState, dst: &mut [u16], dstride: usize) -> [[u16; 4]; 4] {
465 let mut clr8 = [[0; 4]; 4];
466 for (dst, src) in clr8.iter_mut().zip(bstate.clr8.iter()) {
467 for (dst, &src) in dst.iter_mut().zip(src.iter()) {
468 *dst = pack_rgb555(src);
469 }
470 }
471 let mut clr8_flags = bstate.clr8_flags;
472 for (j, line) in dst.chunks_mut(dstride).take(4).enumerate() {
473 for (i, el) in line.iter_mut().take(4).enumerate() {
474 let blk_no = (i >> 1) + (j & 2);
475 *el = clr8[blk_no][(!clr8_flags & 1) as usize];
476 clr8_flags >>= 1;
477 }
478 }
479 clr8
480 }
481}
482
483struct BlockWriter15 {}
484impl BlockWriter15 {
485 fn write_fill(bw: &mut ByteWriter, fill_val: u16) -> EncoderResult<()> {
486 bw.write_u16le(fill_val | 0x8000)?;
487 Ok(())
488 }
489 fn write_clr2(bw: &mut ByteWriter, clr2_flags: u16, clr2: [u16; 2]) -> EncoderResult<()> {
490 bw.write_u16le(clr2_flags)?;
491 bw.write_u16le(clr2[0])?;
492 bw.write_u16le(clr2[1])?;
493 Ok(())
494 }
495 fn write_clr8(bw: &mut ByteWriter, mut clr8_flags: u16, mut clr8: [[u16; 4]; 4]) -> EncoderResult<()> {
496 if (clr8_flags & 0x8000) != 0 {
497 clr8_flags ^= 0xFF00;
498 clr8[2].swap(0, 1);
499 clr8[3].swap(0, 1);
500 }
501 bw.write_u16le(clr8_flags)?;
502 bw.write_u16le(clr8[0][0] | 0x8000)?;
503 bw.write_u16le(clr8[0][1])?;
504 bw.write_u16le(clr8[1][0])?;
505 bw.write_u16le(clr8[1][1])?;
506 bw.write_u16le(clr8[2][0])?;
507 bw.write_u16le(clr8[2][1])?;
508 bw.write_u16le(clr8[3][0])?;
509 bw.write_u16le(clr8[3][1])?;
510 Ok(())
511 }
512}
513
514struct MSVideo1Encoder {
515 stream: Option<NAStreamRef>,
516 pkt: Option<NAPacket>,
517 pool8: NAVideoBufferPool<u8>,
518 pool15: NAVideoBufferPool<u16>,
519 lastfrm8: Option<NAVideoBufferRef<u8>>,
520 lastfrm15: Option<NAVideoBufferRef<u16>>,
521 quality: u8,
522 frmcount: u8,
523 key_int: u8,
524
525 pal_mode: bool,
526 pal: [UnpackedPixel; 256],
527 ls: LocalSearch,
528}
529
530impl MSVideo1Encoder {
531 fn new() -> Self {
532 Self {
533 stream: None,
534 pkt: None,
535 pool8: NAVideoBufferPool::new(2),
536 pool15: NAVideoBufferPool::new(2),
537 lastfrm8: None,
538 lastfrm15: None,
539 quality: 0,
540 frmcount: 0,
541 key_int: 25,
542
543 pal_mode: false,
544 pal: [UnpackedPixel::default(); 256],
545 ls: LocalSearch::new(),
546 }
547 }
548 fn get_block(src: &[u16], sstride: usize, buf: &mut [UnpackedPixel; 16]) {
549 for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) {
550 for (dst, src) in dst.iter_mut().zip(line.iter()) {
551 *dst = src.unpack();
552 }
553 }
554 }
555 fn get_block8(src: &[u8], sstride: usize, buf: &mut [UnpackedPixel; 16], pal: &[UnpackedPixel; 256]) {
556 for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) {
557 for (dst, src) in dst.iter_mut().zip(line.iter()) {
558 *dst = pal[usize::from(*src)];
559 }
560 }
561 }
562 fn write_skips(bw: &mut ByteWriter, skips: usize) -> EncoderResult<()> {
563 bw.write_u16le((skips as u16) | 0x8400)?;
564 Ok(())
565 }
566 fn encode_inter_rgb555(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, prev_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
567 let (skip_threshold, fill_threshold) = map_quality_15bit(quality);
568 let mut is_intra = true;
569 let src = in_frm.get_data();
570 let sstride = in_frm.get_stride(0);
571 let soff = in_frm.get_offset(0);
572 let (w, h) = in_frm.get_dimensions(0);
573 let rsrc = prev_frm.get_data();
574 let rstride = prev_frm.get_stride(0);
575 let roff = prev_frm.get_offset(0);
576 let dstride = cur_frm.get_stride(0);
577 let doff = cur_frm.get_offset(0);
578 let dst = cur_frm.get_data_mut().unwrap();
579 let mut skip_run = 0;
580 let bpainter = BlockPainter15::new();
581 for ((sstrip, rstrip), dstrip) in src[soff..].chunks(sstride * 4).take(h / 4).zip(rsrc[roff..].chunks(rstride * 4)).zip(dst[doff..].chunks_mut(dstride * 4)) {
582 for x in (0..w).step_by(4) {
583 let mut buf = [UnpackedPixel::default(); 16];
584 let mut refbuf = [UnpackedPixel::default(); 16];
585 Self::get_block(&sstrip[x..], sstride, &mut buf);
586 Self::get_block(&rstrip[x..], rstride, &mut refbuf);
587
588 let mut skip_dist = 0;
589 for (pix, rpix) in buf.iter().zip(refbuf.iter()) {
590 skip_dist += dist_core(*rpix, pix);
591 }
592 if skip_dist <= skip_threshold {
593 skip_run += 1;
594 is_intra = false;
595 for (dst, src) in dstrip[x..].chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
596 dst[..4].copy_from_slice(&src[..4]);
597 }
598 if skip_run == 1023 {
599 Self::write_skips(bw, skip_run)?;
600 skip_run = 0;
601 }
602 continue;
603 }
604
605 let mut bstate = BlockState::default();
606 bstate.calc_stats(&buf);
607
608 let dst = &mut dstrip[x..];
609 if skip_dist <= bstate.fill_dist && skip_dist * 2 <= bstate.clr2_dist {
610 skip_run += 1;
611 is_intra = false;
612 for (dst, src) in dst.chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
613 dst[..4].copy_from_slice(&src[..4]);
614 }
615 if skip_run == 1023 {
616 Self::write_skips(bw, skip_run)?;
617 skip_run = 0;
618 }
619 } else if bstate.fill_dist <= fill_threshold ||
620 bstate.fill_dist <= bstate.clr2_dist {
621 let fill_val = bpainter.put_fill(&bstate, dst, dstride);
622 if skip_run != 0 {
623 Self::write_skips(bw, skip_run)?;
624 skip_run = 0;
625 }
626 BlockWriter15::write_fill(bw, fill_val)?;
627 } else if bstate.clr8_dist < bstate.clr2_dist {
628 let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
629 if skip_run != 0 {
630 Self::write_skips(bw, skip_run)?;
631 skip_run = 0;
632 }
633 BlockWriter15::write_clr8(bw, bstate.clr8_flags, clr8)?;
634 } else {
635 let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
636 if skip_run != 0 {
637 Self::write_skips(bw, skip_run)?;
638 skip_run = 0;
639 }
640 BlockWriter15::write_clr2(bw, bstate.clr2_flags, clr2)?;
641 }
642 }
643 }
644 if skip_run != 0 {
645 Self::write_skips(bw, skip_run)?;
646 }
647 if is_intra {
648 bw.write_u16le(0)?;
649 } //xxx: something for inter?
650 Ok(is_intra)
651 }
652 fn encode_intra_rgb555(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, quality: u8) -> EncoderResult<bool> {
653 let (_, fill_threshold) = map_quality_15bit(quality);
654 let src = in_frm.get_data();
655 let sstride = in_frm.get_stride(0);
656 let soff = in_frm.get_offset(0);
657 let (w, h) = in_frm.get_dimensions(0);
658 let dstride = cur_frm.get_stride(0);
659 let doff = cur_frm.get_offset(0);
660 let dst = cur_frm.get_data_mut().unwrap();
661 let bpainter = BlockPainter15::new();
662 for (sstrip, dstrip) in src[soff..].chunks(sstride * 4).take(h / 4).zip(dst[doff..].chunks_mut(dstride * 4)) {
663 for x in (0..w).step_by(4) {
664 let mut buf = [UnpackedPixel::default(); 16];
665 Self::get_block(&sstrip[x..], sstride, &mut buf);
666 let mut bstate = BlockState::default();
667 bstate.calc_stats(&buf);
668
669 let dst = &mut dstrip[x..];
670 if bstate.fill_dist <= fill_threshold ||
671 bstate.fill_dist <= bstate.clr2_dist {
672 let fill_val = bpainter.put_fill(&bstate, dst, dstride);
673 BlockWriter15::write_fill(bw, fill_val)?;
674 } else if bstate.clr8_dist < bstate.clr2_dist {
675 let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
676 BlockWriter15::write_clr8(bw, bstate.clr8_flags, clr8)?;
677 } else {
678 let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
679 BlockWriter15::write_clr2(bw, bstate.clr2_flags, clr2)?;
680 }
681 }
682 }
683 bw.write_u16le(0)?;
684 Ok(true)
685 }
686 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> {
687 let (skip_threshold, fill_threshold) = map_quality_pal(quality);
688 let mut is_intra = true;
689 let src = in_frm.get_data();
690 let sstride = in_frm.get_stride(0);
691 let soff = in_frm.get_offset(0);
692 let (w, h) = in_frm.get_dimensions(0);
693 let rsrc = prev_frm.get_data();
694 let rstride = prev_frm.get_stride(0);
695 let roff = prev_frm.get_offset(0);
696 let dstride = cur_frm.get_stride(0);
697 let doff = cur_frm.get_offset(0);
698 let dst = cur_frm.get_data_mut().unwrap();
699 let mut skip_run = 0;
700 let bpainter = BlockPainterPal::new(ls);
701 for ((sstrip, rstrip), dstrip) in src[soff..].chunks(sstride * 4).take(h / 4).zip(rsrc[roff..].chunks(rstride * 4)).zip(dst[doff..].chunks_mut(dstride * 4)) {
702 for x in (0..w).step_by(4) {
703 let mut buf = [UnpackedPixel::default(); 16];
704 let mut refbuf = [UnpackedPixel::default(); 16];
705 Self::get_block8(&sstrip[x..], sstride, &mut buf, pal);
706 Self::get_block8(&rstrip[x..], rstride, &mut refbuf, pal);
707
708 let mut skip_dist = 0;
709 for (pix, rpix) in buf.iter().zip(refbuf.iter()) {
710 skip_dist += dist_core(*rpix, pix);
711 }
712 if skip_dist <= skip_threshold {
713 skip_run += 1;
714 is_intra = false;
715 for (dst, src) in dstrip[x..].chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
716 dst[..4].copy_from_slice(&src[..4]);
717 }
718 if skip_run == 1023 {
719 Self::write_skips(bw, skip_run)?;
720 skip_run = 0;
721 }
722 continue;
723 }
724
725 let mut bstate = BlockState::new_pal();
726 bstate.calc_stats(&buf);
727
728 let dst = &mut dstrip[x..];
729 if skip_dist <= bstate.fill_dist && skip_dist * 2 <= bstate.clr2_dist {
730 skip_run += 1;
731 is_intra = false;
732 for (dst, src) in dst.chunks_mut(dstride).zip(rstrip[x..].chunks(rstride)).take(4) {
733 dst[..4].copy_from_slice(&src[..4]);
734 }
735 if skip_run == 1023 {
736 Self::write_skips(bw, skip_run)?;
737 skip_run = 0;
738 }
739 } else if bstate.fill_dist <= fill_threshold ||
740 bstate.fill_dist <= bstate.clr2_dist {
741 let fill_val = bpainter.put_fill(&bstate, dst, dstride);
742 if skip_run != 0 {
743 Self::write_skips(bw, skip_run)?;
744 skip_run = 0;
745 }
746 BlockWriterPal::write_fill(bw, fill_val)?;
747 } else if bstate.clr8_dist < bstate.clr2_dist {
748 let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
749 if skip_run != 0 {
750 Self::write_skips(bw, skip_run)?;
751 skip_run = 0;
752 }
753 BlockWriterPal::write_clr8(bw, bstate.clr8_flags, clr8)?;
754 } else {
755 let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
756 if skip_run != 0 {
757 Self::write_skips(bw, skip_run)?;
758 skip_run = 0;
759 }
760 BlockWriterPal::write_clr2(bw, bstate.clr2_flags, clr2)?;
761 }
762 }
763 }
764 if skip_run != 0 {
765 Self::write_skips(bw, skip_run)?;
766 }
767 if is_intra {
768 bw.write_u16le(0)?;
769 } //xxx: something for inter?
770 Ok(is_intra)
771 }
772 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> {
773 let (_, fill_threshold) = map_quality_pal(quality);
774 let src = in_frm.get_data();
775 let sstride = in_frm.get_stride(0);
776 let soff = in_frm.get_offset(0);
777 let (w, h) = in_frm.get_dimensions(0);
778 let dstride = cur_frm.get_stride(0);
779 let doff = cur_frm.get_offset(0);
780 let dst = cur_frm.get_data_mut().unwrap();
781 let bpainter = BlockPainterPal::new(ls);
782 for (sstrip, dstrip) in src[soff..].chunks(sstride * 4).take(h / 4).zip(dst[doff..].chunks_mut(dstride * 4)) {
783 for x in (0..w).step_by(4) {
784 let mut buf = [UnpackedPixel::default(); 16];
785 Self::get_block8(&sstrip[x..], sstride, &mut buf, pal);
786 let mut bstate = BlockState::new_pal();
787 bstate.calc_stats(&buf);
788
789 let dst = &mut dstrip[x..];
790 if bstate.fill_dist <= fill_threshold ||
791 bstate.fill_dist <= bstate.clr2_dist {
792 let fill_val = bpainter.put_fill(&bstate, dst, dstride);
793 BlockWriterPal::write_fill(bw, fill_val)?;
794 } else if bstate.clr8_dist < bstate.clr2_dist {
795 let clr8 = bpainter.put_clr8(&bstate, dst, dstride);
796 BlockWriterPal::write_clr8(bw, bstate.clr8_flags, clr8)?;
797 } else {
798 let clr2 = bpainter.put_clr2(&bstate, dst, dstride);
799 BlockWriterPal::write_clr2(bw, bstate.clr2_flags, clr2)?;
800 }
801 }
802 }
803 bw.write_u16le(0)?;
804 Ok(true)
805 }
806}
807
808const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton {
809 model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
810 comp_info: [
811 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
812 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }),
813 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }),
814 None, None],
815 elem_size: 2, be: false, alpha: false, palette: false };
816
817impl NAEncoder for MSVideo1Encoder {
818 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
819 match encinfo.format {
820 NACodecTypeInfo::None => {
821 Ok(EncodeParameters {
822 format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, RGB555_FORMAT)),
823 ..Default::default() })
824 },
825 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
826 NACodecTypeInfo::Video(vinfo) => {
827 let oformat = if vinfo.format == PAL8_FORMAT { PAL8_FORMAT } else { RGB555_FORMAT };
828 let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, oformat);
829 let mut ofmt = *encinfo;
830 ofmt.format = NACodecTypeInfo::Video(outinfo);
831 Ok(ofmt)
832 }
833 }
834 }
835 fn get_capabilities(&self) -> u64 { ENC_CAPS_SKIPFRAME }
836 #[allow(clippy::collapsible_else_if)]
837 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
838 match encinfo.format {
839 NACodecTypeInfo::None => Err(EncoderError::FormatError),
840 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
841 NACodecTypeInfo::Video(vinfo) => {
842 if vinfo.format != RGB555_FORMAT && vinfo.format != PAL8_FORMAT {
843 return Err(EncoderError::FormatError);
844 }
845 if ((vinfo.width | vinfo.height) & 3) != 0 {
846 return Err(EncoderError::FormatError);
847 }
848
849 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, vinfo.format);
850 let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info), None);
851 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
852 stream.set_num(stream_id as usize);
853 let stream = stream.into_ref();
854 self.pal_mode = vinfo.format.is_paletted();
855
856 if !self.pal_mode {
857 if self.pool15.prealloc_video(out_info, 2).is_err() {
858 return Err(EncoderError::AllocError);
859 }
860 } else {
861 if self.pool8.prealloc_video(out_info, 2).is_err() {
862 return Err(EncoderError::AllocError);
863 }
864 }
865
866 self.stream = Some(stream.clone());
867 self.quality = encinfo.quality;
868
869 Ok(stream)
870 },
871 }
872 }
873 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
874 let buf = frm.get_buffer();
875 if frm.frame_type == FrameType::Skip {
876 if let Some(ref stream) = self.stream {
877 let mut dbuf = Vec::with_capacity(4);
878 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
879 let mut bw = ByteWriter::new(&mut gw);
880
881 let vinfo = stream.get_info().get_properties().get_video_info().unwrap();
882 let mut nskips = ((vinfo.get_width() + 3) / 4) * ((vinfo.get_height() + 3) / 4);
883 while nskips >= 1023 {
884 Self::write_skips(&mut bw, 1023)?;
885 nskips -= 1023;
886 }
887 if nskips > 0 {
888 Self::write_skips(&mut bw, nskips)?;
889 }
890 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, false, dbuf));
891 self.frmcount += 1;
892 if self.frmcount == self.key_int {
893 self.frmcount = 0;
894 }
895 return Ok(());
896 } else {
897 return Err(EncoderError::Bug);
898 }
899 }
900 if let Some(ref vbuf) = buf.get_vbuf16() {
901 if self.pal_mode {
902 return Err(EncoderError::InvalidParameters);
903 }
904 let mut cur_frm = self.pool15.get_free().unwrap();
905 let mut dbuf = Vec::with_capacity(4);
906 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
907 let mut bw = ByteWriter::new(&mut gw);
908 if self.frmcount == 0 {
909 self.lastfrm15 = None;
910 }
911 let is_intra = if let Some(ref prev_buf) = self.lastfrm15 {
912 Self::encode_inter_rgb555(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)?
913 } else {
914 Self::encode_intra_rgb555(&mut bw, &mut cur_frm, vbuf, self.quality)?
915 };
916 self.lastfrm15 = Some(cur_frm);
917 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
918 self.frmcount += 1;
919 if self.frmcount == self.key_int {
920 self.frmcount = 0;
921 }
922 Ok(())
923 } else if let Some(ref vbuf) = buf.get_vbuf() {
924 if !self.pal_mode {
925 return Err(EncoderError::InvalidParameters);
926 }
927 let src = vbuf.get_data();
928 let pal = &src[vbuf.get_offset(1)..];
929 let mut pal_changed = false;
930 for (cur_pal, new_pal) in self.pal.iter_mut().zip(pal.chunks_exact(3)) {
931 let (cur_clr, luma) = cur_pal.split_at_mut(3);
932 let new_clr = [u16::from(new_pal[0]), u16::from(new_pal[1]), u16::from(new_pal[2])];
933 if cur_clr != new_clr {
934 pal_changed = true;
935 cur_clr.copy_from_slice(&new_clr);
936 luma[0] = rgb2y(cur_clr[0], cur_clr[1], cur_clr[2]);
937 }
938 }
939
940 if pal_changed {
941 self.ls.recalculate(&self.pal);
942 }
943
944 let mut cur_frm = self.pool8.get_free().unwrap();
945 let mut dbuf = Vec::with_capacity(4);
946 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
947 let mut bw = ByteWriter::new(&mut gw);
948 if self.frmcount == 0 {
949 self.lastfrm8 = None;
950 }
951 let is_intra = if let Some(ref prev_buf) = self.lastfrm8 {
952 Self::encode_inter_pal(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality, &self.pal, &self.ls)?
953 } else {
954 Self::encode_intra_pal(&mut bw, &mut cur_frm, vbuf, self.quality, &self.pal, &self.ls)?
955 };
956 self.lastfrm8 = Some(cur_frm);
957 let mut pkt = NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf);
958 if pal_changed {
959 let mut epal = [0; 1024];
960 for (dst, src) in epal.chunks_mut(4).zip(self.pal.iter()) {
961 dst[0] = src[0] as u8;
962 dst[1] = src[1] as u8;
963 dst[2] = src[2] as u8;
964 }
965 pkt.add_side_data(NASideData::Palette(true, Arc::new(epal)));
966 }
967 self.pkt = Some(pkt);
968 self.frmcount += 1;
969 if self.frmcount == self.key_int {
970 self.frmcount = 0;
971 }
972 Ok(())
973 } else {
974 Err(EncoderError::InvalidParameters)
975 }
976 }
977 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
978 let mut npkt = None;
979 std::mem::swap(&mut self.pkt, &mut npkt);
980 Ok(npkt)
981 }
982 fn flush(&mut self) -> EncoderResult<()> {
983 self.frmcount = 0;
984 Ok(())
985 }
986}
987
988const ENCODER_OPTS: &[NAOptionDefinition] = &[
989 NAOptionDefinition {
990 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
991 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
992];
993
994impl NAOptionHandler for MSVideo1Encoder {
995 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
996 fn set_options(&mut self, options: &[NAOption]) {
997 for option in options.iter() {
998 for opt_def in ENCODER_OPTS.iter() {
999 if opt_def.check(option).is_ok() {
1000 match option.name {
1001 KEYFRAME_OPTION => {
1002 if let NAValue::Int(intval) = option.value {
1003 self.key_int = intval as u8;
1004 }
1005 },
1006 _ => {},
1007 };
1008 }
1009 }
1010 }
1011 }
1012 fn query_option_value(&self, name: &str) -> Option<NAValue> {
1013 match name {
1014 KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
1015 _ => None,
1016 }
1017 }
1018}
1019
1020pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
1021 Box::new(MSVideo1Encoder::new())
1022}
1023
1024#[cfg(test)]
1025mod test {
1026 use nihav_core::codecs::*;
1027 use nihav_core::demuxers::*;
1028 use nihav_core::muxers::*;
1029 use crate::*;
1030 use nihav_commonfmt::*;
1031 use nihav_codec_support::test::enc_video::*;
1032 use super::RGB555_FORMAT;
1033
1034 #[test]
1035 fn test_ms_video1_encoder_pal() {
1036 let mut dmx_reg = RegisteredDemuxers::new();
1037 generic_register_all_demuxers(&mut dmx_reg);
1038 let mut dec_reg = RegisteredDecoders::new();
1039 generic_register_all_decoders(&mut dec_reg);
1040 ms_register_all_decoders(&mut dec_reg);
1041 let mut mux_reg = RegisteredMuxers::new();
1042 generic_register_all_muxers(&mut mux_reg);
1043 let mut enc_reg = RegisteredEncoders::new();
1044 ms_register_all_encoders(&mut enc_reg);
1045
1046 // sample: https://samples.mplayerhq.hu/V-codecs/RLE/mplayer-msrle-4bit.avi
1047 let dec_config = DecoderTestParams {
1048 demuxer: "avi",
1049 in_name: "assets/MS/mplayer-msrle-4bit.avi",
1050 stream_type: StreamType::Video,
1051 limit: Some(3),
1052 dmx_reg, dec_reg,
1053 };
1054 let enc_config = EncoderTestParams {
1055 muxer: "avi",
1056 enc_name: "msvideo1",
1057 out_name: "msvideo1pal.avi",
1058 mux_reg, enc_reg,
1059 };
1060 let dst_vinfo = NAVideoInfo {
1061 width: 0,
1062 height: 0,
1063 format: PAL8_FORMAT,
1064 flipped: true,
1065 bits: 8,
1066 };
1067 let enc_params = EncodeParameters {
1068 format: NACodecTypeInfo::Video(dst_vinfo),
1069 quality: 0,
1070 bitrate: 0,
1071 tb_num: 0,
1072 tb_den: 0,
1073 flags: 0,
1074 };
1075 //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
1076 test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
1077 &[0x5afaf853, 0xd53ba9dd, 0x630f53e7, 0x41b33a36]);
1078 }
1079
1080 #[test]
1081 fn test_ms_video1_encoder_rgb555() {
1082 let mut dmx_reg = RegisteredDemuxers::new();
1083 generic_register_all_demuxers(&mut dmx_reg);
1084 let mut dec_reg = RegisteredDecoders::new();
1085 generic_register_all_decoders(&mut dec_reg);
1086 ms_register_all_decoders(&mut dec_reg);
1087 let mut mux_reg = RegisteredMuxers::new();
1088 generic_register_all_muxers(&mut mux_reg);
1089 let mut enc_reg = RegisteredEncoders::new();
1090 ms_register_all_encoders(&mut enc_reg);
1091
1092 // sample: https://samples.mplayerhq.hu/V-codecs/UCOD/TalkingHead_352x288.avi
1093 let dec_config = DecoderTestParams {
1094 demuxer: "avi",
1095 in_name: "assets/Misc/TalkingHead_352x288.avi",
1096 stream_type: StreamType::Video,
1097 limit: Some(3),
1098 dmx_reg, dec_reg,
1099 };
1100 let enc_config = EncoderTestParams {
1101 muxer: "avi",
1102 enc_name: "msvideo1",
1103 out_name: "msvideo1.avi",
1104 mux_reg, enc_reg,
1105 };
1106 let dst_vinfo = NAVideoInfo {
1107 width: 0,
1108 height: 0,
1109 format: RGB555_FORMAT,
1110 flipped: true,
1111 bits: 16,
1112 };
1113 let enc_params = EncodeParameters {
1114 format: NACodecTypeInfo::Video(dst_vinfo),
1115 quality: 80,
1116 bitrate: 0,
1117 tb_num: 0,
1118 tb_den: 0,
1119 flags: 0,
1120 };
1121 //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
1122 test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
1123 &[0xb3175a7b, 0x4a6cb45e, 0x526f3f5d, 0xaa1574cc]);
1124 }
1125}