]> git.nihav.org Git - nihav.git/blame_incremental - nihav-qt/src/codecs/svq1.rs
avimux: do not record palette change chunks in OpenDML index
[nihav.git] / nihav-qt / src / codecs / svq1.rs
... / ...
CommitLineData
1use nihav_core::codecs::*;
2use nihav_core::io::bitreader::*;
3use nihav_core::io::codebook::*;
4use nihav_core::io::intcode::*;
5use nihav_codec_support::codecs::{MV, ZERO_MV};
6use nihav_codec_support::codecs::blockdsp::*;
7
8use super::svq1data::*;
9
10#[derive(Clone,Copy,Debug,PartialEq)]
11enum SVQ1FrameType {
12 I,
13 P,
14 Drop,
15}
16
17#[derive(Clone,Copy,Debug,PartialEq)]
18enum BlockType {
19 Intra,
20 Skip,
21 OneMV,
22 FourMV,
23}
24
25#[derive(Clone,Copy,Debug,PartialEq)]
26#[allow(clippy::enum_variant_names)]
27enum BlockDiv {
28 Div16x16,
29 Div16x8,
30 Div8x8,
31 Div8x4,
32 Div4x4,
33 Div4x2,
34}
35
36impl BlockDiv {
37 fn is_final(self) -> bool { self == BlockDiv::Div4x2 }
38 fn split(self) -> (Self, usize, usize) {
39 match self {
40 BlockDiv::Div16x16 => (BlockDiv::Div16x8, 0, 8),
41 BlockDiv::Div16x8 => (BlockDiv::Div8x8, 8, 0),
42 BlockDiv::Div8x8 => (BlockDiv::Div8x4, 0, 4),
43 BlockDiv::Div8x4 => (BlockDiv::Div4x4, 4, 0),
44 BlockDiv::Div4x4 => (BlockDiv::Div4x2, 0, 2),
45 BlockDiv::Div4x2 => unreachable!(),
46 }
47 }
48 fn get_size(self) -> (usize, usize) {
49 match self {
50 BlockDiv::Div16x16 => (16, 16),
51 BlockDiv::Div16x8 => (16, 8),
52 BlockDiv::Div8x8 => ( 8, 8),
53 BlockDiv::Div8x4 => ( 8, 4),
54 BlockDiv::Div4x4 => ( 4, 4),
55 BlockDiv::Div4x2 => ( 4, 2),
56 }
57 }
58 fn get_level(self) -> usize {
59 match self {
60 BlockDiv::Div16x16 => 5,
61 BlockDiv::Div16x8 => 4,
62 BlockDiv::Div8x8 => 3,
63 BlockDiv::Div8x4 => 2,
64 BlockDiv::Div4x4 => 1,
65 BlockDiv::Div4x2 => 0,
66 }
67 }
68}
69
70const BLOCK_TYPES: [BlockType; 4] = [ BlockType::Skip, BlockType::OneMV, BlockType::FourMV, BlockType::Intra ];
71
72impl SVQ1FrameType {
73 fn is_ref(self) -> bool {
74 self != SVQ1FrameType::Drop
75 }
76 fn is_intra(self) -> bool {
77 self == SVQ1FrameType::I
78 }
79 fn to_frame_type(self) -> FrameType {
80 match self {
81 SVQ1FrameType::I => FrameType::I,
82 SVQ1FrameType::P => FrameType::P,
83 SVQ1FrameType::Drop => FrameType::P,
84 }
85 }
86 fn from_id(id: u32) -> DecoderResult<Self> {
87 match id {
88 0 => Ok(SVQ1FrameType::I),
89 1 => Ok(SVQ1FrameType::P),
90 2 => Ok(SVQ1FrameType::Drop),
91 _ => Err(DecoderError::InvalidData),
92 }
93 }
94}
95
96struct SVQ1DescReader {
97 table: &'static [[u8; 2]],
98 bias: i16,
99}
100
101impl CodebookDescReader<i16> for SVQ1DescReader {
102 fn bits(&mut self, idx: usize) -> u8 { self.table[idx][1] }
103 fn code(&mut self, idx: usize) -> u32 { u32::from(self.table[idx][0]) }
104 fn sym(&mut self, idx: usize) -> i16 { (idx as i16) + self.bias }
105 fn len(&mut self) -> usize { self.table.len() }
106}
107
108struct SVQ1InterMeanDescReader {}
109
110impl CodebookDescReader<i16> for SVQ1InterMeanDescReader {
111 fn bits(&mut self, idx: usize) -> u8 { SVQ_INTER_MEAN_CODES[idx][1] as u8 }
112 fn code(&mut self, idx: usize) -> u32 { u32::from(SVQ_INTER_MEAN_CODES[idx][0]) }
113 fn sym(&mut self, idx: usize) -> i16 { (idx as i16) - 256 }
114 fn len(&mut self) -> usize { SVQ_INTER_MEAN_CODES.len() }
115}
116
117struct SVQ1Decoder {
118 info: NACodecInfoRef,
119 width: usize,
120 height: usize,
121 ref_frm: Option<NAVideoBufferRef<u8>>,
122 mvs: Vec<MV>,
123 intra_stages_cb: Vec<Codebook<i16>>,
124 inter_stages_cb: Vec<Codebook<i16>>,
125 intra_mean_cb: Codebook<i16>,
126 inter_mean_cb: Codebook<i16>,
127 mv_cb: Codebook<i16>,
128
129 div_list: [(BlockDiv, usize); 64],
130}
131
132impl SVQ1Decoder {
133 fn new() -> Self {
134 let mut intra_stages_cb = Vec::with_capacity(6);
135 for table in SVQ_INTRA_STAGE_CODES.iter() {
136 let mut cbd = SVQ1DescReader { table, bias: -1 };
137 let cb = Codebook::new(&mut cbd, CodebookMode::MSB).unwrap();
138 intra_stages_cb.push(cb);
139 }
140 let mut inter_stages_cb = Vec::with_capacity(6);
141 for table in SVQ_INTER_STAGE_CODES.iter() {
142 let mut cbd = SVQ1DescReader { table, bias: -1 };
143 let cb = Codebook::new(&mut cbd, CodebookMode::MSB).unwrap();
144 inter_stages_cb.push(cb);
145 }
146 let mut cbd = SVQ1DescReader { table: &SVQ_INTRA_MEAN_CODES, bias: 0 };
147 let intra_mean_cb = Codebook::new(&mut cbd, CodebookMode::MSB).unwrap();
148 let mut cbd = SVQ1InterMeanDescReader {};
149 let inter_mean_cb = Codebook::new(&mut cbd, CodebookMode::MSB).unwrap();
150 let mut cbd = SVQ1DescReader { table: &SVQ_MV_CODES, bias: 0 };
151 let mv_cb = Codebook::new(&mut cbd, CodebookMode::MSB).unwrap();
152 Self {
153 info: NACodecInfoRef::default(),
154 width: 0,
155 height: 0,
156 ref_frm: None,
157 mvs: Vec::new(),
158 intra_stages_cb, inter_stages_cb, intra_mean_cb, inter_mean_cb, mv_cb,
159 div_list: [(BlockDiv::Div16x16, 0); 64],
160 }
161 }
162 fn read_mv(&self, br: &mut BitReader) -> DecoderResult<MV> {
163 let mut x = br.read_cb(&self.mv_cb)?;
164 if x > 0 && br.read_bool()? {
165 x = -x;
166 }
167 let mut y = br.read_cb(&self.mv_cb)?;
168 if y > 0 && br.read_bool()? {
169 y = -y;
170 }
171 Ok(MV { x, y })
172 }
173 #[allow(clippy::too_many_arguments)]
174 fn pred_mv(&self, x: usize, y: usize, w: usize, mv_idx: usize, mvstride: usize, blk_idx: usize, diff: MV) -> MV {
175 let a_mv = if x > 0 || (blk_idx & 1) != 0 { self.mvs[mv_idx - 1] } else { ZERO_MV };
176 let b_mv = if y > 0 || (blk_idx & 2) != 0 { self.mvs[mv_idx - mvstride] } else { a_mv };
177 let c_mv = match blk_idx {
178 0 => if y > 0 && x + 16 < w { self.mvs[mv_idx + 2 - mvstride] } else { ZERO_MV },
179 1 => if y > 0 && x + 16 < w { self.mvs[mv_idx + 1 - mvstride] } else { ZERO_MV },
180 2 => self.mvs[mv_idx + 1 - mvstride],
181 _ => self.mvs[mv_idx - 1 - mvstride],
182 };
183 let mut new_mv = diff + MV::pred(a_mv, b_mv, c_mv);
184 if new_mv.x >= 32 { new_mv.x -= 64; }
185 else if new_mv.x <= -32 { new_mv.x += 64; }
186 if new_mv.y >= 32 { new_mv.y -= 64; }
187 else if new_mv.y <= -32 { new_mv.y += 64; }
188 new_mv
189 }
190 fn decode_intra_block(&mut self, br: &mut BitReader, dst: &mut [u8], dstride: usize) -> DecoderResult<()> {
191 self.div_list[0] = (BlockDiv::Div16x16, 0);
192 let mut idx = 0;
193 let mut end = 1;
194 while idx < end {
195 let (div, off) = self.div_list[idx];
196 if !div.is_final() && br.read_bool()? {
197 let (ndiv, xoff, yoff) = div.split();
198 self.div_list[end] = (ndiv, off);
199 end += 1;
200 self.div_list[end] = (ndiv, off + xoff + yoff * dstride);
201 end += 1;
202 } else {
203 let level = div.get_level();
204 let stages = br.read_cb(&self.intra_stages_cb[level])?;
205 if level > 3 {
206 validate!(stages <= 0);
207 }
208 let (w, h) = div.get_size();
209 let fill = if stages < 0 { 0 } else { br.read_cb(&self.intra_mean_cb)? };
210 if stages > 0 {
211 let mut blk = [fill; 256];
212 for stage in 0..(stages as usize) {
213 let idx = br.read(4)? as usize;
214 let src: &[i8] = match div {
215 BlockDiv::Div8x8 => &SVQ_INTRA_CB_8X8[stage * 16 + idx],
216 BlockDiv::Div8x4 => &SVQ_INTRA_CB_8X4[stage * 16 + idx],
217 BlockDiv::Div4x4 => &SVQ_INTRA_CB_4X4[stage * 16 + idx],
218 BlockDiv::Div4x2 => &SVQ_INTRA_CB_4X2[stage * 16 + idx],
219 _ => unreachable!(),
220 };
221 for (dst, src) in blk.chunks_exact_mut(w).zip(src.chunks_exact(w)) {
222 for (diff, &src) in dst.iter_mut().zip(src.iter()) {
223 *diff += i16::from(src);
224 }
225 }
226 }
227 for (dline, sline) in dst[off..].chunks_mut(dstride).zip(blk.chunks_exact(w)).take(h) {
228 for (dst, &src) in dline.iter_mut().zip(sline.iter()) {
229 *dst = src.max(0).min(255) as u8;
230 }
231 }
232 } else {
233 for line in dst[off..].chunks_mut(dstride).take(h) {
234 for el in line.iter_mut().take(w) {
235 *el = fill as u8;
236 }
237 }
238 }
239 }
240 idx += 1;
241 }
242 Ok(())
243 }
244 fn decode_inter_block(&mut self, br: &mut BitReader, dst: &mut [u8], dstride: usize) -> DecoderResult<()> {
245 self.div_list[0] = (BlockDiv::Div16x16, 0);
246 let mut idx = 0;
247 let mut end = 1;
248 while idx < end {
249 let (div, off) = self.div_list[idx];
250 if !div.is_final() && br.read_bool()? {
251 let (ndiv, xoff, yoff) = div.split();
252 self.div_list[end] = (ndiv, off);
253 end += 1;
254 self.div_list[end] = (ndiv, off + xoff + yoff * dstride);
255 end += 1;
256 } else {
257 let level = div.get_level();
258 let stages = br.read_cb(&self.inter_stages_cb[level])?;
259 if level > 3 {
260 validate!(stages <= 0);
261 }
262 let (w, h) = div.get_size();
263 let fill = if stages < 0 { 0 } else { br.read_cb(&self.inter_mean_cb)? };
264 if fill != 0 || stages != 0 {
265 let mut blk = [0; 256];
266
267 for (dline, sline) in blk.chunks_exact_mut(w).zip(dst[off..].chunks(dstride)).take(h) {
268 for (dst, &src) in dline.iter_mut().zip(sline.iter()) {
269 *dst = i16::from(src);
270 }
271 }
272
273 if fill != 0 {
274 for el in blk.iter_mut().take(w * h) {
275 *el += fill;
276 }
277 }
278 if stages > 0 {
279 for stage in 0..(stages as usize) {
280 let idx = br.read(4)? as usize;
281 let src: &[i8] = match div {
282 BlockDiv::Div8x8 => &SVQ_INTER_CB_8X8[stage * 16 + idx],
283 BlockDiv::Div8x4 => &SVQ_INTER_CB_8X4[stage * 16 + idx],
284 BlockDiv::Div4x4 => &SVQ_INTER_CB_4X4[stage * 16 + idx],
285 BlockDiv::Div4x2 => &SVQ_INTER_CB_4X2[stage * 16 + idx],
286 _ => unreachable!(),
287 };
288 for (line, src) in blk.chunks_exact_mut(w).zip(src.chunks_exact(w)) {
289 for (dst, &src) in line.iter_mut().zip(src.iter()) {
290 *dst += i16::from(src);
291 }
292 }
293 }
294 }
295
296 for (dline, sline) in dst[off..].chunks_mut(dstride).zip(blk.chunks_exact(w)).take(h) {
297 for (dst, &src) in dline.iter_mut().zip(sline.iter()) {
298 *dst = src.max(0).min(255) as u8;
299 }
300 }
301 }
302 }
303 idx += 1;
304 }
305 Ok(())
306 }
307 fn decode_plane(&mut self, br: &mut BitReader, dframe: &mut NASimpleVideoFrame<u8>, plane: usize, is_intra: bool) -> DecoderResult<()> {
308 let (w, h) = if plane == 0 {
309 ((self.width + 15) & !15, (self.height + 15) & !15)
310 } else {
311 ((self.width / 4 + 15) & !15, (self.height / 4 + 15) & !15)
312 };
313 let mvstride = w / 8;
314 self.mvs.clear();
315 self.mvs.resize(mvstride * (h / 8), ZERO_MV);
316 let mut mv_idx = 0;
317
318 let mut doff = dframe.offset[plane];
319 let dstride = dframe.stride[plane];
320 for y in (0..h).step_by(16) {
321 for x in (0..w).step_by(16) {
322 let block_type = if is_intra {
323 BlockType::Intra
324 } else {
325 let idx = br.read_code(UintCodeType::LimitedZeroes(3))? as usize;
326 BLOCK_TYPES[idx]
327 };
328 match block_type {
329 BlockType::Intra => {
330 self.decode_intra_block(br, &mut dframe.data[doff + x..], dstride)?;
331 },
332 BlockType::Skip => {
333 if let Some(ref rfrm) = self.ref_frm {
334 let sstride = rfrm.get_stride(plane);
335 let soff = rfrm.get_offset(plane) + y * sstride;
336 let src = &rfrm.get_data()[soff + x..];
337 let dst = &mut dframe.data[doff + x..];
338 for (dline, sline) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(16) {
339 dline[..16].copy_from_slice(&sline[..16]);
340 }
341 }
342 },
343 BlockType::OneMV => {
344 let mv = self.read_mv(br)?;
345 let new_mv = self.pred_mv(x, y, w, mv_idx + x / 8, mvstride, 0, mv);
346 self.mvs[mv_idx + x / 8] = new_mv;
347 self.mvs[mv_idx + x / 8 + 1] = new_mv;
348 self.mvs[mv_idx + mvstride + x / 8] = new_mv;
349 self.mvs[mv_idx + mvstride + x / 8 + 1] = new_mv;
350 if let Some(ref rfrm) = self.ref_frm {
351 let mode = ((new_mv.x & 1) + (new_mv.y & 1) * 2) as usize;
352 copy_block(dframe, rfrm.clone(), plane, x, y, new_mv.x >> 1, new_mv.y >> 1, 16, 16, 0, 1, mode, HALFPEL_INTERP_FUNCS);
353 }
354 self.decode_inter_block(br, &mut dframe.data[doff + x..], dstride)?;
355 },
356 BlockType::FourMV => {
357 for i in 0..4 {
358 let mv = self.read_mv(br)?;
359 let cur_idx = mv_idx + x / 8 + (i & 1) + (i >> 1) * mvstride;
360 let new_mv = self.pred_mv(x, y, w, cur_idx, mvstride, i, mv);
361 self.mvs[cur_idx] = new_mv;
362 if let Some(ref rfrm) = self.ref_frm {
363 let mode = ((new_mv.x & 1) + (new_mv.y & 1) * 2) as usize;
364 copy_block(dframe, rfrm.clone(), plane, x + (i & 1) * 8, y + (i >> 1) * 8, new_mv.x >> 1, new_mv.y >> 1, 8, 8, 0, 1, mode, HALFPEL_INTERP_FUNCS);
365 }
366 }
367 self.decode_inter_block(br, &mut dframe.data[doff + x..], dstride)?;
368 },
369 };
370 }
371 doff += dstride * 16;
372 mv_idx += mvstride * 2;
373 }
374 Ok(())
375 }
376}
377
378impl NADecoder for SVQ1Decoder {
379 fn init(&mut self, supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
380 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
381 self.width = vinfo.get_width();
382 self.height = vinfo.get_height();
383 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, false, YUV410_FORMAT));
384 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
385 supp.pool_u8.set_dec_bufs(2);
386 supp.pool_u8.prealloc_video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, YUV410_FORMAT), 6)?;
387 self.mvs = Vec::with_capacity((self.width + 15) / 4 * ((self.height + 15) / 4));
388
389 Ok(())
390 } else {
391 Err(DecoderError::InvalidData)
392 }
393 }
394 #[allow(clippy::collapsible_else_if)]
395 fn decode(&mut self, supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
396 let src = pkt.get_buffer();
397 validate!(src.len() >= 2);
398 let mut br = BitReader::new(&src, BitReaderMode::BE);
399
400 let fcode = br.read(22)?;
401 validate!((fcode & 0x60) != 0);
402 let _pts = br.read(8)?;
403 let ptype = br.read(2)?;
404 let ftype = SVQ1FrameType::from_id(ptype)?;
405 let mut frm_data = Vec::new();
406 if fcode != 0x20 {
407 frm_data.extend_from_slice(&src);
408 for i in 0..4 {
409 let a = frm_data[i * 4 + 4];
410 let b = frm_data[i * 4 + 5];
411 let c = frm_data[i * 4 + 6];
412 let d = frm_data[i * 4 + 7];
413 frm_data[i * 4 + 4] = c ^ frm_data[32 - i * 4];
414 frm_data[i * 4 + 5] = d ^ frm_data[33 - i * 4];
415 frm_data[i * 4 + 6] = a ^ frm_data[34 - i * 4];
416 frm_data[i * 4 + 7] = b ^ frm_data[35 - i * 4];
417 }
418 br = BitReader::new(&frm_data, BitReaderMode::BE);
419 br.skip(32)?;
420 }
421 if ftype.is_intra() {
422 if fcode == 0x50 || fcode == 0x60 {
423 let _checksum = br.read(16)? as u16;
424// let crc = calc_crc(frm_data.as_slice(), 0);
425// validate!(crc == _checksum);
426 }
427 if fcode == 0x40 || fcode == 0x60 || fcode == 0x70 {
428 let str_len = br.read(8)? as usize;
429 for _ in 0..str_len {
430 br.skip(8)?;
431 }
432 }
433 br.skip(2)?;
434 br.skip(2)?;
435 br.skip(1)?;
436 let size_id = br.read(3)? as usize;
437 let (w, h) = if size_id < FRAME_SIZES.len() {
438 FRAME_SIZES[size_id]
439 } else {
440 let w = br.read(12)? as usize;
441 let h = br.read(12)? as usize;
442 validate!(w >= 16 && h >= 16);
443 (w, h)
444 };
445 if self.width != w || self.height != h {
446 self.flush();
447 self.width = w;
448 self.height = h;
449 let vinfo = NAVideoInfo::new(self.width, self.height, false, YUV410_FORMAT);
450 supp.pool_u8.reset();
451 supp.pool_u8.prealloc_video(vinfo, 6)?;
452 let nmb = ((w + 15) / 4) * ((h + 15) / 4);
453 if self.mvs.capacity() < nmb {
454 let add = nmb - self.mvs.capacity();
455 self.mvs.reserve(add);
456 }
457 }
458 } else {
459 if self.ref_frm.is_none() {
460 return Err(DecoderError::MissingReference);
461 }
462 }
463 if br.read_bool()? {
464 let _pkt_crc = br.read_bool()?;
465 let _comp_crc = br.read_bool()?;
466 let marker = br.read(2)?;
467 validate!(marker == 0);
468 }
469 if br.read_bool()? {
470 br.skip(1)?;
471 br.skip(4)?;
472 br.skip(1)?;
473 br.skip(2)?;
474 while br.read_bool()? { }
475 }
476
477 let ret = supp.pool_u8.get_free();
478 if ret.is_none() {
479 return Err(DecoderError::AllocError);
480 }
481
482 let mut buf = ret.unwrap();
483 let mut dframe = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap();
484 self.decode_plane(&mut br, &mut dframe, 0, ftype.is_intra())?;
485 self.decode_plane(&mut br, &mut dframe, 1, ftype.is_intra())?;
486 self.decode_plane(&mut br, &mut dframe, 2, ftype.is_intra())?;
487
488 if ftype.is_ref() {
489 self.ref_frm = Some(buf.clone());
490 }
491
492 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf));
493 frm.set_keyframe(ftype.is_intra());
494 frm.set_frame_type(ftype.to_frame_type());
495 Ok(frm.into_ref())
496 }
497 fn flush(&mut self) {
498 self.ref_frm = None;
499 }
500}
501
502impl NAOptionHandler for SVQ1Decoder {
503 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
504 fn set_options(&mut self, _options: &[NAOption]) { }
505 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
506}
507
508pub fn get_decoder() -> Box<dyn NADecoder + Send> {
509 Box::new(SVQ1Decoder::new())
510}
511
512
513#[cfg(test)]
514mod test {
515 use nihav_core::codecs::RegisteredDecoders;
516 use nihav_core::demuxers::RegisteredDemuxers;
517 use nihav_codec_support::test::dec_video::*;
518 use crate::qt_register_all_decoders;
519 use nihav_commonfmt::generic_register_all_demuxers;
520 #[test]
521 fn test_svq1() {
522 let mut dmx_reg = RegisteredDemuxers::new();
523 generic_register_all_demuxers(&mut dmx_reg);
524 let mut dec_reg = RegisteredDecoders::new();
525 qt_register_all_decoders(&mut dec_reg);
526
527 // sample: https://samples.mplayerhq.hu/A-codecs/ima-adpcm/adpcm-bug.mov
528 test_decoding("mov", "sorenson-video", "assets/QT/adpcm-bug.mov", Some(6), &dmx_reg, &dec_reg,
529 ExpectedTestResult::MD5Frames(vec![
530 [0x90c5eb74, 0xcb942d7d, 0x84c5e444, 0x7f1ba2c2],
531 [0x650ae6f7, 0x9a0a6ec2, 0x0d907064, 0xb4c37321],
532 [0xa04e865b, 0xdbd65920, 0x4703d7dd, 0x962707a1],
533 [0xe89c98bc, 0x356791bb, 0xfb6f7302, 0x2250ef05],
534 [0x282ef2a7, 0x235541b4, 0x55055d99, 0x1a8d0b29],
535 [0x79c60bab, 0xe5a11a50, 0x5f9e800b, 0x12bce70d],
536 [0xe9a08fb7, 0x3b482a8b, 0x50e1560e, 0xd6d70287]]));
537 }
538}
539
540const FRAME_SIZES: [(usize, usize); 7] = [
541 (160, 120), (128, 96), (176, 144), (352, 288),
542 (704, 576), (240, 180), (320, 240)
543];
544
545/*const CRC_TABLE: [u16; 256] = [ //CCITT 16-bit CRC?
546 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
547 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
548 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
549 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
550 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
551 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
552 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
553 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
554 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
555 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
556 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
557 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
558 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
559 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
560 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
561 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
562 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
563 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
564 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
565 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
566 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
567 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
568 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
569 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
570 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
571 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
572 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
573 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
574 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
575 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
576 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
577 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
578];
579
580fn calc_crc(src: &[u8], start: u16) -> u16 {
581 let mut crc = start;
582 for byte in src.iter() {
583 crc = CRC_TABLE[(*byte ^ ((crc >> 8) as u8)) as usize] ^ (crc << 8);
584 }
585 crc
586}*/