]> git.nihav.org Git - nihav.git/blob - nihav-ms/src/codecs/msvideo1enc.rs
msvideo1enc: switch to faster 2-colour quantiser
[nihav.git] / nihav-ms / src / codecs / msvideo1enc.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3
4 const INVALID_CLR: u16 = 0x8000;
5
6 trait InvalidPixel {
7 fn is_invalid(self) -> bool;
8 }
9 impl InvalidPixel for u16 {
10 fn is_invalid(self) -> bool { self == INVALID_CLR }
11 }
12
13 trait PixelOps {
14 fn unpack(&self) -> [u16; 4];
15 fn dist<T: PixelOps>(&self, val: T) -> u32 {
16 dist_core(self.unpack(), &val.unpack())
17 }
18 }
19
20 impl PixelOps for u16 {
21 fn unpack(&self) -> [u16; 4] {
22 let val = *self;
23 let r = (val >> 10) & 0x1F;
24 let g = (val >> 5) & 0x1F;
25 let b = val & 0x1F;
26 [r, g, b, rgb2y(r, g, b)]
27 }
28 }
29
30 fn dist_core(val: [u16; 4], other: &[u16; 4]) -> u32 {
31 let sum = val.iter().zip(other.iter()).take(3).fold(0i32,
32 |acc, (&a, &b)| {
33 let diff = i32::from(a) - i32::from(b);
34 acc + diff * diff
35 });
36 sum as u32
37 }
38
39
40 fn rgb2y(r: u16, g: u16, b: u16) -> u16 {
41 (r * 77 + g * 150 + b * 29) >> 8
42 }
43
44 fn pack_rgb555(val: [u16; 4]) -> u16 {
45 (val[0] << 10) | (val[1] << 5) | val[2]
46 }
47
48 #[derive(Default)]
49 struct PixelAverage {
50 sum: [u16; 4],
51 count: u16,
52 }
53
54 impl PixelAverage {
55 fn new() -> Self { Self::default() }
56 fn add(&mut self, val: &[u16; 4]) {
57 for (dst, &src) in self.sum.iter_mut().zip(val.iter()) {
58 *dst += src;
59 }
60 self.count += 1;
61 }
62 fn get_avg(&self) -> [u16; 4] {
63 if self.count > 0 {
64 let mut ret = self.sum;
65 for el in ret.iter_mut() {
66 *el /= self.count;
67 }
68 ret
69 } else {
70 [0; 4]
71 }
72 }
73 }
74
75 macro_rules! quant_template {
76 ($name:ident, $N:expr) => {
77 fn $name(pix: &[u16; $N]) -> ([u16; 2], u16, u32) {
78 let mut tmp = [[0; 4]; $N];
79 let mut avg = PixelAverage::new();
80 let mut maxv = [0; 4];
81 let mut minv = [255; 4];
82 for (dst, &src) in tmp.iter_mut().zip(pix.iter()) {
83 *dst = src.unpack();
84 avg.add(dst);
85 for ((maxv, minv), &comp) in maxv.iter_mut().zip(minv.iter_mut()).zip(dst.iter()) {
86 *maxv = (*maxv).max(comp);
87 *minv = (*minv).min(comp);
88 }
89 }
90 let avg = avg.get_avg();
91
92 let mut best_axis = 3;
93 let mut best_dist = maxv[3] - minv[3];
94 for (comp_no, (&minval, &maxval)) in minv.iter().zip(maxv.iter()).enumerate().take(3) {
95 if maxval - minval > best_dist {
96 best_axis = comp_no;
97 best_dist = maxval - minval;
98 }
99 }
100 if best_dist == 0 {
101 let avg_pix = pack_rgb555(avg);
102 let mut dist = 0;
103 for el in tmp.iter() {
104 dist += dist_core(avg, el);
105 }
106 return ([avg_pix; 2], 0, dist);
107 }
108
109 let mut avg1 = PixelAverage::new();
110 let mut avg2 = PixelAverage::new();
111 let mut mask = 0;
112 let mut mask_bit = 1;
113 for clr in tmp.iter() {
114 if clr[best_axis] > avg[best_axis] {
115 avg2.add(clr);
116 mask |= mask_bit;
117 } else {
118 avg1.add(clr);
119 }
120 mask_bit <<= 1;
121 }
122
123 let clr0 = avg1.get_avg();
124 let clr1 = avg2.get_avg();
125 let mut dist = 0;
126 for clr in tmp.iter() {
127 let dist0 = dist_core(clr0, clr);
128 let dist1 = dist_core(clr1, clr);
129 dist += dist0.min(dist1);
130 }
131 ([pack_rgb555(clr0), pack_rgb555(clr1)], mask, dist)
132 }
133 }
134 }
135
136 quant_template!(quant2_16pix, 16);
137 quant_template!(quant2_4pix, 4);
138
139 #[derive(Default)]
140 struct BlockState {
141 fill_dist: u32,
142 fill_val: u16,
143 clr2_dist: u32,
144 clr2_flags: u16,
145 clr2: [u16; 2],
146 clr8_dist: u32,
147 clr8_flags: u16,
148 clr8: [[u16; 2]; 4],
149 }
150
151 impl BlockState {
152 fn calc_clrs(buf: &[u16; 16]) -> Option<(u16, u16)> {
153 let clr0 = buf[0];
154 let mut clr1 = INVALID_CLR;
155 for &pix in buf[1..].iter() {
156 if pix != clr0 && pix != clr1 {
157 if clr1.is_invalid() {
158 clr1 = pix;
159 } else {
160 return None;
161 }
162 }
163 }
164 Some((clr0, clr1))
165 }
166 fn calc_stats(&mut self, buf: &[u16; 16]) {
167 let mut filled = false;
168 let mut two_clr = false;
169 if let Some((clr0, clr1)) = Self::calc_clrs(buf) {
170 self.clr2[0] = clr0;
171 self.clr2[1] = if !clr1.is_invalid() { clr1 } else { clr0 };
172 if clr0 == clr1 {
173 self.fill_val = buf[0] & !0x400;
174 filled = true;
175 }
176 two_clr = true;
177 }
178 self.fill_dist = 0;
179 if !filled {
180 let mut avg = PixelAverage::new();
181 for &pix in buf.iter() {
182 avg.add(&pix.unpack());
183 }
184 self.fill_val = pack_rgb555(avg.get_avg()) & !0x400;
185 for pix in buf.iter() {
186 self.fill_dist += pix.dist(self.fill_val);
187 }
188 }
189 if self.fill_dist == 0 {
190 self.clr2_dist = std::u32::MAX;
191 self.clr8_dist = std::u32::MAX;
192 return;
193 }
194
195 self.clr2_flags = 0u16;
196 if two_clr {
197 let mut mask = 1;
198 self.clr2_dist = 0;
199 for &pix in buf.iter() {
200 if pix == self.clr2[0] {
201 self.clr2_flags |= mask;
202 } else {
203 }
204 mask <<= 1;
205 }
206 if (self.clr2_flags & 0x8000) != 0 {
207 self.clr2_flags = !self.clr2_flags;
208 self.clr2.swap(0, 1);
209 }
210 } else {
211 let (clrs, mask, dist) = quant2_16pix(&buf);
212 self.clr2 = clrs;
213 self.clr2_flags = mask;
214 self.clr2_dist = dist;
215 if (self.clr2_flags & 0x8000) != 0 {
216 self.clr2_flags = !self.clr2_flags;
217 self.clr2.swap(0, 1);
218 }
219 }
220 if self.clr2_dist == 0 {
221 self.clr8_dist = std::u32::MAX;
222 return;
223 }
224
225 self.clr8 = [[0; 2]; 4];
226 self.clr8_flags = 0;
227 self.clr8_dist = 0;
228 for i in 0..4 {
229 let off = (i & 1) * 2 + (i & 2) * 4;
230 let src2 = [buf[off], buf[off + 1], buf[off + 4], buf[off + 5]];
231 let (clrs, mask, dist) = quant2_4pix(&src2);
232 self.clr8[i] = clrs;
233 self.clr8_flags |= mask << (i * 4);
234 self.clr8_dist += dist;
235 }
236 if (self.clr8_flags & 0x8000) != 0 {
237 self.clr8_flags ^= 0xF000;
238 self.clr8[3].swap(0, 1);
239 }
240 }
241 fn put_fill(&self, dst: &mut [u16], dstride: usize) {
242 for line in dst.chunks_mut(dstride) {
243 for i in 0..4 {
244 line[i] = self.fill_val;
245 }
246 }
247 }
248 fn put_clr2(&self, dst: &mut [u16], dstride: usize) {
249 for j in 0..4 {
250 for i in 0..4 {
251 if (self.clr2_flags & (1 << (i + j * 4))) == 0 {
252 dst[i + j * dstride] = self.clr2[0];
253 } else {
254 dst[i + j * dstride] = self.clr2[1];
255 }
256 }
257 }
258 }
259 fn put_clr8(&self, dst: &mut [u16], dstride: usize) {
260 for i in 0..4 {
261 let off = (i & 1) * 2 + (i & 2) * dstride;
262 let cur_flg = (self.clr8_flags >> (i * 4)) & 0xF;
263 dst[off] = self.clr8[i][( !cur_flg & 1) as usize];
264 dst[off + 1] = self.clr8[i][((!cur_flg >> 1) & 1) as usize];
265 dst[off + dstride] = self.clr8[i][((!cur_flg >> 2) & 1) as usize];
266 dst[off + 1 + dstride] = self.clr8[i][((!cur_flg >> 3) & 1) as usize];
267 }
268 }
269 fn write_fill(&self, bw: &mut ByteWriter) -> EncoderResult<()> {
270 bw.write_u16le(self.fill_val | 0x8000)?;
271 Ok(())
272 }
273 fn write_clr2(&self, bw: &mut ByteWriter) -> EncoderResult<()> {
274 bw.write_u16le(self.clr2_flags)?;
275 bw.write_u16le(self.clr2[0])?;
276 bw.write_u16le(self.clr2[1])?;
277 Ok(())
278 }
279 fn write_clr8(&self, bw: &mut ByteWriter) -> EncoderResult<()> {
280 bw.write_u16le(self.clr8_flags)?;
281 bw.write_u16le(self.clr8[0][0] | 0x8000)?;
282 bw.write_u16le(self.clr8[0][1])?;
283 bw.write_u16le(self.clr8[1][0])?;
284 bw.write_u16le(self.clr8[1][1])?;
285 bw.write_u16le(self.clr8[2][0])?;
286 bw.write_u16le(self.clr8[2][1])?;
287 bw.write_u16le(self.clr8[3][0])?;
288 bw.write_u16le(self.clr8[3][1])?;
289 Ok(())
290 }
291 }
292
293 struct MSVideo1Encoder {
294 stream: Option<NAStreamRef>,
295 pkt: Option<NAPacket>,
296 pool: NAVideoBufferPool<u16>,
297 lastfrm: Option<NAVideoBufferRef<u16>>,
298 quality: u8,
299 frmcount: u8,
300 key_int: u8,
301 }
302
303 impl MSVideo1Encoder {
304 fn new() -> Self {
305 Self {
306 stream: None,
307 pkt: None,
308 pool: NAVideoBufferPool::new(2),
309 lastfrm: None,
310 quality: 0,
311 frmcount: 0,
312 key_int: 25,
313 }
314 }
315 fn get_block(src: &[u16], sstride: usize, buf: &mut [u16; 16]) {
316 for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) {
317 dst.copy_from_slice(&line[..4]);
318 }
319 }
320 fn write_skips(bw: &mut ByteWriter, skips: usize) -> EncoderResult<()> {
321 bw.write_u16le((skips as u16) | 0x8400)?;
322 Ok(())
323 }
324 fn encode_inter(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, prev_frm: &NAVideoBuffer<u16>, _quality: u8) -> EncoderResult<bool> {
325 let mut is_intra = true;
326 let src = in_frm.get_data();
327 let sstride = in_frm.get_stride(0);
328 let soff = in_frm.get_offset(0);
329 let (w, h) = in_frm.get_dimensions(0);
330 let rsrc = prev_frm.get_data();
331 let rstride = prev_frm.get_stride(0);
332 let roff = prev_frm.get_offset(0);
333 let dstride = cur_frm.get_stride(0);
334 let doff = cur_frm.get_offset(0);
335 let dst = cur_frm.get_data_mut().unwrap();
336 let mut skip_run = 0;
337 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)) {
338 for x in (0..w).step_by(4) {
339 let mut buf = [0; 16];
340 let mut refbuf = [0; 16];
341 Self::get_block(&sstrip[x..], sstride, &mut buf);
342 Self::get_block(&rstrip[x..], rstride, &mut refbuf);
343
344 let mut skip_dist = 0;
345 for (pix, rpix) in buf.iter().zip(refbuf.iter()) {
346 skip_dist += pix.dist(*rpix);
347 }
348 if skip_dist == 0 {
349 skip_run += 1;
350 is_intra = false;
351 if skip_run == 1023 {
352 Self::write_skips(bw, skip_run)?;
353 skip_run = 0;
354 }
355 continue;
356 }
357
358 let mut bstate = BlockState::default();
359 bstate.calc_stats(&buf);
360
361 let dst = &mut dstrip[x..];
362 if skip_dist <= bstate.fill_dist {
363 skip_run += 1;
364 is_intra = false;
365 if skip_run == 1023 {
366 Self::write_skips(bw, skip_run)?;
367 skip_run = 0;
368 }
369 } else if bstate.fill_dist <= bstate.clr2_dist {
370 bstate.put_fill(dst, dstride);
371 if skip_run != 0 {
372 Self::write_skips(bw, skip_run)?;
373 skip_run = 0;
374 }
375 bstate.write_fill(bw)?;
376 } else if bstate.clr8_dist < bstate.clr2_dist {
377 bstate.put_clr8(dst, dstride);
378 if skip_run != 0 {
379 Self::write_skips(bw, skip_run)?;
380 skip_run = 0;
381 }
382 bstate.write_clr8(bw)?;
383 } else {
384 bstate.put_clr2(dst, dstride);
385 if skip_run != 0 {
386 Self::write_skips(bw, skip_run)?;
387 skip_run = 0;
388 }
389 bstate.write_clr2(bw)?;
390 }
391 }
392 }
393 if skip_run != 0 {
394 Self::write_skips(bw, skip_run)?;
395 }
396 if is_intra {
397 bw.write_u16le(0)?;
398 } //xxx: something for inter?
399 Ok(is_intra)
400 }
401 fn encode_intra(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, _quality: u8) -> EncoderResult<bool> {
402 let src = in_frm.get_data();
403 let sstride = in_frm.get_stride(0);
404 let soff = in_frm.get_offset(0);
405 let (w, h) = in_frm.get_dimensions(0);
406 let dstride = cur_frm.get_stride(0);
407 let doff = cur_frm.get_offset(0);
408 let dst = cur_frm.get_data_mut().unwrap();
409 for (sstrip, dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
410 for x in (0..w).step_by(4) {
411 let mut buf = [0; 16];
412 Self::get_block(&sstrip[x..], sstride, &mut buf);
413 let mut bstate = BlockState::default();
414 bstate.calc_stats(&buf);
415
416 let dst = &mut dstrip[x..];
417 if bstate.fill_dist <= bstate.clr2_dist {
418 bstate.put_fill(dst, dstride);
419 bstate.write_fill(bw)?;
420 } else if bstate.clr8_dist < bstate.clr2_dist {
421 bstate.put_clr8(dst, dstride);
422 bstate.write_clr8(bw)?;
423 } else {
424 bstate.put_clr2(dst, dstride);
425 bstate.write_clr2(bw)?;
426 }
427 }
428 }
429 bw.write_u16le(0)?;
430 Ok(true)
431 }
432 }
433
434 const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton {
435 model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
436 comp_info: [
437 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
438 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }),
439 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }),
440 None, None],
441 elem_size: 2, be: false, alpha: false, palette: false };
442
443 impl NAEncoder for MSVideo1Encoder {
444 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
445 match encinfo.format {
446 NACodecTypeInfo::None => {
447 Ok(EncodeParameters {
448 format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, RGB555_FORMAT)),
449 ..Default::default() })
450 },
451 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
452 NACodecTypeInfo::Video(vinfo) => {
453 let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, RGB555_FORMAT);
454 let mut ofmt = *encinfo;
455 ofmt.format = NACodecTypeInfo::Video(outinfo);
456 Ok(ofmt)
457 }
458 }
459 }
460 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
461 match encinfo.format {
462 NACodecTypeInfo::None => Err(EncoderError::FormatError),
463 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
464 NACodecTypeInfo::Video(vinfo) => {
465 if vinfo.format != RGB555_FORMAT {
466 return Err(EncoderError::FormatError);
467 }
468 if ((vinfo.width | vinfo.height) & 3) != 0 {
469 return Err(EncoderError::FormatError);
470 }
471
472 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT);
473 let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info), None);
474 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
475 stream.set_num(stream_id as usize);
476 let stream = stream.into_ref();
477 if self.pool.prealloc_video(out_info, 2).is_err() {
478 return Err(EncoderError::AllocError);
479 }
480
481 self.stream = Some(stream.clone());
482 self.quality = encinfo.quality;
483
484 Ok(stream)
485 },
486 }
487 }
488 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
489 let buf = frm.get_buffer();
490 if let Some(ref vbuf) = buf.get_vbuf16() {
491 let mut cur_frm = self.pool.get_free().unwrap();
492 let mut dbuf = Vec::with_capacity(4);
493 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
494 let mut bw = ByteWriter::new(&mut gw);
495 if self.frmcount == 0 {
496 self.lastfrm = None;
497 }
498 let is_intra = if let Some(ref prev_buf) = self.lastfrm {
499 Self::encode_inter(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)?
500 } else {
501 Self::encode_intra(&mut bw, &mut cur_frm, vbuf, self.quality)?
502 };
503 self.lastfrm = Some(cur_frm);
504 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
505 self.frmcount += 1;
506 if self.frmcount == self.key_int {
507 self.frmcount = 0;
508 }
509 Ok(())
510 } else {
511 Err(EncoderError::InvalidParameters)
512 }
513 }
514 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
515 let mut npkt = None;
516 std::mem::swap(&mut self.pkt, &mut npkt);
517 Ok(npkt)
518 }
519 fn flush(&mut self) -> EncoderResult<()> {
520 self.frmcount = 0;
521 Ok(())
522 }
523 }
524
525 const ENCODER_OPTS: &[NAOptionDefinition] = &[
526 NAOptionDefinition {
527 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
528 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
529 ];
530
531 impl NAOptionHandler for MSVideo1Encoder {
532 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
533 fn set_options(&mut self, options: &[NAOption]) {
534 for option in options.iter() {
535 for opt_def in ENCODER_OPTS.iter() {
536 if opt_def.check(option).is_ok() {
537 match option.name {
538 KEYFRAME_OPTION => {
539 if let NAValue::Int(intval) = option.value {
540 self.key_int = intval as u8;
541 }
542 },
543 _ => {},
544 };
545 }
546 }
547 }
548 }
549 fn query_option_value(&self, name: &str) -> Option<NAValue> {
550 match name {
551 KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
552 _ => None,
553 }
554 }
555 }
556
557 pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
558 Box::new(MSVideo1Encoder::new())
559 }
560
561 #[cfg(test)]
562 mod test {
563 use nihav_core::codecs::*;
564 use nihav_core::demuxers::*;
565 use nihav_core::muxers::*;
566 use crate::*;
567 use nihav_commonfmt::*;
568 use nihav_codec_support::test::enc_video::*;
569 use super::RGB555_FORMAT;
570
571 #[test]
572 fn test_ms_video1_encoder() {
573 let mut dmx_reg = RegisteredDemuxers::new();
574 generic_register_all_demuxers(&mut dmx_reg);
575 let mut dec_reg = RegisteredDecoders::new();
576 generic_register_all_decoders(&mut dec_reg);
577 ms_register_all_decoders(&mut dec_reg);
578 let mut mux_reg = RegisteredMuxers::new();
579 generic_register_all_muxers(&mut mux_reg);
580 let mut enc_reg = RegisteredEncoders::new();
581 ms_register_all_encoders(&mut enc_reg);
582
583 // sample: https://samples.mplayerhq.hu/V-codecs/UCOD/TalkingHead_352x288.avi
584 let dec_config = DecoderTestParams {
585 demuxer: "avi",
586 in_name: "assets/Misc/TalkingHead_352x288.avi",
587 stream_type: StreamType::Video,
588 limit: Some(3),
589 dmx_reg, dec_reg,
590 };
591 let enc_config = EncoderTestParams {
592 muxer: "avi",
593 enc_name: "msvideo1",
594 out_name: "msvideo1.avi",
595 mux_reg, enc_reg,
596 };
597 let dst_vinfo = NAVideoInfo {
598 width: 0,
599 height: 0,
600 format: RGB555_FORMAT,
601 flipped: true,
602 bits: 16,
603 };
604 let enc_params = EncodeParameters {
605 format: NACodecTypeInfo::Video(dst_vinfo),
606 quality: 0,
607 bitrate: 0,
608 tb_num: 0,
609 tb_den: 0,
610 flags: 0,
611 };
612 //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
613 test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
614 &[0x6921e67e, 0x4f2ada95, 0x009ffc62, 0xd4bfab6a]);
615 }
616 }