noVNC/tests/test.ast2100.js

385 lines
16 KiB
JavaScript

// requires local modules: ast2100, ast2100idct, ast2100const, ast2100util
/* jshint expr: true */
var assert = chai.assert;
var expect = chai.expect;
// Convert a hex string, optionally containing spaces, into an array of integers representing bytes.
var parseHex = function (s) {
s = s.replace(/\s/g, '');
if (s.length % 2 != 0)
throw 'Hex data has uneven length!';
for (var bytes = [], i = 0; i < s.length; i += 2)
bytes.push(parseInt(s.substr(i, 2), 16));
return bytes;
};
function make_decoder () {
return new Ast2100Decoder({
width: 0x400,
height: 0x300,
blitCallback: function () {}
});
}
// Swap u32 byte order. Mutates input!
function buf_swap32 (data) {
for (var i = 0; i < data.length; i += 4) {
var tmp;
tmp = data[i+0];
data[i+0] = data[i+3];
data[i+3] = tmp;
tmp = data[i+1];
data[i+1] = data[i+2];
data[i+2] = tmp;
}
return data;
}
describe('ATEN_AST2100 video encoding', function() {
"use strict";
var dec;
beforeEach(function () {
dec = make_decoder();
dec._mcuLimit = 10;
});
describe('BitStream', function() {
var stream, stream2, stream3;
var data = buf_swap32(parseHex('f0f0 cccc cccc cccc cccc cccc cccc cccc'));
var data2 = parseHex('F0F1F2F3 F4F5F6F7 F8F9FAFB FCFDFEFF');
var data3 = parseHex('0b0b01bc45fbc5e020040101ffff3f20c0ffffff0000240000000000000000005028140a0000000000000000');
beforeEach(function () {
stream = new BitStream({data: data});
stream2 = new BitStream({data: data2});
stream3 = new BitStream({data: data3});
});
it('should pass simple sanity checks', function () {
expect(stream.read(4)).to.equal(0xF);
expect(stream.read(4)).to.equal(0x0);
expect(stream.read(4)).to.equal(0xF);
expect(stream.read(4)).to.equal(0x0);
});
it('should pass simple sanity checks (part II)', function () {
var i;
for (i = 0; i < 4; ++i)
expect(stream.read(1)).to.equal(1);
for (i = 0; i < 4; ++i)
expect(stream.read(1)).to.equal(0);
});
it('should be able to properly refill the buffer', function () {
expect(stream.read(4)).to.equal(0xF);
expect(stream.read(4)).to.equal(0x0);
expect(stream.read(4)).to.equal(0xF);
expect(stream.read(8)).to.equal(0x0C);
});
it('should properly swap byte order', function () {
expect(stream2.read(8)).to.equal(0xF3);
expect(stream2.read(8)).to.equal(0xF2);
expect(stream2.read(8)).to.equal(0xF1);
expect(stream2.read(8)).to.equal(0xF0);
expect(stream2.read(8)).to.equal(0xF7);
});
it('should properly handle skipping the full 32-bit read-buffer (data3)', function () {
// Must do this in two parts so that bits < 32 each time.
stream3.skip(16);
stream3.skip(16);
expect(stream3.read(4)).to.equal(0xE);
});
// it('issue repro', function () {
// var data = parseHex('f0f0 cccc cccc cccc cccc cccc cccc cccc');
// var stream = new BitStream({data: data});
// expect(stream.read(31)).to.equal(0xF0F0CCCC >>> 1);
// expect(stream.read(8)).to.equal(0x66);
// });
});
describe('quant tables', function() {
it('quant tables are properly loaded and scaled', function () {
// This is luma quant table #4.
var expected = Int32Array.from([
0x00090000, 0x0008527e, 0x00068866, 0x000a9537, 0x000d0000, 0x00114908, 0x000f274b, 0x0009616d,
0x0008527e, 0x000b8b14, 0x000caf8f, 0x00104f53, 0x00136b26, 0x0022df8f, 0x0018c594, 0x000b7b02,
0x0009255c, 0x000caf8f, 0x000f5d2c, 0x0013f8fd, 0x001cbe90, 0x0020d994, 0x001adebc, 0x000b2cc4,
0x00083b2b, 0x000eadca, 0x00126faf, 0x00161f78, 0x0020ecad, 0x002c58a1, 0x001ca316, 0x000b07c7,
0x000a0000, 0x0010a4fc, 0x001a219a, 0x002473bf, 0x00260000, 0x002fed69, 0x001ed922, 0x000bdd19,
0x000a36ca, 0x0014b4bd, 0x001ecbfa, 0x00214279, 0x00235b34, 0x0023cdea, 0x001ac9de, 0x000b0e2f,
0x000e9cbf, 0x001b0616, 0x001e67d4, 0x001e8bd4, 0x001ed922, 0x001cea24, 0x00139fb4, 0x00085c96,
0x000b0935, 0x00138450, 0x00131afd, 0x0011d7e1, 0x001161b4, 0x000c23a7, 0x000882d0, 0x00042fc6
]);
dec._loadQuantTable(0, ATEN_QT_LUMA[4]);
var luma_quant_table = dec.quantTables[0];
expect(luma_quant_table).to.deep.equal(expected);
});
});
describe('IDCT', function () {
var outputBuf;
beforeEach(function () {
outputBuf = new Uint8Array(DCTSIZE2); // equivalent to one of the componentBufs in Ast2100Decoder.
});
// TODO: Fix typing: variables should be typed arrays.
it('test case 0 - DC value only', function () {
// this is the output of the VLC / entropy coding process
// XXX: @KK: Why is this 16-bit?
var dataUnit = [ // new Int16Array(
0xFF9C,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
var expected_idct_output = Uint8Array.from([ // new Uint8Array(
0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,
0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,
0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF]);
dec._loadQuantTable(0, ATEN_QT_LUMA[4]);
var luma_quant_table = dec.quantTables[0];
AST2100IDCT.idct_fixed_aan(luma_quant_table, dataUnit, outputBuf);
// console.log(result);
console.log('expected:');
console.log(fmt_u8a(expected_idct_output));
console.log('result:');
console.log(fmt_u8a(outputBuf));
expect(outputBuf).to.deep.equal(expected_idct_output);
});
it('test case 1', function () {
// this is the output of the VLC / entropy coding process
// XXX: @KK: Why is this 16-bit?
var dataUnit = Int16Array.from([
0xFFBD,0,0,0,0,0,0,0,
0xFFC3,0,0,0,0,0,0,0,
0x26,0,0,0,0,0,0,0,
0xFFED,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
6,0,0,0,0,0,0,0,
0xFFFC,0,0,0,0,0,0,0,
2,0,0,0,0,0,0,0]);
var expected = Uint8Array.from([
0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,
0xE,0xE,0xE,0xE,0xE,0xE,0xE,0xE,
0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,
0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,
0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,
0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1]);
dec._loadQuantTable(0, ATEN_QT_LUMA[4]);
var luma_quant_table = dec.quantTables[0];
AST2100IDCT.idct_fixed_aan(luma_quant_table, dataUnit, outputBuf);
/*
console.log('expected:');
console.log(fmt_u8a(expected));
console.log('result:');
console.log(fmt_u8a(outputBuf));
*/
// XXX: TEMPORARY -- figure out this rounding issue
// expect(result).to.deep.equal(expected);
var maxErr = 0;
for (i = 0; i < 64; ++i)
maxErr = Math.max(maxErr, Math.abs(outputBuf[i] - expected[i]));
if (maxErr > 1)
throw 'Error too high!';
});
it('test case 2', function () {
// this is the output of the VLC / entropy coding process
// XXX: @KK: Why is this 16-bit?
var dataUnit = Int16Array.from([
0xFF9B, 0, 0, 0, 0, 0, 0, 0, 0xFFA4, 0, 0, 0, 0, 0, 0, 0, 0x35, 0, 0, 0, 0, 0, 0, 0,
0xFFE6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0xFFFA,
0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]);
var expected = [
0xE, 0xE, 0xE, 0xE, 0xE, 0xE, 0xE, 0xE, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1];
// Between IDCT passes, the workspace should look like this:
// [-903, 0, 0, 0, 0, 0, 0, 0, -874, 0, 0, 0, 0, 0, 0, 0, -914, 0, 0, 0, 0, 0, 0, 0, -874, 0, 0, 0, 0, 0, 0, 0,
// -915, 0, 0, 0, 0, 0, 0, 0, -868, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0, 0, 0, 0, 0, 0, 266, 0, 0, 0, 0, 0, 0, 0]
dec._loadQuantTable(0, ATEN_QT_LUMA[5]);
var luma_quant_table = dec.quantTables[0];
AST2100IDCT.idct_fixed_aan(luma_quant_table, dataUnit, outputBuf);
// console.log(result);
console.log('expected:');
console.log(fmt_u8a(expected));
console.log('result:');
console.log(fmt_u8a(outputBuf));
// XXX: TEMPORARY -- figure out this rounding issue
// expect(result).to.deep.equal(expected);
var maxErr = 0;
for (i = 0; i < 64; ++i)
maxErr = Math.max(maxErr, Math.abs(outputBuf[i] - expected[i]));
if (maxErr > 1)
throw 'Error too high!';
});
});
describe('VQ', function () {
it('should successfully load a codebook (colors)', function () {
var data = buf_swap32(parseHex('bc010b0b e0c5fb45 01010420 203fffff ffffffc0 00240000 00000000 00000000 0a142850'));
data = data.slice(4);
var stream = new BitStream({data: data}); // Strip off quant table selectors and subsampling mode.
var controlFlag = stream.read(4);
expect(controlFlag).to.equal(0xE);
var xMcuPos = stream.read(8), yMcuPos = stream.read(8);
expect(xMcuPos).to.equal(0xC);
expect(yMcuPos).to.equal(0x5F);
dec.subsamplingMode = 444; // required or an assertion in the VQ code will fail
dec._stream = stream;
dec._parseVqBlock(1); // codewordSize=1
expect(dec._vqCodewordLookup).to.deep.equal([1, 0, 2, 3]);
expect(dec._vqCodebook).to.deep.equal([
[0x10, 0x80, 0x80],
[0xA2, 0x80, 0x80],
[0x80, 0x80, 0x80],
[0xC0, 0x80, 0x80]
]);
});
it('should correctly handle multiple data blocks', function () {
// This data is from a memory dump. It should contain two VQ (0xE-type) blocks and then an 0x9 (end-frame).
var data = buf_swap32(parseHex('bc010b0b e0c5fb45 01010420 203fffff ffffffc0 00240000 00000000 00000000 0a142850' + '00000000 00000000'));
console.log('VQ multiple block data:');
console.log(fmt_u8a(data));
dec.decode(data);
// TODO: improve asserts/etc. beyond just 'finished without error'
});
});
describe('Full JPEG subsampled MCU example 0', function () {
// Corresponds to whole-mcu-example / main_mcu_test_2()
var data = parseHex('040701a61bff6280f81fbaa2ff4dbc408dfccf405c15ff1f004800f500000000000000002dd4a462237ced2c9c25dca4cef7e6667d6626f3308063c3c7a1b7335badc24aaf0b5e9daf69590fcb96206b59e6f2ccb2bdd386b17dbb72182080d6288a2a1f2f0608a0fe87ae287f132f1023ff33d057c5ff470012403d0000000000000000ec1fb06cdacd2bdf9d79f3cde7f9f1362b7ce914ba521c79524a5bdc212a5ed47c3cbc6c7e072ae3e3c18384785f6ca6d4e22fb1af0c96449d1847a9c7037e966fa3ac8d4990830339463d277f25fc6f30ffe5f4f5cfec9f4ffff8bf00a0f5d358a2ab6e4e6778d929f686bd58ca9c26b8f7338f15105065dd39c718e219a78e');
it('should decode properly', function () {
// DOES NOT APPEAR TO correspond to my notes in 'whole-mcu-example.txt'.
dec._blitCallback = function (x, y, width, height, buf) {
console.log({x:x, y:y, width:width, height:height, buf:buf});
console.log(fmt_rgb_buf(width, buf));
};
dec.decode(data);
});
});
describe('Full JPEG subsampled MCU example 1', function () {
/* TODO: this looks VERRRRRY SIMILAR to the above example */
var data = parseHex('040701a61bff6280f81fbaa2ff4dbc408dfccf405c15ff1f004800f500000000000000002dd4a462237ced2c9c25dca4cef7e6667d6626f3308063c3c7a1b7335badc24aaf0b5e9daf69590fcb96206b59e6f2ccb2bdd386b17dbb72182080d6288a2a1f2f0608a0fe87ae287f132f1023ff33d057c5ff470012403d0000000000000000ec1fb06cdacd2bdf9d79f3cde7f9f1362b7ce914ba521c79524a5bdc212a5ed47c3cbc6c7e072ae3e3c18384785f6ca6d4e22fb1af0c96449d1847a9c7037e966fa3ac8d4990830339463d277f25fc6f30ffe5f4f5cfec9f4ffff8bf00a0f5d358a2ab6e4e6778d929f686bd58ca9c26b8f7338f15105065dd39c718');
/*
it('should load quant tables and set subsamplingMode', function () {
dec.decode(data);
expect(dec._loadedQuantTables[0]).to.equal(4);
expect(dec._loadedQuantTables[1]).to.equal(7);
expect(dec.subsamplingMode).to.equal(422);
});
*/
it('should decode properly', function () {
// DOES NOT APPEAR TO correspond to my notes in 'whole-mcu-example.txt'.
dec._blitCallback = function (x, y, width, height, buf) {
console.log({x:x, y:y, width:width, height:height, buf:buf});
console.log(fmt_rgb_buf(width, buf));
};
dec.decode(data);
});
});
// TODO: On the 'full-frame decode' and 'frame udpate decode' tests, we are NOT actually asserting anything about the code's output yet.
/*
describe('Full frame decode test', function () {
it('should decode properly', function () {
throw "Won't work without jQuery (or some other way of loading data).";
dec._blitCallback = function (x, y, width, height, buf) {
if (x == 0 && y == 0) {
console.log({x:x, y:y, width:width, height:height, buf:buf});
console.log(fmt_rgb_buf(width, buf));
}
};
// TODO: This path won't work for other people, and the test case needs to be made to understand that this
// is async; plus, I had to manually add jQuery to the generated HTML.
$.get("/novnc-tests/tests/frame4.hex", function (data) {
data = parseHex(data);
dec.decode(data);
});
});
});
*/
describe('Frame update decode test', function () {
it('should decode properly', function () {
var data = parseHex('050501a69a3f6080008aa2a8000000900000000000000000');
console.log(data);
// this data should NOT be solid-white!
dec._blitCallback = function (x, y, width, height, buf) {
console.log({x:x, y:y, width:width, height:height, buf:buf});
console.log(fmt_rgb_buf(width, buf));
};
dec.decode(data);
});
});
});