forked from apps/plugin-preview
9995 lines
367 KiB
JavaScript
9995 lines
367 KiB
JavaScript
(function (global, factory) {
|
||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('path'), require('fs'), require('crypto')) :
|
||
typeof define === 'function' && define.amd ? define(['path', 'fs', 'crypto'], factory) :
|
||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.path, global.fs, global.crypto$1));
|
||
})(this, (function (path, fs, crypto$1) { 'use strict';
|
||
|
||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
||
|
||
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
||
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
||
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto$1);
|
||
|
||
function createCommonjsModule(fn, module) {
|
||
return module = { exports: {} }, fn(module, module.exports), module.exports;
|
||
}
|
||
|
||
var decoderProSimd = createCommonjsModule(function (module) {
|
||
var Module = typeof Module != "undefined" ? Module : {};
|
||
var moduleOverrides = Object.assign({}, Module);
|
||
var thisProgram = "./this.program";
|
||
var ENVIRONMENT_IS_WEB = typeof window == "object";
|
||
var ENVIRONMENT_IS_WORKER = typeof importScripts == "function";
|
||
var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string";
|
||
var scriptDirectory = "";
|
||
function locateFile(path) {
|
||
if (Module["locateFile"]) {
|
||
return Module["locateFile"](path, scriptDirectory);
|
||
}
|
||
return scriptDirectory + path;
|
||
}
|
||
var read_, readAsync, readBinary;
|
||
var fs;
|
||
var nodePath;
|
||
var requireNodeFS;
|
||
if (ENVIRONMENT_IS_NODE) {
|
||
if (ENVIRONMENT_IS_WORKER) {
|
||
scriptDirectory = path__default["default"].dirname(scriptDirectory) + "/";
|
||
} else {
|
||
scriptDirectory = __dirname + "/";
|
||
}
|
||
requireNodeFS = () => {
|
||
if (!nodePath) {
|
||
fs = fs__default["default"];
|
||
nodePath = path__default["default"];
|
||
}
|
||
};
|
||
read_ = function shell_read(filename, binary) {
|
||
requireNodeFS();
|
||
filename = nodePath["normalize"](filename);
|
||
return fs.readFileSync(filename, binary ? undefined : "utf8");
|
||
};
|
||
readBinary = filename => {
|
||
var ret = read_(filename, true);
|
||
if (!ret.buffer) {
|
||
ret = new Uint8Array(ret);
|
||
}
|
||
return ret;
|
||
};
|
||
readAsync = (filename, onload, onerror) => {
|
||
requireNodeFS();
|
||
filename = nodePath["normalize"](filename);
|
||
fs.readFile(filename, function (err, data) {
|
||
if (err) onerror(err);else onload(data.buffer);
|
||
});
|
||
};
|
||
if (process["argv"].length > 1) {
|
||
thisProgram = process["argv"][1].replace(/\\/g, "/");
|
||
}
|
||
process["argv"].slice(2);
|
||
{
|
||
module["exports"] = Module;
|
||
}
|
||
process["on"]("uncaughtException", function (ex) {
|
||
if (!(ex instanceof ExitStatus)) {
|
||
throw ex;
|
||
}
|
||
});
|
||
process["on"]("unhandledRejection", function (reason) {
|
||
throw reason;
|
||
});
|
||
Module["inspect"] = function () {
|
||
return "[Emscripten Module object]";
|
||
};
|
||
} else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
|
||
if (ENVIRONMENT_IS_WORKER) {
|
||
scriptDirectory = self.location.href;
|
||
} else if (typeof document != "undefined" && document.currentScript) {
|
||
scriptDirectory = document.currentScript.src;
|
||
}
|
||
if (scriptDirectory.indexOf("blob:") !== 0) {
|
||
scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1);
|
||
} else {
|
||
scriptDirectory = "";
|
||
}
|
||
{
|
||
read_ = url => {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open("GET", url, false);
|
||
xhr.send(null);
|
||
return xhr.responseText;
|
||
};
|
||
if (ENVIRONMENT_IS_WORKER) {
|
||
readBinary = url => {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open("GET", url, false);
|
||
xhr.responseType = "arraybuffer";
|
||
xhr.send(null);
|
||
return new Uint8Array(xhr.response);
|
||
};
|
||
}
|
||
readAsync = (url, onload, onerror) => {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open("GET", url, true);
|
||
xhr.responseType = "arraybuffer";
|
||
xhr.onload = () => {
|
||
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
||
onload(xhr.response);
|
||
return;
|
||
}
|
||
onerror();
|
||
};
|
||
xhr.onerror = onerror;
|
||
xhr.send(null);
|
||
};
|
||
}
|
||
} else ;
|
||
var out = Module["print"] || console.log.bind(console);
|
||
var err = Module["printErr"] || console.warn.bind(console);
|
||
Object.assign(Module, moduleOverrides);
|
||
moduleOverrides = null;
|
||
if (Module["arguments"]) Module["arguments"];
|
||
if (Module["thisProgram"]) thisProgram = Module["thisProgram"];
|
||
if (Module["quit"]) Module["quit"];
|
||
var POINTER_SIZE = 4;
|
||
var wasmBinary;
|
||
if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"];
|
||
Module["noExitRuntime"] || true;
|
||
if (typeof WebAssembly != "object") {
|
||
abort("no native wasm support detected");
|
||
}
|
||
var wasmMemory;
|
||
var ABORT = false;
|
||
function assert(condition, text) {
|
||
if (!condition) {
|
||
abort(text);
|
||
}
|
||
}
|
||
var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf8") : undefined;
|
||
function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) {
|
||
var endIdx = idx + maxBytesToRead;
|
||
var endPtr = idx;
|
||
while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
|
||
if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
|
||
return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
|
||
}
|
||
var str = "";
|
||
while (idx < endPtr) {
|
||
var u0 = heapOrArray[idx++];
|
||
if (!(u0 & 128)) {
|
||
str += String.fromCharCode(u0);
|
||
continue;
|
||
}
|
||
var u1 = heapOrArray[idx++] & 63;
|
||
if ((u0 & 224) == 192) {
|
||
str += String.fromCharCode((u0 & 31) << 6 | u1);
|
||
continue;
|
||
}
|
||
var u2 = heapOrArray[idx++] & 63;
|
||
if ((u0 & 240) == 224) {
|
||
u0 = (u0 & 15) << 12 | u1 << 6 | u2;
|
||
} else {
|
||
u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heapOrArray[idx++] & 63;
|
||
}
|
||
if (u0 < 65536) {
|
||
str += String.fromCharCode(u0);
|
||
} else {
|
||
var ch = u0 - 65536;
|
||
str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
|
||
}
|
||
}
|
||
return str;
|
||
}
|
||
function UTF8ToString(ptr, maxBytesToRead) {
|
||
return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "";
|
||
}
|
||
function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) {
|
||
if (!(maxBytesToWrite > 0)) return 0;
|
||
var startIdx = outIdx;
|
||
var endIdx = outIdx + maxBytesToWrite - 1;
|
||
for (var i = 0; i < str.length; ++i) {
|
||
var u = str.charCodeAt(i);
|
||
if (u >= 55296 && u <= 57343) {
|
||
var u1 = str.charCodeAt(++i);
|
||
u = 65536 + ((u & 1023) << 10) | u1 & 1023;
|
||
}
|
||
if (u <= 127) {
|
||
if (outIdx >= endIdx) break;
|
||
heap[outIdx++] = u;
|
||
} else if (u <= 2047) {
|
||
if (outIdx + 1 >= endIdx) break;
|
||
heap[outIdx++] = 192 | u >> 6;
|
||
heap[outIdx++] = 128 | u & 63;
|
||
} else if (u <= 65535) {
|
||
if (outIdx + 2 >= endIdx) break;
|
||
heap[outIdx++] = 224 | u >> 12;
|
||
heap[outIdx++] = 128 | u >> 6 & 63;
|
||
heap[outIdx++] = 128 | u & 63;
|
||
} else {
|
||
if (outIdx + 3 >= endIdx) break;
|
||
heap[outIdx++] = 240 | u >> 18;
|
||
heap[outIdx++] = 128 | u >> 12 & 63;
|
||
heap[outIdx++] = 128 | u >> 6 & 63;
|
||
heap[outIdx++] = 128 | u & 63;
|
||
}
|
||
}
|
||
heap[outIdx] = 0;
|
||
return outIdx - startIdx;
|
||
}
|
||
function stringToUTF8(str, outPtr, maxBytesToWrite) {
|
||
return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
|
||
}
|
||
function lengthBytesUTF8(str) {
|
||
var len = 0;
|
||
for (var i = 0; i < str.length; ++i) {
|
||
var c = str.charCodeAt(i);
|
||
if (c <= 127) {
|
||
len++;
|
||
} else if (c <= 2047) {
|
||
len += 2;
|
||
} else if (c >= 55296 && c <= 57343) {
|
||
len += 4;
|
||
++i;
|
||
} else {
|
||
len += 3;
|
||
}
|
||
}
|
||
return len;
|
||
}
|
||
var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;
|
||
function updateGlobalBufferAndViews(buf) {
|
||
buffer = buf;
|
||
Module["HEAP8"] = HEAP8 = new Int8Array(buf);
|
||
Module["HEAP16"] = HEAP16 = new Int16Array(buf);
|
||
Module["HEAP32"] = HEAP32 = new Int32Array(buf);
|
||
Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf);
|
||
Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf);
|
||
Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf);
|
||
Module["HEAPF32"] = HEAPF32 = new Float32Array(buf);
|
||
Module["HEAPF64"] = HEAPF64 = new Float64Array(buf);
|
||
}
|
||
Module["INITIAL_MEMORY"] || 268435456;
|
||
var wasmTable;
|
||
var __ATPRERUN__ = [];
|
||
var __ATINIT__ = [];
|
||
var __ATPOSTRUN__ = [];
|
||
function preRun() {
|
||
if (Module["preRun"]) {
|
||
if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]];
|
||
while (Module["preRun"].length) {
|
||
addOnPreRun(Module["preRun"].shift());
|
||
}
|
||
}
|
||
callRuntimeCallbacks(__ATPRERUN__);
|
||
}
|
||
function initRuntime() {
|
||
if (!Module["noFSInit"] && !FS.init.initialized) FS.init();
|
||
FS.ignorePermissions = false;
|
||
callRuntimeCallbacks(__ATINIT__);
|
||
}
|
||
function postRun() {
|
||
if (Module["postRun"]) {
|
||
if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]];
|
||
while (Module["postRun"].length) {
|
||
addOnPostRun(Module["postRun"].shift());
|
||
}
|
||
}
|
||
callRuntimeCallbacks(__ATPOSTRUN__);
|
||
}
|
||
function addOnPreRun(cb) {
|
||
__ATPRERUN__.unshift(cb);
|
||
}
|
||
function addOnInit(cb) {
|
||
__ATINIT__.unshift(cb);
|
||
}
|
||
function addOnPostRun(cb) {
|
||
__ATPOSTRUN__.unshift(cb);
|
||
}
|
||
var runDependencies = 0;
|
||
var dependenciesFulfilled = null;
|
||
function getUniqueRunDependency(id) {
|
||
return id;
|
||
}
|
||
function addRunDependency(id) {
|
||
runDependencies++;
|
||
if (Module["monitorRunDependencies"]) {
|
||
Module["monitorRunDependencies"](runDependencies);
|
||
}
|
||
}
|
||
function removeRunDependency(id) {
|
||
runDependencies--;
|
||
if (Module["monitorRunDependencies"]) {
|
||
Module["monitorRunDependencies"](runDependencies);
|
||
}
|
||
if (runDependencies == 0) {
|
||
if (dependenciesFulfilled) {
|
||
var callback = dependenciesFulfilled;
|
||
dependenciesFulfilled = null;
|
||
callback();
|
||
}
|
||
}
|
||
}
|
||
function abort(what) {
|
||
{
|
||
if (Module["onAbort"]) {
|
||
Module["onAbort"](what);
|
||
}
|
||
}
|
||
what = "Aborted(" + what + ")";
|
||
err(what);
|
||
ABORT = true;
|
||
what += ". Build with -sASSERTIONS for more info.";
|
||
var e = new WebAssembly.RuntimeError(what);
|
||
throw e;
|
||
}
|
||
var dataURIPrefix = "data:application/octet-stream;base64,";
|
||
function isDataURI(filename) {
|
||
return filename.startsWith(dataURIPrefix);
|
||
}
|
||
function isFileURI(filename) {
|
||
return filename.startsWith("file://");
|
||
}
|
||
var wasmBinaryFile;
|
||
wasmBinaryFile = "decoder-pro-simd.wasm";
|
||
if (!isDataURI(wasmBinaryFile)) {
|
||
wasmBinaryFile = locateFile(wasmBinaryFile);
|
||
}
|
||
function getBinary(file) {
|
||
try {
|
||
if (file == wasmBinaryFile && wasmBinary) {
|
||
return new Uint8Array(wasmBinary);
|
||
}
|
||
if (readBinary) {
|
||
return readBinary(file);
|
||
}
|
||
throw "both async and sync fetching of the wasm failed";
|
||
} catch (err) {
|
||
abort(err);
|
||
}
|
||
}
|
||
function getBinaryPromise() {
|
||
if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) {
|
||
if (typeof fetch == "function" && !isFileURI(wasmBinaryFile)) {
|
||
return fetch(wasmBinaryFile, {
|
||
credentials: "same-origin"
|
||
}).then(function (response) {
|
||
if (!response["ok"]) {
|
||
throw "failed to load wasm binary file at '" + wasmBinaryFile + "'";
|
||
}
|
||
return response["arrayBuffer"]();
|
||
}).catch(function () {
|
||
return getBinary(wasmBinaryFile);
|
||
});
|
||
} else {
|
||
if (readAsync) {
|
||
return new Promise(function (resolve, reject) {
|
||
readAsync(wasmBinaryFile, function (response) {
|
||
resolve(new Uint8Array(response));
|
||
}, reject);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
return Promise.resolve().then(function () {
|
||
return getBinary(wasmBinaryFile);
|
||
});
|
||
}
|
||
function createWasm() {
|
||
var info = {
|
||
"a": asmLibraryArg
|
||
};
|
||
function receiveInstance(instance, module) {
|
||
var exports = instance.exports;
|
||
Module["asm"] = exports;
|
||
wasmMemory = Module["asm"]["F"];
|
||
updateGlobalBufferAndViews(wasmMemory.buffer);
|
||
wasmTable = Module["asm"]["J"];
|
||
addOnInit(Module["asm"]["G"]);
|
||
removeRunDependency();
|
||
}
|
||
addRunDependency();
|
||
function receiveInstantiationResult(result) {
|
||
receiveInstance(result["instance"]);
|
||
}
|
||
function instantiateArrayBuffer(receiver) {
|
||
return getBinaryPromise().then(function (binary) {
|
||
return WebAssembly.instantiate(binary, info);
|
||
}).then(function (instance) {
|
||
return instance;
|
||
}).then(receiver, function (reason) {
|
||
err("failed to asynchronously prepare wasm: " + reason);
|
||
abort(reason);
|
||
});
|
||
}
|
||
function instantiateAsync() {
|
||
if (!wasmBinary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == "function") {
|
||
return fetch(wasmBinaryFile, {
|
||
credentials: "same-origin"
|
||
}).then(function (response) {
|
||
var result = WebAssembly.instantiateStreaming(response, info);
|
||
return result.then(receiveInstantiationResult, function (reason) {
|
||
err("wasm streaming compile failed: " + reason);
|
||
err("falling back to ArrayBuffer instantiation");
|
||
return instantiateArrayBuffer(receiveInstantiationResult);
|
||
});
|
||
});
|
||
} else {
|
||
return instantiateArrayBuffer(receiveInstantiationResult);
|
||
}
|
||
}
|
||
if (Module["instantiateWasm"]) {
|
||
try {
|
||
var exports = Module["instantiateWasm"](info, receiveInstance);
|
||
return exports;
|
||
} catch (e) {
|
||
err("Module.instantiateWasm callback failed with error: " + e);
|
||
return false;
|
||
}
|
||
}
|
||
instantiateAsync();
|
||
return {};
|
||
}
|
||
var tempDouble;
|
||
var tempI64;
|
||
function ExitStatus(status) {
|
||
this.name = "ExitStatus";
|
||
this.message = "Program terminated with exit(" + status + ")";
|
||
this.status = status;
|
||
}
|
||
function callRuntimeCallbacks(callbacks) {
|
||
while (callbacks.length > 0) {
|
||
callbacks.shift()(Module);
|
||
}
|
||
}
|
||
function ___cxa_allocate_exception(size) {
|
||
return _malloc(size + 24) + 24;
|
||
}
|
||
function ExceptionInfo(excPtr) {
|
||
this.excPtr = excPtr;
|
||
this.ptr = excPtr - 24;
|
||
this.set_type = function (type) {
|
||
HEAPU32[this.ptr + 4 >> 2] = type;
|
||
};
|
||
this.get_type = function () {
|
||
return HEAPU32[this.ptr + 4 >> 2];
|
||
};
|
||
this.set_destructor = function (destructor) {
|
||
HEAPU32[this.ptr + 8 >> 2] = destructor;
|
||
};
|
||
this.get_destructor = function () {
|
||
return HEAPU32[this.ptr + 8 >> 2];
|
||
};
|
||
this.set_refcount = function (refcount) {
|
||
HEAP32[this.ptr >> 2] = refcount;
|
||
};
|
||
this.set_caught = function (caught) {
|
||
caught = caught ? 1 : 0;
|
||
HEAP8[this.ptr + 12 >> 0] = caught;
|
||
};
|
||
this.get_caught = function () {
|
||
return HEAP8[this.ptr + 12 >> 0] != 0;
|
||
};
|
||
this.set_rethrown = function (rethrown) {
|
||
rethrown = rethrown ? 1 : 0;
|
||
HEAP8[this.ptr + 13 >> 0] = rethrown;
|
||
};
|
||
this.get_rethrown = function () {
|
||
return HEAP8[this.ptr + 13 >> 0] != 0;
|
||
};
|
||
this.init = function (type, destructor) {
|
||
this.set_adjusted_ptr(0);
|
||
this.set_type(type);
|
||
this.set_destructor(destructor);
|
||
this.set_refcount(0);
|
||
this.set_caught(false);
|
||
this.set_rethrown(false);
|
||
};
|
||
this.add_ref = function () {
|
||
var value = HEAP32[this.ptr >> 2];
|
||
HEAP32[this.ptr >> 2] = value + 1;
|
||
};
|
||
this.release_ref = function () {
|
||
var prev = HEAP32[this.ptr >> 2];
|
||
HEAP32[this.ptr >> 2] = prev - 1;
|
||
return prev === 1;
|
||
};
|
||
this.set_adjusted_ptr = function (adjustedPtr) {
|
||
HEAPU32[this.ptr + 16 >> 2] = adjustedPtr;
|
||
};
|
||
this.get_adjusted_ptr = function () {
|
||
return HEAPU32[this.ptr + 16 >> 2];
|
||
};
|
||
this.get_exception_ptr = function () {
|
||
var isPointer = ___cxa_is_pointer_type(this.get_type());
|
||
if (isPointer) {
|
||
return HEAPU32[this.excPtr >> 2];
|
||
}
|
||
var adjusted = this.get_adjusted_ptr();
|
||
if (adjusted !== 0) return adjusted;
|
||
return this.excPtr;
|
||
};
|
||
}
|
||
function ___cxa_throw(ptr, type, destructor) {
|
||
var info = new ExceptionInfo(ptr);
|
||
info.init(type, destructor);
|
||
throw ptr;
|
||
}
|
||
function setErrNo(value) {
|
||
HEAP32[___errno_location() >> 2] = value;
|
||
return value;
|
||
}
|
||
var PATH = {
|
||
isAbs: path => path.charAt(0) === "/",
|
||
splitPath: filename => {
|
||
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
|
||
return splitPathRe.exec(filename).slice(1);
|
||
},
|
||
normalizeArray: (parts, allowAboveRoot) => {
|
||
var up = 0;
|
||
for (var i = parts.length - 1; i >= 0; i--) {
|
||
var last = parts[i];
|
||
if (last === ".") {
|
||
parts.splice(i, 1);
|
||
} else if (last === "..") {
|
||
parts.splice(i, 1);
|
||
up++;
|
||
} else if (up) {
|
||
parts.splice(i, 1);
|
||
up--;
|
||
}
|
||
}
|
||
if (allowAboveRoot) {
|
||
for (; up; up--) {
|
||
parts.unshift("..");
|
||
}
|
||
}
|
||
return parts;
|
||
},
|
||
normalize: path => {
|
||
var isAbsolute = PATH.isAbs(path),
|
||
trailingSlash = path.substr(-1) === "/";
|
||
path = PATH.normalizeArray(path.split("/").filter(p => !!p), !isAbsolute).join("/");
|
||
if (!path && !isAbsolute) {
|
||
path = ".";
|
||
}
|
||
if (path && trailingSlash) {
|
||
path += "/";
|
||
}
|
||
return (isAbsolute ? "/" : "") + path;
|
||
},
|
||
dirname: path => {
|
||
var result = PATH.splitPath(path),
|
||
root = result[0],
|
||
dir = result[1];
|
||
if (!root && !dir) {
|
||
return ".";
|
||
}
|
||
if (dir) {
|
||
dir = dir.substr(0, dir.length - 1);
|
||
}
|
||
return root + dir;
|
||
},
|
||
basename: path => {
|
||
if (path === "/") return "/";
|
||
path = PATH.normalize(path);
|
||
path = path.replace(/\/$/, "");
|
||
var lastSlash = path.lastIndexOf("/");
|
||
if (lastSlash === -1) return path;
|
||
return path.substr(lastSlash + 1);
|
||
},
|
||
join: function () {
|
||
var paths = Array.prototype.slice.call(arguments, 0);
|
||
return PATH.normalize(paths.join("/"));
|
||
},
|
||
join2: (l, r) => {
|
||
return PATH.normalize(l + "/" + r);
|
||
}
|
||
};
|
||
function getRandomDevice() {
|
||
if (typeof crypto == "object" && typeof crypto["getRandomValues"] == "function") {
|
||
var randomBuffer = new Uint8Array(1);
|
||
return () => {
|
||
crypto.getRandomValues(randomBuffer);
|
||
return randomBuffer[0];
|
||
};
|
||
} else if (ENVIRONMENT_IS_NODE) {
|
||
try {
|
||
var crypto_module = crypto__default["default"];
|
||
return () => crypto_module["randomBytes"](1)[0];
|
||
} catch (e) {}
|
||
}
|
||
return () => abort("randomDevice");
|
||
}
|
||
var PATH_FS = {
|
||
resolve: function () {
|
||
var resolvedPath = "",
|
||
resolvedAbsolute = false;
|
||
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
||
var path = i >= 0 ? arguments[i] : FS.cwd();
|
||
if (typeof path != "string") {
|
||
throw new TypeError("Arguments to path.resolve must be strings");
|
||
} else if (!path) {
|
||
return "";
|
||
}
|
||
resolvedPath = path + "/" + resolvedPath;
|
||
resolvedAbsolute = PATH.isAbs(path);
|
||
}
|
||
resolvedPath = PATH.normalizeArray(resolvedPath.split("/").filter(p => !!p), !resolvedAbsolute).join("/");
|
||
return (resolvedAbsolute ? "/" : "") + resolvedPath || ".";
|
||
},
|
||
relative: (from, to) => {
|
||
from = PATH_FS.resolve(from).substr(1);
|
||
to = PATH_FS.resolve(to).substr(1);
|
||
function trim(arr) {
|
||
var start = 0;
|
||
for (; start < arr.length; start++) {
|
||
if (arr[start] !== "") break;
|
||
}
|
||
var end = arr.length - 1;
|
||
for (; end >= 0; end--) {
|
||
if (arr[end] !== "") break;
|
||
}
|
||
if (start > end) return [];
|
||
return arr.slice(start, end - start + 1);
|
||
}
|
||
var fromParts = trim(from.split("/"));
|
||
var toParts = trim(to.split("/"));
|
||
var length = Math.min(fromParts.length, toParts.length);
|
||
var samePartsLength = length;
|
||
for (var i = 0; i < length; i++) {
|
||
if (fromParts[i] !== toParts[i]) {
|
||
samePartsLength = i;
|
||
break;
|
||
}
|
||
}
|
||
var outputParts = [];
|
||
for (var i = samePartsLength; i < fromParts.length; i++) {
|
||
outputParts.push("..");
|
||
}
|
||
outputParts = outputParts.concat(toParts.slice(samePartsLength));
|
||
return outputParts.join("/");
|
||
}
|
||
};
|
||
function intArrayFromString(stringy, dontAddNull, length) {
|
||
var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1;
|
||
var u8array = new Array(len);
|
||
var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length);
|
||
if (dontAddNull) u8array.length = numBytesWritten;
|
||
return u8array;
|
||
}
|
||
var TTY = {
|
||
ttys: [],
|
||
init: function () {},
|
||
shutdown: function () {},
|
||
register: function (dev, ops) {
|
||
TTY.ttys[dev] = {
|
||
input: [],
|
||
output: [],
|
||
ops: ops
|
||
};
|
||
FS.registerDevice(dev, TTY.stream_ops);
|
||
},
|
||
stream_ops: {
|
||
open: function (stream) {
|
||
var tty = TTY.ttys[stream.node.rdev];
|
||
if (!tty) {
|
||
throw new FS.ErrnoError(43);
|
||
}
|
||
stream.tty = tty;
|
||
stream.seekable = false;
|
||
},
|
||
close: function (stream) {
|
||
stream.tty.ops.flush(stream.tty);
|
||
},
|
||
flush: function (stream) {
|
||
stream.tty.ops.flush(stream.tty);
|
||
},
|
||
read: function (stream, buffer, offset, length, pos) {
|
||
if (!stream.tty || !stream.tty.ops.get_char) {
|
||
throw new FS.ErrnoError(60);
|
||
}
|
||
var bytesRead = 0;
|
||
for (var i = 0; i < length; i++) {
|
||
var result;
|
||
try {
|
||
result = stream.tty.ops.get_char(stream.tty);
|
||
} catch (e) {
|
||
throw new FS.ErrnoError(29);
|
||
}
|
||
if (result === undefined && bytesRead === 0) {
|
||
throw new FS.ErrnoError(6);
|
||
}
|
||
if (result === null || result === undefined) break;
|
||
bytesRead++;
|
||
buffer[offset + i] = result;
|
||
}
|
||
if (bytesRead) {
|
||
stream.node.timestamp = Date.now();
|
||
}
|
||
return bytesRead;
|
||
},
|
||
write: function (stream, buffer, offset, length, pos) {
|
||
if (!stream.tty || !stream.tty.ops.put_char) {
|
||
throw new FS.ErrnoError(60);
|
||
}
|
||
try {
|
||
for (var i = 0; i < length; i++) {
|
||
stream.tty.ops.put_char(stream.tty, buffer[offset + i]);
|
||
}
|
||
} catch (e) {
|
||
throw new FS.ErrnoError(29);
|
||
}
|
||
if (length) {
|
||
stream.node.timestamp = Date.now();
|
||
}
|
||
return i;
|
||
}
|
||
},
|
||
default_tty_ops: {
|
||
get_char: function (tty) {
|
||
if (!tty.input.length) {
|
||
var result = null;
|
||
if (ENVIRONMENT_IS_NODE) {
|
||
var BUFSIZE = 256;
|
||
var buf = Buffer.alloc(BUFSIZE);
|
||
var bytesRead = 0;
|
||
try {
|
||
bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1);
|
||
} catch (e) {
|
||
if (e.toString().includes("EOF")) bytesRead = 0;else throw e;
|
||
}
|
||
if (bytesRead > 0) {
|
||
result = buf.slice(0, bytesRead).toString("utf-8");
|
||
} else {
|
||
result = null;
|
||
}
|
||
} else if (typeof window != "undefined" && typeof window.prompt == "function") {
|
||
result = window.prompt("Input: ");
|
||
if (result !== null) {
|
||
result += "\n";
|
||
}
|
||
} else if (typeof readline == "function") {
|
||
result = readline();
|
||
if (result !== null) {
|
||
result += "\n";
|
||
}
|
||
}
|
||
if (!result) {
|
||
return null;
|
||
}
|
||
tty.input = intArrayFromString(result, true);
|
||
}
|
||
return tty.input.shift();
|
||
},
|
||
put_char: function (tty, val) {
|
||
if (val === null || val === 10) {
|
||
out(UTF8ArrayToString(tty.output, 0));
|
||
tty.output = [];
|
||
} else {
|
||
if (val != 0) tty.output.push(val);
|
||
}
|
||
},
|
||
flush: function (tty) {
|
||
if (tty.output && tty.output.length > 0) {
|
||
out(UTF8ArrayToString(tty.output, 0));
|
||
tty.output = [];
|
||
}
|
||
}
|
||
},
|
||
default_tty1_ops: {
|
||
put_char: function (tty, val) {
|
||
if (val === null || val === 10) {
|
||
err(UTF8ArrayToString(tty.output, 0));
|
||
tty.output = [];
|
||
} else {
|
||
if (val != 0) tty.output.push(val);
|
||
}
|
||
},
|
||
flush: function (tty) {
|
||
if (tty.output && tty.output.length > 0) {
|
||
err(UTF8ArrayToString(tty.output, 0));
|
||
tty.output = [];
|
||
}
|
||
}
|
||
}
|
||
};
|
||
function zeroMemory(address, size) {
|
||
HEAPU8.fill(0, address, address + size);
|
||
}
|
||
function alignMemory(size, alignment) {
|
||
return Math.ceil(size / alignment) * alignment;
|
||
}
|
||
function mmapAlloc(size) {
|
||
size = alignMemory(size, 65536);
|
||
var ptr = _emscripten_builtin_memalign(65536, size);
|
||
if (!ptr) return 0;
|
||
zeroMemory(ptr, size);
|
||
return ptr;
|
||
}
|
||
var MEMFS = {
|
||
ops_table: null,
|
||
mount: function (mount) {
|
||
return MEMFS.createNode(null, "/", 16384 | 511, 0);
|
||
},
|
||
createNode: function (parent, name, mode, dev) {
|
||
if (FS.isBlkdev(mode) || FS.isFIFO(mode)) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
if (!MEMFS.ops_table) {
|
||
MEMFS.ops_table = {
|
||
dir: {
|
||
node: {
|
||
getattr: MEMFS.node_ops.getattr,
|
||
setattr: MEMFS.node_ops.setattr,
|
||
lookup: MEMFS.node_ops.lookup,
|
||
mknod: MEMFS.node_ops.mknod,
|
||
rename: MEMFS.node_ops.rename,
|
||
unlink: MEMFS.node_ops.unlink,
|
||
rmdir: MEMFS.node_ops.rmdir,
|
||
readdir: MEMFS.node_ops.readdir,
|
||
symlink: MEMFS.node_ops.symlink
|
||
},
|
||
stream: {
|
||
llseek: MEMFS.stream_ops.llseek
|
||
}
|
||
},
|
||
file: {
|
||
node: {
|
||
getattr: MEMFS.node_ops.getattr,
|
||
setattr: MEMFS.node_ops.setattr
|
||
},
|
||
stream: {
|
||
llseek: MEMFS.stream_ops.llseek,
|
||
read: MEMFS.stream_ops.read,
|
||
write: MEMFS.stream_ops.write,
|
||
allocate: MEMFS.stream_ops.allocate,
|
||
mmap: MEMFS.stream_ops.mmap,
|
||
msync: MEMFS.stream_ops.msync
|
||
}
|
||
},
|
||
link: {
|
||
node: {
|
||
getattr: MEMFS.node_ops.getattr,
|
||
setattr: MEMFS.node_ops.setattr,
|
||
readlink: MEMFS.node_ops.readlink
|
||
},
|
||
stream: {}
|
||
},
|
||
chrdev: {
|
||
node: {
|
||
getattr: MEMFS.node_ops.getattr,
|
||
setattr: MEMFS.node_ops.setattr
|
||
},
|
||
stream: FS.chrdev_stream_ops
|
||
}
|
||
};
|
||
}
|
||
var node = FS.createNode(parent, name, mode, dev);
|
||
if (FS.isDir(node.mode)) {
|
||
node.node_ops = MEMFS.ops_table.dir.node;
|
||
node.stream_ops = MEMFS.ops_table.dir.stream;
|
||
node.contents = {};
|
||
} else if (FS.isFile(node.mode)) {
|
||
node.node_ops = MEMFS.ops_table.file.node;
|
||
node.stream_ops = MEMFS.ops_table.file.stream;
|
||
node.usedBytes = 0;
|
||
node.contents = null;
|
||
} else if (FS.isLink(node.mode)) {
|
||
node.node_ops = MEMFS.ops_table.link.node;
|
||
node.stream_ops = MEMFS.ops_table.link.stream;
|
||
} else if (FS.isChrdev(node.mode)) {
|
||
node.node_ops = MEMFS.ops_table.chrdev.node;
|
||
node.stream_ops = MEMFS.ops_table.chrdev.stream;
|
||
}
|
||
node.timestamp = Date.now();
|
||
if (parent) {
|
||
parent.contents[name] = node;
|
||
parent.timestamp = node.timestamp;
|
||
}
|
||
return node;
|
||
},
|
||
getFileDataAsTypedArray: function (node) {
|
||
if (!node.contents) return new Uint8Array(0);
|
||
if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes);
|
||
return new Uint8Array(node.contents);
|
||
},
|
||
expandFileStorage: function (node, newCapacity) {
|
||
var prevCapacity = node.contents ? node.contents.length : 0;
|
||
if (prevCapacity >= newCapacity) return;
|
||
var CAPACITY_DOUBLING_MAX = 1024 * 1024;
|
||
newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125) >>> 0);
|
||
if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256);
|
||
var oldContents = node.contents;
|
||
node.contents = new Uint8Array(newCapacity);
|
||
if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0);
|
||
},
|
||
resizeFileStorage: function (node, newSize) {
|
||
if (node.usedBytes == newSize) return;
|
||
if (newSize == 0) {
|
||
node.contents = null;
|
||
node.usedBytes = 0;
|
||
} else {
|
||
var oldContents = node.contents;
|
||
node.contents = new Uint8Array(newSize);
|
||
if (oldContents) {
|
||
node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes)));
|
||
}
|
||
node.usedBytes = newSize;
|
||
}
|
||
},
|
||
node_ops: {
|
||
getattr: function (node) {
|
||
var attr = {};
|
||
attr.dev = FS.isChrdev(node.mode) ? node.id : 1;
|
||
attr.ino = node.id;
|
||
attr.mode = node.mode;
|
||
attr.nlink = 1;
|
||
attr.uid = 0;
|
||
attr.gid = 0;
|
||
attr.rdev = node.rdev;
|
||
if (FS.isDir(node.mode)) {
|
||
attr.size = 4096;
|
||
} else if (FS.isFile(node.mode)) {
|
||
attr.size = node.usedBytes;
|
||
} else if (FS.isLink(node.mode)) {
|
||
attr.size = node.link.length;
|
||
} else {
|
||
attr.size = 0;
|
||
}
|
||
attr.atime = new Date(node.timestamp);
|
||
attr.mtime = new Date(node.timestamp);
|
||
attr.ctime = new Date(node.timestamp);
|
||
attr.blksize = 4096;
|
||
attr.blocks = Math.ceil(attr.size / attr.blksize);
|
||
return attr;
|
||
},
|
||
setattr: function (node, attr) {
|
||
if (attr.mode !== undefined) {
|
||
node.mode = attr.mode;
|
||
}
|
||
if (attr.timestamp !== undefined) {
|
||
node.timestamp = attr.timestamp;
|
||
}
|
||
if (attr.size !== undefined) {
|
||
MEMFS.resizeFileStorage(node, attr.size);
|
||
}
|
||
},
|
||
lookup: function (parent, name) {
|
||
throw FS.genericErrors[44];
|
||
},
|
||
mknod: function (parent, name, mode, dev) {
|
||
return MEMFS.createNode(parent, name, mode, dev);
|
||
},
|
||
rename: function (old_node, new_dir, new_name) {
|
||
if (FS.isDir(old_node.mode)) {
|
||
var new_node;
|
||
try {
|
||
new_node = FS.lookupNode(new_dir, new_name);
|
||
} catch (e) {}
|
||
if (new_node) {
|
||
for (var i in new_node.contents) {
|
||
throw new FS.ErrnoError(55);
|
||
}
|
||
}
|
||
}
|
||
delete old_node.parent.contents[old_node.name];
|
||
old_node.parent.timestamp = Date.now();
|
||
old_node.name = new_name;
|
||
new_dir.contents[new_name] = old_node;
|
||
new_dir.timestamp = old_node.parent.timestamp;
|
||
old_node.parent = new_dir;
|
||
},
|
||
unlink: function (parent, name) {
|
||
delete parent.contents[name];
|
||
parent.timestamp = Date.now();
|
||
},
|
||
rmdir: function (parent, name) {
|
||
var node = FS.lookupNode(parent, name);
|
||
for (var i in node.contents) {
|
||
throw new FS.ErrnoError(55);
|
||
}
|
||
delete parent.contents[name];
|
||
parent.timestamp = Date.now();
|
||
},
|
||
readdir: function (node) {
|
||
var entries = [".", ".."];
|
||
for (var key in node.contents) {
|
||
if (!node.contents.hasOwnProperty(key)) {
|
||
continue;
|
||
}
|
||
entries.push(key);
|
||
}
|
||
return entries;
|
||
},
|
||
symlink: function (parent, newname, oldpath) {
|
||
var node = MEMFS.createNode(parent, newname, 511 | 40960, 0);
|
||
node.link = oldpath;
|
||
return node;
|
||
},
|
||
readlink: function (node) {
|
||
if (!FS.isLink(node.mode)) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
return node.link;
|
||
}
|
||
},
|
||
stream_ops: {
|
||
read: function (stream, buffer, offset, length, position) {
|
||
var contents = stream.node.contents;
|
||
if (position >= stream.node.usedBytes) return 0;
|
||
var size = Math.min(stream.node.usedBytes - position, length);
|
||
if (size > 8 && contents.subarray) {
|
||
buffer.set(contents.subarray(position, position + size), offset);
|
||
} else {
|
||
for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
|
||
}
|
||
return size;
|
||
},
|
||
write: function (stream, buffer, offset, length, position, canOwn) {
|
||
if (!length) return 0;
|
||
var node = stream.node;
|
||
node.timestamp = Date.now();
|
||
if (buffer.subarray && (!node.contents || node.contents.subarray)) {
|
||
if (canOwn) {
|
||
node.contents = buffer.subarray(offset, offset + length);
|
||
node.usedBytes = length;
|
||
return length;
|
||
} else if (node.usedBytes === 0 && position === 0) {
|
||
node.contents = buffer.slice(offset, offset + length);
|
||
node.usedBytes = length;
|
||
return length;
|
||
} else if (position + length <= node.usedBytes) {
|
||
node.contents.set(buffer.subarray(offset, offset + length), position);
|
||
return length;
|
||
}
|
||
}
|
||
MEMFS.expandFileStorage(node, position + length);
|
||
if (node.contents.subarray && buffer.subarray) {
|
||
node.contents.set(buffer.subarray(offset, offset + length), position);
|
||
} else {
|
||
for (var i = 0; i < length; i++) {
|
||
node.contents[position + i] = buffer[offset + i];
|
||
}
|
||
}
|
||
node.usedBytes = Math.max(node.usedBytes, position + length);
|
||
return length;
|
||
},
|
||
llseek: function (stream, offset, whence) {
|
||
var position = offset;
|
||
if (whence === 1) {
|
||
position += stream.position;
|
||
} else if (whence === 2) {
|
||
if (FS.isFile(stream.node.mode)) {
|
||
position += stream.node.usedBytes;
|
||
}
|
||
}
|
||
if (position < 0) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
return position;
|
||
},
|
||
allocate: function (stream, offset, length) {
|
||
MEMFS.expandFileStorage(stream.node, offset + length);
|
||
stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length);
|
||
},
|
||
mmap: function (stream, length, position, prot, flags) {
|
||
if (!FS.isFile(stream.node.mode)) {
|
||
throw new FS.ErrnoError(43);
|
||
}
|
||
var ptr;
|
||
var allocated;
|
||
var contents = stream.node.contents;
|
||
if (!(flags & 2) && contents.buffer === buffer) {
|
||
allocated = false;
|
||
ptr = contents.byteOffset;
|
||
} else {
|
||
if (position > 0 || position + length < contents.length) {
|
||
if (contents.subarray) {
|
||
contents = contents.subarray(position, position + length);
|
||
} else {
|
||
contents = Array.prototype.slice.call(contents, position, position + length);
|
||
}
|
||
}
|
||
allocated = true;
|
||
ptr = mmapAlloc(length);
|
||
if (!ptr) {
|
||
throw new FS.ErrnoError(48);
|
||
}
|
||
HEAP8.set(contents, ptr);
|
||
}
|
||
return {
|
||
ptr: ptr,
|
||
allocated: allocated
|
||
};
|
||
},
|
||
msync: function (stream, buffer, offset, length, mmapFlags) {
|
||
if (!FS.isFile(stream.node.mode)) {
|
||
throw new FS.ErrnoError(43);
|
||
}
|
||
if (mmapFlags & 2) {
|
||
return 0;
|
||
}
|
||
MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false);
|
||
return 0;
|
||
}
|
||
}
|
||
};
|
||
function asyncLoad(url, onload, onerror, noRunDep) {
|
||
var dep = !noRunDep ? getUniqueRunDependency("al " + url) : "";
|
||
readAsync(url, arrayBuffer => {
|
||
assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).');
|
||
onload(new Uint8Array(arrayBuffer));
|
||
if (dep) removeRunDependency();
|
||
}, event => {
|
||
if (onerror) {
|
||
onerror();
|
||
} else {
|
||
throw 'Loading data file "' + url + '" failed.';
|
||
}
|
||
});
|
||
if (dep) addRunDependency();
|
||
}
|
||
var FS = {
|
||
root: null,
|
||
mounts: [],
|
||
devices: {},
|
||
streams: [],
|
||
nextInode: 1,
|
||
nameTable: null,
|
||
currentPath: "/",
|
||
initialized: false,
|
||
ignorePermissions: true,
|
||
ErrnoError: null,
|
||
genericErrors: {},
|
||
filesystems: null,
|
||
syncFSRequests: 0,
|
||
lookupPath: function (path) {
|
||
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||
path = PATH_FS.resolve(FS.cwd(), path);
|
||
if (!path) return {
|
||
path: "",
|
||
node: null
|
||
};
|
||
var defaults = {
|
||
follow_mount: true,
|
||
recurse_count: 0
|
||
};
|
||
opts = Object.assign(defaults, opts);
|
||
if (opts.recurse_count > 8) {
|
||
throw new FS.ErrnoError(32);
|
||
}
|
||
var parts = PATH.normalizeArray(path.split("/").filter(p => !!p), false);
|
||
var current = FS.root;
|
||
var current_path = "/";
|
||
for (var i = 0; i < parts.length; i++) {
|
||
var islast = i === parts.length - 1;
|
||
if (islast && opts.parent) {
|
||
break;
|
||
}
|
||
current = FS.lookupNode(current, parts[i]);
|
||
current_path = PATH.join2(current_path, parts[i]);
|
||
if (FS.isMountpoint(current)) {
|
||
if (!islast || islast && opts.follow_mount) {
|
||
current = current.mounted.root;
|
||
}
|
||
}
|
||
if (!islast || opts.follow) {
|
||
var count = 0;
|
||
while (FS.isLink(current.mode)) {
|
||
var link = FS.readlink(current_path);
|
||
current_path = PATH_FS.resolve(PATH.dirname(current_path), link);
|
||
var lookup = FS.lookupPath(current_path, {
|
||
recurse_count: opts.recurse_count + 1
|
||
});
|
||
current = lookup.node;
|
||
if (count++ > 40) {
|
||
throw new FS.ErrnoError(32);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return {
|
||
path: current_path,
|
||
node: current
|
||
};
|
||
},
|
||
getPath: node => {
|
||
var path;
|
||
while (true) {
|
||
if (FS.isRoot(node)) {
|
||
var mount = node.mount.mountpoint;
|
||
if (!path) return mount;
|
||
return mount[mount.length - 1] !== "/" ? mount + "/" + path : mount + path;
|
||
}
|
||
path = path ? node.name + "/" + path : node.name;
|
||
node = node.parent;
|
||
}
|
||
},
|
||
hashName: (parentid, name) => {
|
||
var hash = 0;
|
||
for (var i = 0; i < name.length; i++) {
|
||
hash = (hash << 5) - hash + name.charCodeAt(i) | 0;
|
||
}
|
||
return (parentid + hash >>> 0) % FS.nameTable.length;
|
||
},
|
||
hashAddNode: node => {
|
||
var hash = FS.hashName(node.parent.id, node.name);
|
||
node.name_next = FS.nameTable[hash];
|
||
FS.nameTable[hash] = node;
|
||
},
|
||
hashRemoveNode: node => {
|
||
var hash = FS.hashName(node.parent.id, node.name);
|
||
if (FS.nameTable[hash] === node) {
|
||
FS.nameTable[hash] = node.name_next;
|
||
} else {
|
||
var current = FS.nameTable[hash];
|
||
while (current) {
|
||
if (current.name_next === node) {
|
||
current.name_next = node.name_next;
|
||
break;
|
||
}
|
||
current = current.name_next;
|
||
}
|
||
}
|
||
},
|
||
lookupNode: (parent, name) => {
|
||
var errCode = FS.mayLookup(parent);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode, parent);
|
||
}
|
||
var hash = FS.hashName(parent.id, name);
|
||
for (var node = FS.nameTable[hash]; node; node = node.name_next) {
|
||
var nodeName = node.name;
|
||
if (node.parent.id === parent.id && nodeName === name) {
|
||
return node;
|
||
}
|
||
}
|
||
return FS.lookup(parent, name);
|
||
},
|
||
createNode: (parent, name, mode, rdev) => {
|
||
var node = new FS.FSNode(parent, name, mode, rdev);
|
||
FS.hashAddNode(node);
|
||
return node;
|
||
},
|
||
destroyNode: node => {
|
||
FS.hashRemoveNode(node);
|
||
},
|
||
isRoot: node => {
|
||
return node === node.parent;
|
||
},
|
||
isMountpoint: node => {
|
||
return !!node.mounted;
|
||
},
|
||
isFile: mode => {
|
||
return (mode & 61440) === 32768;
|
||
},
|
||
isDir: mode => {
|
||
return (mode & 61440) === 16384;
|
||
},
|
||
isLink: mode => {
|
||
return (mode & 61440) === 40960;
|
||
},
|
||
isChrdev: mode => {
|
||
return (mode & 61440) === 8192;
|
||
},
|
||
isBlkdev: mode => {
|
||
return (mode & 61440) === 24576;
|
||
},
|
||
isFIFO: mode => {
|
||
return (mode & 61440) === 4096;
|
||
},
|
||
isSocket: mode => {
|
||
return (mode & 49152) === 49152;
|
||
},
|
||
flagModes: {
|
||
"r": 0,
|
||
"r+": 2,
|
||
"w": 577,
|
||
"w+": 578,
|
||
"a": 1089,
|
||
"a+": 1090
|
||
},
|
||
modeStringToFlags: str => {
|
||
var flags = FS.flagModes[str];
|
||
if (typeof flags == "undefined") {
|
||
throw new Error("Unknown file open mode: " + str);
|
||
}
|
||
return flags;
|
||
},
|
||
flagsToPermissionString: flag => {
|
||
var perms = ["r", "w", "rw"][flag & 3];
|
||
if (flag & 512) {
|
||
perms += "w";
|
||
}
|
||
return perms;
|
||
},
|
||
nodePermissions: (node, perms) => {
|
||
if (FS.ignorePermissions) {
|
||
return 0;
|
||
}
|
||
if (perms.includes("r") && !(node.mode & 292)) {
|
||
return 2;
|
||
} else if (perms.includes("w") && !(node.mode & 146)) {
|
||
return 2;
|
||
} else if (perms.includes("x") && !(node.mode & 73)) {
|
||
return 2;
|
||
}
|
||
return 0;
|
||
},
|
||
mayLookup: dir => {
|
||
var errCode = FS.nodePermissions(dir, "x");
|
||
if (errCode) return errCode;
|
||
if (!dir.node_ops.lookup) return 2;
|
||
return 0;
|
||
},
|
||
mayCreate: (dir, name) => {
|
||
try {
|
||
var node = FS.lookupNode(dir, name);
|
||
return 20;
|
||
} catch (e) {}
|
||
return FS.nodePermissions(dir, "wx");
|
||
},
|
||
mayDelete: (dir, name, isdir) => {
|
||
var node;
|
||
try {
|
||
node = FS.lookupNode(dir, name);
|
||
} catch (e) {
|
||
return e.errno;
|
||
}
|
||
var errCode = FS.nodePermissions(dir, "wx");
|
||
if (errCode) {
|
||
return errCode;
|
||
}
|
||
if (isdir) {
|
||
if (!FS.isDir(node.mode)) {
|
||
return 54;
|
||
}
|
||
if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) {
|
||
return 10;
|
||
}
|
||
} else {
|
||
if (FS.isDir(node.mode)) {
|
||
return 31;
|
||
}
|
||
}
|
||
return 0;
|
||
},
|
||
mayOpen: (node, flags) => {
|
||
if (!node) {
|
||
return 44;
|
||
}
|
||
if (FS.isLink(node.mode)) {
|
||
return 32;
|
||
} else if (FS.isDir(node.mode)) {
|
||
if (FS.flagsToPermissionString(flags) !== "r" || flags & 512) {
|
||
return 31;
|
||
}
|
||
}
|
||
return FS.nodePermissions(node, FS.flagsToPermissionString(flags));
|
||
},
|
||
MAX_OPEN_FDS: 4096,
|
||
nextfd: function () {
|
||
let fd_start = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||
let fd_end = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : FS.MAX_OPEN_FDS;
|
||
for (var fd = fd_start; fd <= fd_end; fd++) {
|
||
if (!FS.streams[fd]) {
|
||
return fd;
|
||
}
|
||
}
|
||
throw new FS.ErrnoError(33);
|
||
},
|
||
getStream: fd => FS.streams[fd],
|
||
createStream: (stream, fd_start, fd_end) => {
|
||
if (!FS.FSStream) {
|
||
FS.FSStream = function () {
|
||
this.shared = {};
|
||
};
|
||
FS.FSStream.prototype = {};
|
||
Object.defineProperties(FS.FSStream.prototype, {
|
||
object: {
|
||
get: function () {
|
||
return this.node;
|
||
},
|
||
set: function (val) {
|
||
this.node = val;
|
||
}
|
||
},
|
||
isRead: {
|
||
get: function () {
|
||
return (this.flags & 2097155) !== 1;
|
||
}
|
||
},
|
||
isWrite: {
|
||
get: function () {
|
||
return (this.flags & 2097155) !== 0;
|
||
}
|
||
},
|
||
isAppend: {
|
||
get: function () {
|
||
return this.flags & 1024;
|
||
}
|
||
},
|
||
flags: {
|
||
get: function () {
|
||
return this.shared.flags;
|
||
},
|
||
set: function (val) {
|
||
this.shared.flags = val;
|
||
}
|
||
},
|
||
position: {
|
||
get: function () {
|
||
return this.shared.position;
|
||
},
|
||
set: function (val) {
|
||
this.shared.position = val;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
stream = Object.assign(new FS.FSStream(), stream);
|
||
var fd = FS.nextfd(fd_start, fd_end);
|
||
stream.fd = fd;
|
||
FS.streams[fd] = stream;
|
||
return stream;
|
||
},
|
||
closeStream: fd => {
|
||
FS.streams[fd] = null;
|
||
},
|
||
chrdev_stream_ops: {
|
||
open: stream => {
|
||
var device = FS.getDevice(stream.node.rdev);
|
||
stream.stream_ops = device.stream_ops;
|
||
if (stream.stream_ops.open) {
|
||
stream.stream_ops.open(stream);
|
||
}
|
||
},
|
||
llseek: () => {
|
||
throw new FS.ErrnoError(70);
|
||
}
|
||
},
|
||
major: dev => dev >> 8,
|
||
minor: dev => dev & 255,
|
||
makedev: (ma, mi) => ma << 8 | mi,
|
||
registerDevice: (dev, ops) => {
|
||
FS.devices[dev] = {
|
||
stream_ops: ops
|
||
};
|
||
},
|
||
getDevice: dev => FS.devices[dev],
|
||
getMounts: mount => {
|
||
var mounts = [];
|
||
var check = [mount];
|
||
while (check.length) {
|
||
var m = check.pop();
|
||
mounts.push(m);
|
||
check.push.apply(check, m.mounts);
|
||
}
|
||
return mounts;
|
||
},
|
||
syncfs: (populate, callback) => {
|
||
if (typeof populate == "function") {
|
||
callback = populate;
|
||
populate = false;
|
||
}
|
||
FS.syncFSRequests++;
|
||
if (FS.syncFSRequests > 1) {
|
||
err("warning: " + FS.syncFSRequests + " FS.syncfs operations in flight at once, probably just doing extra work");
|
||
}
|
||
var mounts = FS.getMounts(FS.root.mount);
|
||
var completed = 0;
|
||
function doCallback(errCode) {
|
||
FS.syncFSRequests--;
|
||
return callback(errCode);
|
||
}
|
||
function done(errCode) {
|
||
if (errCode) {
|
||
if (!done.errored) {
|
||
done.errored = true;
|
||
return doCallback(errCode);
|
||
}
|
||
return;
|
||
}
|
||
if (++completed >= mounts.length) {
|
||
doCallback(null);
|
||
}
|
||
}
|
||
mounts.forEach(mount => {
|
||
if (!mount.type.syncfs) {
|
||
return done(null);
|
||
}
|
||
mount.type.syncfs(mount, populate, done);
|
||
});
|
||
},
|
||
mount: (type, opts, mountpoint) => {
|
||
var root = mountpoint === "/";
|
||
var pseudo = !mountpoint;
|
||
var node;
|
||
if (root && FS.root) {
|
||
throw new FS.ErrnoError(10);
|
||
} else if (!root && !pseudo) {
|
||
var lookup = FS.lookupPath(mountpoint, {
|
||
follow_mount: false
|
||
});
|
||
mountpoint = lookup.path;
|
||
node = lookup.node;
|
||
if (FS.isMountpoint(node)) {
|
||
throw new FS.ErrnoError(10);
|
||
}
|
||
if (!FS.isDir(node.mode)) {
|
||
throw new FS.ErrnoError(54);
|
||
}
|
||
}
|
||
var mount = {
|
||
type: type,
|
||
opts: opts,
|
||
mountpoint: mountpoint,
|
||
mounts: []
|
||
};
|
||
var mountRoot = type.mount(mount);
|
||
mountRoot.mount = mount;
|
||
mount.root = mountRoot;
|
||
if (root) {
|
||
FS.root = mountRoot;
|
||
} else if (node) {
|
||
node.mounted = mount;
|
||
if (node.mount) {
|
||
node.mount.mounts.push(mount);
|
||
}
|
||
}
|
||
return mountRoot;
|
||
},
|
||
unmount: mountpoint => {
|
||
var lookup = FS.lookupPath(mountpoint, {
|
||
follow_mount: false
|
||
});
|
||
if (!FS.isMountpoint(lookup.node)) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
var node = lookup.node;
|
||
var mount = node.mounted;
|
||
var mounts = FS.getMounts(mount);
|
||
Object.keys(FS.nameTable).forEach(hash => {
|
||
var current = FS.nameTable[hash];
|
||
while (current) {
|
||
var next = current.name_next;
|
||
if (mounts.includes(current.mount)) {
|
||
FS.destroyNode(current);
|
||
}
|
||
current = next;
|
||
}
|
||
});
|
||
node.mounted = null;
|
||
var idx = node.mount.mounts.indexOf(mount);
|
||
node.mount.mounts.splice(idx, 1);
|
||
},
|
||
lookup: (parent, name) => {
|
||
return parent.node_ops.lookup(parent, name);
|
||
},
|
||
mknod: (path, mode, dev) => {
|
||
var lookup = FS.lookupPath(path, {
|
||
parent: true
|
||
});
|
||
var parent = lookup.node;
|
||
var name = PATH.basename(path);
|
||
if (!name || name === "." || name === "..") {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
var errCode = FS.mayCreate(parent, name);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
if (!parent.node_ops.mknod) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
return parent.node_ops.mknod(parent, name, mode, dev);
|
||
},
|
||
create: (path, mode) => {
|
||
mode = mode !== undefined ? mode : 438;
|
||
mode &= 4095;
|
||
mode |= 32768;
|
||
return FS.mknod(path, mode, 0);
|
||
},
|
||
mkdir: (path, mode) => {
|
||
mode = mode !== undefined ? mode : 511;
|
||
mode &= 511 | 512;
|
||
mode |= 16384;
|
||
return FS.mknod(path, mode, 0);
|
||
},
|
||
mkdirTree: (path, mode) => {
|
||
var dirs = path.split("/");
|
||
var d = "";
|
||
for (var i = 0; i < dirs.length; ++i) {
|
||
if (!dirs[i]) continue;
|
||
d += "/" + dirs[i];
|
||
try {
|
||
FS.mkdir(d, mode);
|
||
} catch (e) {
|
||
if (e.errno != 20) throw e;
|
||
}
|
||
}
|
||
},
|
||
mkdev: (path, mode, dev) => {
|
||
if (typeof dev == "undefined") {
|
||
dev = mode;
|
||
mode = 438;
|
||
}
|
||
mode |= 8192;
|
||
return FS.mknod(path, mode, dev);
|
||
},
|
||
symlink: (oldpath, newpath) => {
|
||
if (!PATH_FS.resolve(oldpath)) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
var lookup = FS.lookupPath(newpath, {
|
||
parent: true
|
||
});
|
||
var parent = lookup.node;
|
||
if (!parent) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
var newname = PATH.basename(newpath);
|
||
var errCode = FS.mayCreate(parent, newname);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
if (!parent.node_ops.symlink) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
return parent.node_ops.symlink(parent, newname, oldpath);
|
||
},
|
||
rename: (old_path, new_path) => {
|
||
var old_dirname = PATH.dirname(old_path);
|
||
var new_dirname = PATH.dirname(new_path);
|
||
var old_name = PATH.basename(old_path);
|
||
var new_name = PATH.basename(new_path);
|
||
var lookup, old_dir, new_dir;
|
||
lookup = FS.lookupPath(old_path, {
|
||
parent: true
|
||
});
|
||
old_dir = lookup.node;
|
||
lookup = FS.lookupPath(new_path, {
|
||
parent: true
|
||
});
|
||
new_dir = lookup.node;
|
||
if (!old_dir || !new_dir) throw new FS.ErrnoError(44);
|
||
if (old_dir.mount !== new_dir.mount) {
|
||
throw new FS.ErrnoError(75);
|
||
}
|
||
var old_node = FS.lookupNode(old_dir, old_name);
|
||
var relative = PATH_FS.relative(old_path, new_dirname);
|
||
if (relative.charAt(0) !== ".") {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
relative = PATH_FS.relative(new_path, old_dirname);
|
||
if (relative.charAt(0) !== ".") {
|
||
throw new FS.ErrnoError(55);
|
||
}
|
||
var new_node;
|
||
try {
|
||
new_node = FS.lookupNode(new_dir, new_name);
|
||
} catch (e) {}
|
||
if (old_node === new_node) {
|
||
return;
|
||
}
|
||
var isdir = FS.isDir(old_node.mode);
|
||
var errCode = FS.mayDelete(old_dir, old_name, isdir);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
if (!old_dir.node_ops.rename) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) {
|
||
throw new FS.ErrnoError(10);
|
||
}
|
||
if (new_dir !== old_dir) {
|
||
errCode = FS.nodePermissions(old_dir, "w");
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
}
|
||
FS.hashRemoveNode(old_node);
|
||
try {
|
||
old_dir.node_ops.rename(old_node, new_dir, new_name);
|
||
} catch (e) {
|
||
throw e;
|
||
} finally {
|
||
FS.hashAddNode(old_node);
|
||
}
|
||
},
|
||
rmdir: path => {
|
||
var lookup = FS.lookupPath(path, {
|
||
parent: true
|
||
});
|
||
var parent = lookup.node;
|
||
var name = PATH.basename(path);
|
||
var node = FS.lookupNode(parent, name);
|
||
var errCode = FS.mayDelete(parent, name, true);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
if (!parent.node_ops.rmdir) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
if (FS.isMountpoint(node)) {
|
||
throw new FS.ErrnoError(10);
|
||
}
|
||
parent.node_ops.rmdir(parent, name);
|
||
FS.destroyNode(node);
|
||
},
|
||
readdir: path => {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: true
|
||
});
|
||
var node = lookup.node;
|
||
if (!node.node_ops.readdir) {
|
||
throw new FS.ErrnoError(54);
|
||
}
|
||
return node.node_ops.readdir(node);
|
||
},
|
||
unlink: path => {
|
||
var lookup = FS.lookupPath(path, {
|
||
parent: true
|
||
});
|
||
var parent = lookup.node;
|
||
if (!parent) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
var name = PATH.basename(path);
|
||
var node = FS.lookupNode(parent, name);
|
||
var errCode = FS.mayDelete(parent, name, false);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
if (!parent.node_ops.unlink) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
if (FS.isMountpoint(node)) {
|
||
throw new FS.ErrnoError(10);
|
||
}
|
||
parent.node_ops.unlink(parent, name);
|
||
FS.destroyNode(node);
|
||
},
|
||
readlink: path => {
|
||
var lookup = FS.lookupPath(path);
|
||
var link = lookup.node;
|
||
if (!link) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
if (!link.node_ops.readlink) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link));
|
||
},
|
||
stat: (path, dontFollow) => {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: !dontFollow
|
||
});
|
||
var node = lookup.node;
|
||
if (!node) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
if (!node.node_ops.getattr) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
return node.node_ops.getattr(node);
|
||
},
|
||
lstat: path => {
|
||
return FS.stat(path, true);
|
||
},
|
||
chmod: (path, mode, dontFollow) => {
|
||
var node;
|
||
if (typeof path == "string") {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: !dontFollow
|
||
});
|
||
node = lookup.node;
|
||
} else {
|
||
node = path;
|
||
}
|
||
if (!node.node_ops.setattr) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
node.node_ops.setattr(node, {
|
||
mode: mode & 4095 | node.mode & ~4095,
|
||
timestamp: Date.now()
|
||
});
|
||
},
|
||
lchmod: (path, mode) => {
|
||
FS.chmod(path, mode, true);
|
||
},
|
||
fchmod: (fd, mode) => {
|
||
var stream = FS.getStream(fd);
|
||
if (!stream) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
FS.chmod(stream.node, mode);
|
||
},
|
||
chown: (path, uid, gid, dontFollow) => {
|
||
var node;
|
||
if (typeof path == "string") {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: !dontFollow
|
||
});
|
||
node = lookup.node;
|
||
} else {
|
||
node = path;
|
||
}
|
||
if (!node.node_ops.setattr) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
node.node_ops.setattr(node, {
|
||
timestamp: Date.now()
|
||
});
|
||
},
|
||
lchown: (path, uid, gid) => {
|
||
FS.chown(path, uid, gid, true);
|
||
},
|
||
fchown: (fd, uid, gid) => {
|
||
var stream = FS.getStream(fd);
|
||
if (!stream) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
FS.chown(stream.node, uid, gid);
|
||
},
|
||
truncate: (path, len) => {
|
||
if (len < 0) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
var node;
|
||
if (typeof path == "string") {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: true
|
||
});
|
||
node = lookup.node;
|
||
} else {
|
||
node = path;
|
||
}
|
||
if (!node.node_ops.setattr) {
|
||
throw new FS.ErrnoError(63);
|
||
}
|
||
if (FS.isDir(node.mode)) {
|
||
throw new FS.ErrnoError(31);
|
||
}
|
||
if (!FS.isFile(node.mode)) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
var errCode = FS.nodePermissions(node, "w");
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
node.node_ops.setattr(node, {
|
||
size: len,
|
||
timestamp: Date.now()
|
||
});
|
||
},
|
||
ftruncate: (fd, len) => {
|
||
var stream = FS.getStream(fd);
|
||
if (!stream) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if ((stream.flags & 2097155) === 0) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
FS.truncate(stream.node, len);
|
||
},
|
||
utime: (path, atime, mtime) => {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: true
|
||
});
|
||
var node = lookup.node;
|
||
node.node_ops.setattr(node, {
|
||
timestamp: Math.max(atime, mtime)
|
||
});
|
||
},
|
||
open: (path, flags, mode) => {
|
||
if (path === "") {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
flags = typeof flags == "string" ? FS.modeStringToFlags(flags) : flags;
|
||
mode = typeof mode == "undefined" ? 438 : mode;
|
||
if (flags & 64) {
|
||
mode = mode & 4095 | 32768;
|
||
} else {
|
||
mode = 0;
|
||
}
|
||
var node;
|
||
if (typeof path == "object") {
|
||
node = path;
|
||
} else {
|
||
path = PATH.normalize(path);
|
||
try {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: !(flags & 131072)
|
||
});
|
||
node = lookup.node;
|
||
} catch (e) {}
|
||
}
|
||
var created = false;
|
||
if (flags & 64) {
|
||
if (node) {
|
||
if (flags & 128) {
|
||
throw new FS.ErrnoError(20);
|
||
}
|
||
} else {
|
||
node = FS.mknod(path, mode, 0);
|
||
created = true;
|
||
}
|
||
}
|
||
if (!node) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
if (FS.isChrdev(node.mode)) {
|
||
flags &= ~512;
|
||
}
|
||
if (flags & 65536 && !FS.isDir(node.mode)) {
|
||
throw new FS.ErrnoError(54);
|
||
}
|
||
if (!created) {
|
||
var errCode = FS.mayOpen(node, flags);
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
}
|
||
if (flags & 512 && !created) {
|
||
FS.truncate(node, 0);
|
||
}
|
||
flags &= ~(128 | 512 | 131072);
|
||
var stream = FS.createStream({
|
||
node: node,
|
||
path: FS.getPath(node),
|
||
flags: flags,
|
||
seekable: true,
|
||
position: 0,
|
||
stream_ops: node.stream_ops,
|
||
ungotten: [],
|
||
error: false
|
||
});
|
||
if (stream.stream_ops.open) {
|
||
stream.stream_ops.open(stream);
|
||
}
|
||
if (Module["logReadFiles"] && !(flags & 1)) {
|
||
if (!FS.readFiles) FS.readFiles = {};
|
||
if (!(path in FS.readFiles)) {
|
||
FS.readFiles[path] = 1;
|
||
}
|
||
}
|
||
return stream;
|
||
},
|
||
close: stream => {
|
||
if (FS.isClosed(stream)) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if (stream.getdents) stream.getdents = null;
|
||
try {
|
||
if (stream.stream_ops.close) {
|
||
stream.stream_ops.close(stream);
|
||
}
|
||
} catch (e) {
|
||
throw e;
|
||
} finally {
|
||
FS.closeStream(stream.fd);
|
||
}
|
||
stream.fd = null;
|
||
},
|
||
isClosed: stream => {
|
||
return stream.fd === null;
|
||
},
|
||
llseek: (stream, offset, whence) => {
|
||
if (FS.isClosed(stream)) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if (!stream.seekable || !stream.stream_ops.llseek) {
|
||
throw new FS.ErrnoError(70);
|
||
}
|
||
if (whence != 0 && whence != 1 && whence != 2) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
stream.position = stream.stream_ops.llseek(stream, offset, whence);
|
||
stream.ungotten = [];
|
||
return stream.position;
|
||
},
|
||
read: (stream, buffer, offset, length, position) => {
|
||
if (length < 0 || position < 0) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
if (FS.isClosed(stream)) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if ((stream.flags & 2097155) === 1) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if (FS.isDir(stream.node.mode)) {
|
||
throw new FS.ErrnoError(31);
|
||
}
|
||
if (!stream.stream_ops.read) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
var seeking = typeof position != "undefined";
|
||
if (!seeking) {
|
||
position = stream.position;
|
||
} else if (!stream.seekable) {
|
||
throw new FS.ErrnoError(70);
|
||
}
|
||
var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position);
|
||
if (!seeking) stream.position += bytesRead;
|
||
return bytesRead;
|
||
},
|
||
write: (stream, buffer, offset, length, position, canOwn) => {
|
||
if (length < 0 || position < 0) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
if (FS.isClosed(stream)) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if ((stream.flags & 2097155) === 0) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if (FS.isDir(stream.node.mode)) {
|
||
throw new FS.ErrnoError(31);
|
||
}
|
||
if (!stream.stream_ops.write) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
if (stream.seekable && stream.flags & 1024) {
|
||
FS.llseek(stream, 0, 2);
|
||
}
|
||
var seeking = typeof position != "undefined";
|
||
if (!seeking) {
|
||
position = stream.position;
|
||
} else if (!stream.seekable) {
|
||
throw new FS.ErrnoError(70);
|
||
}
|
||
var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
|
||
if (!seeking) stream.position += bytesWritten;
|
||
return bytesWritten;
|
||
},
|
||
allocate: (stream, offset, length) => {
|
||
if (FS.isClosed(stream)) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if (offset < 0 || length <= 0) {
|
||
throw new FS.ErrnoError(28);
|
||
}
|
||
if ((stream.flags & 2097155) === 0) {
|
||
throw new FS.ErrnoError(8);
|
||
}
|
||
if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) {
|
||
throw new FS.ErrnoError(43);
|
||
}
|
||
if (!stream.stream_ops.allocate) {
|
||
throw new FS.ErrnoError(138);
|
||
}
|
||
stream.stream_ops.allocate(stream, offset, length);
|
||
},
|
||
mmap: (stream, length, position, prot, flags) => {
|
||
if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) {
|
||
throw new FS.ErrnoError(2);
|
||
}
|
||
if ((stream.flags & 2097155) === 1) {
|
||
throw new FS.ErrnoError(2);
|
||
}
|
||
if (!stream.stream_ops.mmap) {
|
||
throw new FS.ErrnoError(43);
|
||
}
|
||
return stream.stream_ops.mmap(stream, length, position, prot, flags);
|
||
},
|
||
msync: (stream, buffer, offset, length, mmapFlags) => {
|
||
if (!stream || !stream.stream_ops.msync) {
|
||
return 0;
|
||
}
|
||
return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags);
|
||
},
|
||
munmap: stream => 0,
|
||
ioctl: (stream, cmd, arg) => {
|
||
if (!stream.stream_ops.ioctl) {
|
||
throw new FS.ErrnoError(59);
|
||
}
|
||
return stream.stream_ops.ioctl(stream, cmd, arg);
|
||
},
|
||
readFile: function (path) {
|
||
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||
opts.flags = opts.flags || 0;
|
||
opts.encoding = opts.encoding || "binary";
|
||
if (opts.encoding !== "utf8" && opts.encoding !== "binary") {
|
||
throw new Error('Invalid encoding type "' + opts.encoding + '"');
|
||
}
|
||
var ret;
|
||
var stream = FS.open(path, opts.flags);
|
||
var stat = FS.stat(path);
|
||
var length = stat.size;
|
||
var buf = new Uint8Array(length);
|
||
FS.read(stream, buf, 0, length, 0);
|
||
if (opts.encoding === "utf8") {
|
||
ret = UTF8ArrayToString(buf, 0);
|
||
} else if (opts.encoding === "binary") {
|
||
ret = buf;
|
||
}
|
||
FS.close(stream);
|
||
return ret;
|
||
},
|
||
writeFile: function (path, data) {
|
||
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||
opts.flags = opts.flags || 577;
|
||
var stream = FS.open(path, opts.flags, opts.mode);
|
||
if (typeof data == "string") {
|
||
var buf = new Uint8Array(lengthBytesUTF8(data) + 1);
|
||
var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length);
|
||
FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn);
|
||
} else if (ArrayBuffer.isView(data)) {
|
||
FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn);
|
||
} else {
|
||
throw new Error("Unsupported data type");
|
||
}
|
||
FS.close(stream);
|
||
},
|
||
cwd: () => FS.currentPath,
|
||
chdir: path => {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: true
|
||
});
|
||
if (lookup.node === null) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
if (!FS.isDir(lookup.node.mode)) {
|
||
throw new FS.ErrnoError(54);
|
||
}
|
||
var errCode = FS.nodePermissions(lookup.node, "x");
|
||
if (errCode) {
|
||
throw new FS.ErrnoError(errCode);
|
||
}
|
||
FS.currentPath = lookup.path;
|
||
},
|
||
createDefaultDirectories: () => {
|
||
FS.mkdir("/tmp");
|
||
FS.mkdir("/home");
|
||
FS.mkdir("/home/web_user");
|
||
},
|
||
createDefaultDevices: () => {
|
||
FS.mkdir("/dev");
|
||
FS.registerDevice(FS.makedev(1, 3), {
|
||
read: () => 0,
|
||
write: (stream, buffer, offset, length, pos) => length
|
||
});
|
||
FS.mkdev("/dev/null", FS.makedev(1, 3));
|
||
TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
|
||
TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
|
||
FS.mkdev("/dev/tty", FS.makedev(5, 0));
|
||
FS.mkdev("/dev/tty1", FS.makedev(6, 0));
|
||
var random_device = getRandomDevice();
|
||
FS.createDevice("/dev", "random", random_device);
|
||
FS.createDevice("/dev", "urandom", random_device);
|
||
FS.mkdir("/dev/shm");
|
||
FS.mkdir("/dev/shm/tmp");
|
||
},
|
||
createSpecialDirectories: () => {
|
||
FS.mkdir("/proc");
|
||
var proc_self = FS.mkdir("/proc/self");
|
||
FS.mkdir("/proc/self/fd");
|
||
FS.mount({
|
||
mount: () => {
|
||
var node = FS.createNode(proc_self, "fd", 16384 | 511, 73);
|
||
node.node_ops = {
|
||
lookup: (parent, name) => {
|
||
var fd = +name;
|
||
var stream = FS.getStream(fd);
|
||
if (!stream) throw new FS.ErrnoError(8);
|
||
var ret = {
|
||
parent: null,
|
||
mount: {
|
||
mountpoint: "fake"
|
||
},
|
||
node_ops: {
|
||
readlink: () => stream.path
|
||
}
|
||
};
|
||
ret.parent = ret;
|
||
return ret;
|
||
}
|
||
};
|
||
return node;
|
||
}
|
||
}, {}, "/proc/self/fd");
|
||
},
|
||
createStandardStreams: () => {
|
||
if (Module["stdin"]) {
|
||
FS.createDevice("/dev", "stdin", Module["stdin"]);
|
||
} else {
|
||
FS.symlink("/dev/tty", "/dev/stdin");
|
||
}
|
||
if (Module["stdout"]) {
|
||
FS.createDevice("/dev", "stdout", null, Module["stdout"]);
|
||
} else {
|
||
FS.symlink("/dev/tty", "/dev/stdout");
|
||
}
|
||
if (Module["stderr"]) {
|
||
FS.createDevice("/dev", "stderr", null, Module["stderr"]);
|
||
} else {
|
||
FS.symlink("/dev/tty1", "/dev/stderr");
|
||
}
|
||
FS.open("/dev/stdin", 0);
|
||
FS.open("/dev/stdout", 1);
|
||
FS.open("/dev/stderr", 1);
|
||
},
|
||
ensureErrnoError: () => {
|
||
if (FS.ErrnoError) return;
|
||
FS.ErrnoError = function ErrnoError(errno, node) {
|
||
this.node = node;
|
||
this.setErrno = function (errno) {
|
||
this.errno = errno;
|
||
};
|
||
this.setErrno(errno);
|
||
this.message = "FS error";
|
||
};
|
||
FS.ErrnoError.prototype = new Error();
|
||
FS.ErrnoError.prototype.constructor = FS.ErrnoError;
|
||
[44].forEach(code => {
|
||
FS.genericErrors[code] = new FS.ErrnoError(code);
|
||
FS.genericErrors[code].stack = "<generic error, no stack>";
|
||
});
|
||
},
|
||
staticInit: () => {
|
||
FS.ensureErrnoError();
|
||
FS.nameTable = new Array(4096);
|
||
FS.mount(MEMFS, {}, "/");
|
||
FS.createDefaultDirectories();
|
||
FS.createDefaultDevices();
|
||
FS.createSpecialDirectories();
|
||
FS.filesystems = {
|
||
"MEMFS": MEMFS
|
||
};
|
||
},
|
||
init: (input, output, error) => {
|
||
FS.init.initialized = true;
|
||
FS.ensureErrnoError();
|
||
Module["stdin"] = input || Module["stdin"];
|
||
Module["stdout"] = output || Module["stdout"];
|
||
Module["stderr"] = error || Module["stderr"];
|
||
FS.createStandardStreams();
|
||
},
|
||
quit: () => {
|
||
FS.init.initialized = false;
|
||
for (var i = 0; i < FS.streams.length; i++) {
|
||
var stream = FS.streams[i];
|
||
if (!stream) {
|
||
continue;
|
||
}
|
||
FS.close(stream);
|
||
}
|
||
},
|
||
getMode: (canRead, canWrite) => {
|
||
var mode = 0;
|
||
if (canRead) mode |= 292 | 73;
|
||
if (canWrite) mode |= 146;
|
||
return mode;
|
||
},
|
||
findObject: (path, dontResolveLastLink) => {
|
||
var ret = FS.analyzePath(path, dontResolveLastLink);
|
||
if (!ret.exists) {
|
||
return null;
|
||
}
|
||
return ret.object;
|
||
},
|
||
analyzePath: (path, dontResolveLastLink) => {
|
||
try {
|
||
var lookup = FS.lookupPath(path, {
|
||
follow: !dontResolveLastLink
|
||
});
|
||
path = lookup.path;
|
||
} catch (e) {}
|
||
var ret = {
|
||
isRoot: false,
|
||
exists: false,
|
||
error: 0,
|
||
name: null,
|
||
path: null,
|
||
object: null,
|
||
parentExists: false,
|
||
parentPath: null,
|
||
parentObject: null
|
||
};
|
||
try {
|
||
var lookup = FS.lookupPath(path, {
|
||
parent: true
|
||
});
|
||
ret.parentExists = true;
|
||
ret.parentPath = lookup.path;
|
||
ret.parentObject = lookup.node;
|
||
ret.name = PATH.basename(path);
|
||
lookup = FS.lookupPath(path, {
|
||
follow: !dontResolveLastLink
|
||
});
|
||
ret.exists = true;
|
||
ret.path = lookup.path;
|
||
ret.object = lookup.node;
|
||
ret.name = lookup.node.name;
|
||
ret.isRoot = lookup.path === "/";
|
||
} catch (e) {
|
||
ret.error = e.errno;
|
||
}
|
||
return ret;
|
||
},
|
||
createPath: (parent, path, canRead, canWrite) => {
|
||
parent = typeof parent == "string" ? parent : FS.getPath(parent);
|
||
var parts = path.split("/").reverse();
|
||
while (parts.length) {
|
||
var part = parts.pop();
|
||
if (!part) continue;
|
||
var current = PATH.join2(parent, part);
|
||
try {
|
||
FS.mkdir(current);
|
||
} catch (e) {}
|
||
parent = current;
|
||
}
|
||
return current;
|
||
},
|
||
createFile: (parent, name, properties, canRead, canWrite) => {
|
||
var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name);
|
||
var mode = FS.getMode(canRead, canWrite);
|
||
return FS.create(path, mode);
|
||
},
|
||
createDataFile: (parent, name, data, canRead, canWrite, canOwn) => {
|
||
var path = name;
|
||
if (parent) {
|
||
parent = typeof parent == "string" ? parent : FS.getPath(parent);
|
||
path = name ? PATH.join2(parent, name) : parent;
|
||
}
|
||
var mode = FS.getMode(canRead, canWrite);
|
||
var node = FS.create(path, mode);
|
||
if (data) {
|
||
if (typeof data == "string") {
|
||
var arr = new Array(data.length);
|
||
for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
|
||
data = arr;
|
||
}
|
||
FS.chmod(node, mode | 146);
|
||
var stream = FS.open(node, 577);
|
||
FS.write(stream, data, 0, data.length, 0, canOwn);
|
||
FS.close(stream);
|
||
FS.chmod(node, mode);
|
||
}
|
||
return node;
|
||
},
|
||
createDevice: (parent, name, input, output) => {
|
||
var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name);
|
||
var mode = FS.getMode(!!input, !!output);
|
||
if (!FS.createDevice.major) FS.createDevice.major = 64;
|
||
var dev = FS.makedev(FS.createDevice.major++, 0);
|
||
FS.registerDevice(dev, {
|
||
open: stream => {
|
||
stream.seekable = false;
|
||
},
|
||
close: stream => {
|
||
if (output && output.buffer && output.buffer.length) {
|
||
output(10);
|
||
}
|
||
},
|
||
read: (stream, buffer, offset, length, pos) => {
|
||
var bytesRead = 0;
|
||
for (var i = 0; i < length; i++) {
|
||
var result;
|
||
try {
|
||
result = input();
|
||
} catch (e) {
|
||
throw new FS.ErrnoError(29);
|
||
}
|
||
if (result === undefined && bytesRead === 0) {
|
||
throw new FS.ErrnoError(6);
|
||
}
|
||
if (result === null || result === undefined) break;
|
||
bytesRead++;
|
||
buffer[offset + i] = result;
|
||
}
|
||
if (bytesRead) {
|
||
stream.node.timestamp = Date.now();
|
||
}
|
||
return bytesRead;
|
||
},
|
||
write: (stream, buffer, offset, length, pos) => {
|
||
for (var i = 0; i < length; i++) {
|
||
try {
|
||
output(buffer[offset + i]);
|
||
} catch (e) {
|
||
throw new FS.ErrnoError(29);
|
||
}
|
||
}
|
||
if (length) {
|
||
stream.node.timestamp = Date.now();
|
||
}
|
||
return i;
|
||
}
|
||
});
|
||
return FS.mkdev(path, mode, dev);
|
||
},
|
||
forceLoadFile: obj => {
|
||
if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
|
||
if (typeof XMLHttpRequest != "undefined") {
|
||
throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
|
||
} else if (read_) {
|
||
try {
|
||
obj.contents = intArrayFromString(read_(obj.url), true);
|
||
obj.usedBytes = obj.contents.length;
|
||
} catch (e) {
|
||
throw new FS.ErrnoError(29);
|
||
}
|
||
} else {
|
||
throw new Error("Cannot load without read() or XMLHttpRequest.");
|
||
}
|
||
},
|
||
createLazyFile: (parent, name, url, canRead, canWrite) => {
|
||
function LazyUint8Array() {
|
||
this.lengthKnown = false;
|
||
this.chunks = [];
|
||
}
|
||
LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) {
|
||
if (idx > this.length - 1 || idx < 0) {
|
||
return undefined;
|
||
}
|
||
var chunkOffset = idx % this.chunkSize;
|
||
var chunkNum = idx / this.chunkSize | 0;
|
||
return this.getter(chunkNum)[chunkOffset];
|
||
};
|
||
LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) {
|
||
this.getter = getter;
|
||
};
|
||
LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open("HEAD", url, false);
|
||
xhr.send(null);
|
||
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
|
||
var datalength = Number(xhr.getResponseHeader("Content-length"));
|
||
var header;
|
||
var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
|
||
var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip";
|
||
var chunkSize = 1024 * 1024;
|
||
if (!hasByteServing) chunkSize = datalength;
|
||
var doXHR = (from, to) => {
|
||
if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
|
||
if (to > datalength - 1) throw new Error("only " + datalength + " bytes available! programmer error!");
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open("GET", url, false);
|
||
if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
|
||
xhr.responseType = "arraybuffer";
|
||
if (xhr.overrideMimeType) {
|
||
xhr.overrideMimeType("text/plain; charset=x-user-defined");
|
||
}
|
||
xhr.send(null);
|
||
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
|
||
if (xhr.response !== undefined) {
|
||
return new Uint8Array(xhr.response || []);
|
||
}
|
||
return intArrayFromString(xhr.responseText || "", true);
|
||
};
|
||
var lazyArray = this;
|
||
lazyArray.setDataGetter(chunkNum => {
|
||
var start = chunkNum * chunkSize;
|
||
var end = (chunkNum + 1) * chunkSize - 1;
|
||
end = Math.min(end, datalength - 1);
|
||
if (typeof lazyArray.chunks[chunkNum] == "undefined") {
|
||
lazyArray.chunks[chunkNum] = doXHR(start, end);
|
||
}
|
||
if (typeof lazyArray.chunks[chunkNum] == "undefined") throw new Error("doXHR failed!");
|
||
return lazyArray.chunks[chunkNum];
|
||
});
|
||
if (usesGzip || !datalength) {
|
||
chunkSize = datalength = 1;
|
||
datalength = this.getter(0).length;
|
||
chunkSize = datalength;
|
||
out("LazyFiles on gzip forces download of the whole file when length is accessed");
|
||
}
|
||
this._length = datalength;
|
||
this._chunkSize = chunkSize;
|
||
this.lengthKnown = true;
|
||
};
|
||
if (typeof XMLHttpRequest != "undefined") {
|
||
if (!ENVIRONMENT_IS_WORKER) throw "Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";
|
||
var lazyArray = new LazyUint8Array();
|
||
Object.defineProperties(lazyArray, {
|
||
length: {
|
||
get: function () {
|
||
if (!this.lengthKnown) {
|
||
this.cacheLength();
|
||
}
|
||
return this._length;
|
||
}
|
||
},
|
||
chunkSize: {
|
||
get: function () {
|
||
if (!this.lengthKnown) {
|
||
this.cacheLength();
|
||
}
|
||
return this._chunkSize;
|
||
}
|
||
}
|
||
});
|
||
var properties = {
|
||
isDevice: false,
|
||
contents: lazyArray
|
||
};
|
||
} else {
|
||
var properties = {
|
||
isDevice: false,
|
||
url: url
|
||
};
|
||
}
|
||
var node = FS.createFile(parent, name, properties, canRead, canWrite);
|
||
if (properties.contents) {
|
||
node.contents = properties.contents;
|
||
} else if (properties.url) {
|
||
node.contents = null;
|
||
node.url = properties.url;
|
||
}
|
||
Object.defineProperties(node, {
|
||
usedBytes: {
|
||
get: function () {
|
||
return this.contents.length;
|
||
}
|
||
}
|
||
});
|
||
var stream_ops = {};
|
||
var keys = Object.keys(node.stream_ops);
|
||
keys.forEach(key => {
|
||
var fn = node.stream_ops[key];
|
||
stream_ops[key] = function forceLoadLazyFile() {
|
||
FS.forceLoadFile(node);
|
||
return fn.apply(null, arguments);
|
||
};
|
||
});
|
||
function writeChunks(stream, buffer, offset, length, position) {
|
||
var contents = stream.node.contents;
|
||
if (position >= contents.length) return 0;
|
||
var size = Math.min(contents.length - position, length);
|
||
if (contents.slice) {
|
||
for (var i = 0; i < size; i++) {
|
||
buffer[offset + i] = contents[position + i];
|
||
}
|
||
} else {
|
||
for (var i = 0; i < size; i++) {
|
||
buffer[offset + i] = contents.get(position + i);
|
||
}
|
||
}
|
||
return size;
|
||
}
|
||
stream_ops.read = (stream, buffer, offset, length, position) => {
|
||
FS.forceLoadFile(node);
|
||
return writeChunks(stream, buffer, offset, length, position);
|
||
};
|
||
stream_ops.mmap = (stream, length, position, prot, flags) => {
|
||
FS.forceLoadFile(node);
|
||
var ptr = mmapAlloc(length);
|
||
if (!ptr) {
|
||
throw new FS.ErrnoError(48);
|
||
}
|
||
writeChunks(stream, HEAP8, ptr, length, position);
|
||
return {
|
||
ptr: ptr,
|
||
allocated: true
|
||
};
|
||
};
|
||
node.stream_ops = stream_ops;
|
||
return node;
|
||
},
|
||
createPreloadedFile: (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => {
|
||
var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent;
|
||
function processData(byteArray) {
|
||
function finish(byteArray) {
|
||
if (preFinish) preFinish();
|
||
if (!dontCreateFile) {
|
||
FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
|
||
}
|
||
if (onload) onload();
|
||
removeRunDependency();
|
||
}
|
||
if (Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => {
|
||
if (onerror) onerror();
|
||
removeRunDependency();
|
||
})) {
|
||
return;
|
||
}
|
||
finish(byteArray);
|
||
}
|
||
addRunDependency();
|
||
if (typeof url == "string") {
|
||
asyncLoad(url, byteArray => processData(byteArray), onerror);
|
||
} else {
|
||
processData(url);
|
||
}
|
||
},
|
||
indexedDB: () => {
|
||
return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||
},
|
||
DB_NAME: () => {
|
||
return "EM_FS_" + window.location.pathname;
|
||
},
|
||
DB_VERSION: 20,
|
||
DB_STORE_NAME: "FILE_DATA",
|
||
saveFilesToDB: (paths, onload, onerror) => {
|
||
onload = onload || (() => {});
|
||
onerror = onerror || (() => {});
|
||
var indexedDB = FS.indexedDB();
|
||
try {
|
||
var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
|
||
} catch (e) {
|
||
return onerror(e);
|
||
}
|
||
openRequest.onupgradeneeded = () => {
|
||
out("creating db");
|
||
var db = openRequest.result;
|
||
db.createObjectStore(FS.DB_STORE_NAME);
|
||
};
|
||
openRequest.onsuccess = () => {
|
||
var db = openRequest.result;
|
||
var transaction = db.transaction([FS.DB_STORE_NAME], "readwrite");
|
||
var files = transaction.objectStore(FS.DB_STORE_NAME);
|
||
var ok = 0,
|
||
fail = 0,
|
||
total = paths.length;
|
||
function finish() {
|
||
if (fail == 0) onload();else onerror();
|
||
}
|
||
paths.forEach(path => {
|
||
var putRequest = files.put(FS.analyzePath(path).object.contents, path);
|
||
putRequest.onsuccess = () => {
|
||
ok++;
|
||
if (ok + fail == total) finish();
|
||
};
|
||
putRequest.onerror = () => {
|
||
fail++;
|
||
if (ok + fail == total) finish();
|
||
};
|
||
});
|
||
transaction.onerror = onerror;
|
||
};
|
||
openRequest.onerror = onerror;
|
||
},
|
||
loadFilesFromDB: (paths, onload, onerror) => {
|
||
onload = onload || (() => {});
|
||
onerror = onerror || (() => {});
|
||
var indexedDB = FS.indexedDB();
|
||
try {
|
||
var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
|
||
} catch (e) {
|
||
return onerror(e);
|
||
}
|
||
openRequest.onupgradeneeded = onerror;
|
||
openRequest.onsuccess = () => {
|
||
var db = openRequest.result;
|
||
try {
|
||
var transaction = db.transaction([FS.DB_STORE_NAME], "readonly");
|
||
} catch (e) {
|
||
onerror(e);
|
||
return;
|
||
}
|
||
var files = transaction.objectStore(FS.DB_STORE_NAME);
|
||
var ok = 0,
|
||
fail = 0,
|
||
total = paths.length;
|
||
function finish() {
|
||
if (fail == 0) onload();else onerror();
|
||
}
|
||
paths.forEach(path => {
|
||
var getRequest = files.get(path);
|
||
getRequest.onsuccess = () => {
|
||
if (FS.analyzePath(path).exists) {
|
||
FS.unlink(path);
|
||
}
|
||
FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
|
||
ok++;
|
||
if (ok + fail == total) finish();
|
||
};
|
||
getRequest.onerror = () => {
|
||
fail++;
|
||
if (ok + fail == total) finish();
|
||
};
|
||
});
|
||
transaction.onerror = onerror;
|
||
};
|
||
openRequest.onerror = onerror;
|
||
}
|
||
};
|
||
var SYSCALLS = {
|
||
DEFAULT_POLLMASK: 5,
|
||
calculateAt: function (dirfd, path, allowEmpty) {
|
||
if (PATH.isAbs(path)) {
|
||
return path;
|
||
}
|
||
var dir;
|
||
if (dirfd === -100) {
|
||
dir = FS.cwd();
|
||
} else {
|
||
var dirstream = FS.getStream(dirfd);
|
||
if (!dirstream) throw new FS.ErrnoError(8);
|
||
dir = dirstream.path;
|
||
}
|
||
if (path.length == 0) {
|
||
if (!allowEmpty) {
|
||
throw new FS.ErrnoError(44);
|
||
}
|
||
return dir;
|
||
}
|
||
return PATH.join2(dir, path);
|
||
},
|
||
doStat: function (func, path, buf) {
|
||
try {
|
||
var stat = func(path);
|
||
} catch (e) {
|
||
if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) {
|
||
return -54;
|
||
}
|
||
throw e;
|
||
}
|
||
HEAP32[buf >> 2] = stat.dev;
|
||
HEAP32[buf + 4 >> 2] = 0;
|
||
HEAP32[buf + 8 >> 2] = stat.ino;
|
||
HEAP32[buf + 12 >> 2] = stat.mode;
|
||
HEAP32[buf + 16 >> 2] = stat.nlink;
|
||
HEAP32[buf + 20 >> 2] = stat.uid;
|
||
HEAP32[buf + 24 >> 2] = stat.gid;
|
||
HEAP32[buf + 28 >> 2] = stat.rdev;
|
||
HEAP32[buf + 32 >> 2] = 0;
|
||
tempI64 = [stat.size >>> 0, (tempDouble = stat.size, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)], HEAP32[buf + 40 >> 2] = tempI64[0], HEAP32[buf + 44 >> 2] = tempI64[1];
|
||
HEAP32[buf + 48 >> 2] = 4096;
|
||
HEAP32[buf + 52 >> 2] = stat.blocks;
|
||
tempI64 = [Math.floor(stat.atime.getTime() / 1e3) >>> 0, (tempDouble = Math.floor(stat.atime.getTime() / 1e3), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)], HEAP32[buf + 56 >> 2] = tempI64[0], HEAP32[buf + 60 >> 2] = tempI64[1];
|
||
HEAP32[buf + 64 >> 2] = 0;
|
||
tempI64 = [Math.floor(stat.mtime.getTime() / 1e3) >>> 0, (tempDouble = Math.floor(stat.mtime.getTime() / 1e3), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)], HEAP32[buf + 72 >> 2] = tempI64[0], HEAP32[buf + 76 >> 2] = tempI64[1];
|
||
HEAP32[buf + 80 >> 2] = 0;
|
||
tempI64 = [Math.floor(stat.ctime.getTime() / 1e3) >>> 0, (tempDouble = Math.floor(stat.ctime.getTime() / 1e3), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)], HEAP32[buf + 88 >> 2] = tempI64[0], HEAP32[buf + 92 >> 2] = tempI64[1];
|
||
HEAP32[buf + 96 >> 2] = 0;
|
||
tempI64 = [stat.ino >>> 0, (tempDouble = stat.ino, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)], HEAP32[buf + 104 >> 2] = tempI64[0], HEAP32[buf + 108 >> 2] = tempI64[1];
|
||
return 0;
|
||
},
|
||
doMsync: function (addr, stream, len, flags, offset) {
|
||
var buffer = HEAPU8.slice(addr, addr + len);
|
||
FS.msync(stream, buffer, offset, len, flags);
|
||
},
|
||
varargs: undefined,
|
||
get: function () {
|
||
SYSCALLS.varargs += 4;
|
||
var ret = HEAP32[SYSCALLS.varargs - 4 >> 2];
|
||
return ret;
|
||
},
|
||
getStr: function (ptr) {
|
||
var ret = UTF8ToString(ptr);
|
||
return ret;
|
||
},
|
||
getStreamFromFD: function (fd) {
|
||
var stream = FS.getStream(fd);
|
||
if (!stream) throw new FS.ErrnoError(8);
|
||
return stream;
|
||
}
|
||
};
|
||
function ___syscall_fcntl64(fd, cmd, varargs) {
|
||
SYSCALLS.varargs = varargs;
|
||
try {
|
||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||
switch (cmd) {
|
||
case 0:
|
||
{
|
||
var arg = SYSCALLS.get();
|
||
if (arg < 0) {
|
||
return -28;
|
||
}
|
||
var newStream;
|
||
newStream = FS.createStream(stream, arg);
|
||
return newStream.fd;
|
||
}
|
||
case 1:
|
||
case 2:
|
||
return 0;
|
||
case 3:
|
||
return stream.flags;
|
||
case 4:
|
||
{
|
||
var arg = SYSCALLS.get();
|
||
stream.flags |= arg;
|
||
return 0;
|
||
}
|
||
case 5:
|
||
{
|
||
var arg = SYSCALLS.get();
|
||
var offset = 0;
|
||
HEAP16[arg + offset >> 1] = 2;
|
||
return 0;
|
||
}
|
||
case 6:
|
||
case 7:
|
||
return 0;
|
||
case 16:
|
||
case 8:
|
||
return -28;
|
||
case 9:
|
||
setErrNo(28);
|
||
return -1;
|
||
default:
|
||
{
|
||
return -28;
|
||
}
|
||
}
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return -e.errno;
|
||
}
|
||
}
|
||
function ___syscall_openat(dirfd, path, flags, varargs) {
|
||
SYSCALLS.varargs = varargs;
|
||
try {
|
||
path = SYSCALLS.getStr(path);
|
||
path = SYSCALLS.calculateAt(dirfd, path);
|
||
var mode = varargs ? SYSCALLS.get() : 0;
|
||
return FS.open(path, flags, mode).fd;
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return -e.errno;
|
||
}
|
||
}
|
||
function __embind_register_bigint(primitiveType, name, size, minRange, maxRange) {}
|
||
function getShiftFromSize(size) {
|
||
switch (size) {
|
||
case 1:
|
||
return 0;
|
||
case 2:
|
||
return 1;
|
||
case 4:
|
||
return 2;
|
||
case 8:
|
||
return 3;
|
||
default:
|
||
throw new TypeError("Unknown type size: " + size);
|
||
}
|
||
}
|
||
function embind_init_charCodes() {
|
||
var codes = new Array(256);
|
||
for (var i = 0; i < 256; ++i) {
|
||
codes[i] = String.fromCharCode(i);
|
||
}
|
||
embind_charCodes = codes;
|
||
}
|
||
var embind_charCodes = undefined;
|
||
function readLatin1String(ptr) {
|
||
var ret = "";
|
||
var c = ptr;
|
||
while (HEAPU8[c]) {
|
||
ret += embind_charCodes[HEAPU8[c++]];
|
||
}
|
||
return ret;
|
||
}
|
||
var awaitingDependencies = {};
|
||
var registeredTypes = {};
|
||
var typeDependencies = {};
|
||
var char_0 = 48;
|
||
var char_9 = 57;
|
||
function makeLegalFunctionName(name) {
|
||
if (undefined === name) {
|
||
return "_unknown";
|
||
}
|
||
name = name.replace(/[^a-zA-Z0-9_]/g, "$");
|
||
var f = name.charCodeAt(0);
|
||
if (f >= char_0 && f <= char_9) {
|
||
return "_" + name;
|
||
}
|
||
return name;
|
||
}
|
||
function createNamedFunction(name, body) {
|
||
name = makeLegalFunctionName(name);
|
||
return new Function("body", "return function " + name + "() {\n" + ' "use strict";' + " return body.apply(this, arguments);\n" + "};\n")(body);
|
||
}
|
||
function extendError(baseErrorType, errorName) {
|
||
var errorClass = createNamedFunction(errorName, function (message) {
|
||
this.name = errorName;
|
||
this.message = message;
|
||
var stack = new Error(message).stack;
|
||
if (stack !== undefined) {
|
||
this.stack = this.toString() + "\n" + stack.replace(/^Error(:[^\n]*)?\n/, "");
|
||
}
|
||
});
|
||
errorClass.prototype = Object.create(baseErrorType.prototype);
|
||
errorClass.prototype.constructor = errorClass;
|
||
errorClass.prototype.toString = function () {
|
||
if (this.message === undefined) {
|
||
return this.name;
|
||
} else {
|
||
return this.name + ": " + this.message;
|
||
}
|
||
};
|
||
return errorClass;
|
||
}
|
||
var BindingError = undefined;
|
||
function throwBindingError(message) {
|
||
throw new BindingError(message);
|
||
}
|
||
var InternalError = undefined;
|
||
function throwInternalError(message) {
|
||
throw new InternalError(message);
|
||
}
|
||
function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverters) {
|
||
myTypes.forEach(function (type) {
|
||
typeDependencies[type] = dependentTypes;
|
||
});
|
||
function onComplete(typeConverters) {
|
||
var myTypeConverters = getTypeConverters(typeConverters);
|
||
if (myTypeConverters.length !== myTypes.length) {
|
||
throwInternalError("Mismatched type converter count");
|
||
}
|
||
for (var i = 0; i < myTypes.length; ++i) {
|
||
registerType(myTypes[i], myTypeConverters[i]);
|
||
}
|
||
}
|
||
var typeConverters = new Array(dependentTypes.length);
|
||
var unregisteredTypes = [];
|
||
var registered = 0;
|
||
dependentTypes.forEach((dt, i) => {
|
||
if (registeredTypes.hasOwnProperty(dt)) {
|
||
typeConverters[i] = registeredTypes[dt];
|
||
} else {
|
||
unregisteredTypes.push(dt);
|
||
if (!awaitingDependencies.hasOwnProperty(dt)) {
|
||
awaitingDependencies[dt] = [];
|
||
}
|
||
awaitingDependencies[dt].push(() => {
|
||
typeConverters[i] = registeredTypes[dt];
|
||
++registered;
|
||
if (registered === unregisteredTypes.length) {
|
||
onComplete(typeConverters);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
if (0 === unregisteredTypes.length) {
|
||
onComplete(typeConverters);
|
||
}
|
||
}
|
||
function registerType(rawType, registeredInstance) {
|
||
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||
if (!("argPackAdvance" in registeredInstance)) {
|
||
throw new TypeError("registerType registeredInstance requires argPackAdvance");
|
||
}
|
||
var name = registeredInstance.name;
|
||
if (!rawType) {
|
||
throwBindingError('type "' + name + '" must have a positive integer typeid pointer');
|
||
}
|
||
if (registeredTypes.hasOwnProperty(rawType)) {
|
||
if (options.ignoreDuplicateRegistrations) {
|
||
return;
|
||
} else {
|
||
throwBindingError("Cannot register type '" + name + "' twice");
|
||
}
|
||
}
|
||
registeredTypes[rawType] = registeredInstance;
|
||
delete typeDependencies[rawType];
|
||
if (awaitingDependencies.hasOwnProperty(rawType)) {
|
||
var callbacks = awaitingDependencies[rawType];
|
||
delete awaitingDependencies[rawType];
|
||
callbacks.forEach(cb => cb());
|
||
}
|
||
}
|
||
function __embind_register_bool(rawType, name, size, trueValue, falseValue) {
|
||
var shift = getShiftFromSize(size);
|
||
name = readLatin1String(name);
|
||
registerType(rawType, {
|
||
name: name,
|
||
"fromWireType": function (wt) {
|
||
return !!wt;
|
||
},
|
||
"toWireType": function (destructors, o) {
|
||
return o ? trueValue : falseValue;
|
||
},
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": function (pointer) {
|
||
var heap;
|
||
if (size === 1) {
|
||
heap = HEAP8;
|
||
} else if (size === 2) {
|
||
heap = HEAP16;
|
||
} else if (size === 4) {
|
||
heap = HEAP32;
|
||
} else {
|
||
throw new TypeError("Unknown boolean type size: " + name);
|
||
}
|
||
return this["fromWireType"](heap[pointer >> shift]);
|
||
},
|
||
destructorFunction: null
|
||
});
|
||
}
|
||
function ClassHandle_isAliasOf(other) {
|
||
if (!(this instanceof ClassHandle)) {
|
||
return false;
|
||
}
|
||
if (!(other instanceof ClassHandle)) {
|
||
return false;
|
||
}
|
||
var leftClass = this.$$.ptrType.registeredClass;
|
||
var left = this.$$.ptr;
|
||
var rightClass = other.$$.ptrType.registeredClass;
|
||
var right = other.$$.ptr;
|
||
while (leftClass.baseClass) {
|
||
left = leftClass.upcast(left);
|
||
leftClass = leftClass.baseClass;
|
||
}
|
||
while (rightClass.baseClass) {
|
||
right = rightClass.upcast(right);
|
||
rightClass = rightClass.baseClass;
|
||
}
|
||
return leftClass === rightClass && left === right;
|
||
}
|
||
function shallowCopyInternalPointer(o) {
|
||
return {
|
||
count: o.count,
|
||
deleteScheduled: o.deleteScheduled,
|
||
preservePointerOnDelete: o.preservePointerOnDelete,
|
||
ptr: o.ptr,
|
||
ptrType: o.ptrType,
|
||
smartPtr: o.smartPtr,
|
||
smartPtrType: o.smartPtrType
|
||
};
|
||
}
|
||
function throwInstanceAlreadyDeleted(obj) {
|
||
function getInstanceTypeName(handle) {
|
||
return handle.$$.ptrType.registeredClass.name;
|
||
}
|
||
throwBindingError(getInstanceTypeName(obj) + " instance already deleted");
|
||
}
|
||
var finalizationRegistry = false;
|
||
function detachFinalizer(handle) {}
|
||
function runDestructor($$) {
|
||
if ($$.smartPtr) {
|
||
$$.smartPtrType.rawDestructor($$.smartPtr);
|
||
} else {
|
||
$$.ptrType.registeredClass.rawDestructor($$.ptr);
|
||
}
|
||
}
|
||
function releaseClassHandle($$) {
|
||
$$.count.value -= 1;
|
||
var toDelete = 0 === $$.count.value;
|
||
if (toDelete) {
|
||
runDestructor($$);
|
||
}
|
||
}
|
||
function downcastPointer(ptr, ptrClass, desiredClass) {
|
||
if (ptrClass === desiredClass) {
|
||
return ptr;
|
||
}
|
||
if (undefined === desiredClass.baseClass) {
|
||
return null;
|
||
}
|
||
var rv = downcastPointer(ptr, ptrClass, desiredClass.baseClass);
|
||
if (rv === null) {
|
||
return null;
|
||
}
|
||
return desiredClass.downcast(rv);
|
||
}
|
||
var registeredPointers = {};
|
||
function getInheritedInstanceCount() {
|
||
return Object.keys(registeredInstances).length;
|
||
}
|
||
function getLiveInheritedInstances() {
|
||
var rv = [];
|
||
for (var k in registeredInstances) {
|
||
if (registeredInstances.hasOwnProperty(k)) {
|
||
rv.push(registeredInstances[k]);
|
||
}
|
||
}
|
||
return rv;
|
||
}
|
||
var deletionQueue = [];
|
||
function flushPendingDeletes() {
|
||
while (deletionQueue.length) {
|
||
var obj = deletionQueue.pop();
|
||
obj.$$.deleteScheduled = false;
|
||
obj["delete"]();
|
||
}
|
||
}
|
||
var delayFunction = undefined;
|
||
function setDelayFunction(fn) {
|
||
delayFunction = fn;
|
||
if (deletionQueue.length && delayFunction) {
|
||
delayFunction(flushPendingDeletes);
|
||
}
|
||
}
|
||
function init_embind() {
|
||
Module["getInheritedInstanceCount"] = getInheritedInstanceCount;
|
||
Module["getLiveInheritedInstances"] = getLiveInheritedInstances;
|
||
Module["flushPendingDeletes"] = flushPendingDeletes;
|
||
Module["setDelayFunction"] = setDelayFunction;
|
||
}
|
||
var registeredInstances = {};
|
||
function getBasestPointer(class_, ptr) {
|
||
if (ptr === undefined) {
|
||
throwBindingError("ptr should not be undefined");
|
||
}
|
||
while (class_.baseClass) {
|
||
ptr = class_.upcast(ptr);
|
||
class_ = class_.baseClass;
|
||
}
|
||
return ptr;
|
||
}
|
||
function getInheritedInstance(class_, ptr) {
|
||
ptr = getBasestPointer(class_, ptr);
|
||
return registeredInstances[ptr];
|
||
}
|
||
function makeClassHandle(prototype, record) {
|
||
if (!record.ptrType || !record.ptr) {
|
||
throwInternalError("makeClassHandle requires ptr and ptrType");
|
||
}
|
||
var hasSmartPtrType = !!record.smartPtrType;
|
||
var hasSmartPtr = !!record.smartPtr;
|
||
if (hasSmartPtrType !== hasSmartPtr) {
|
||
throwInternalError("Both smartPtrType and smartPtr must be specified");
|
||
}
|
||
record.count = {
|
||
value: 1
|
||
};
|
||
return attachFinalizer(Object.create(prototype, {
|
||
$$: {
|
||
value: record
|
||
}
|
||
}));
|
||
}
|
||
function RegisteredPointer_fromWireType(ptr) {
|
||
var rawPointer = this.getPointee(ptr);
|
||
if (!rawPointer) {
|
||
this.destructor(ptr);
|
||
return null;
|
||
}
|
||
var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer);
|
||
if (undefined !== registeredInstance) {
|
||
if (0 === registeredInstance.$$.count.value) {
|
||
registeredInstance.$$.ptr = rawPointer;
|
||
registeredInstance.$$.smartPtr = ptr;
|
||
return registeredInstance["clone"]();
|
||
} else {
|
||
var rv = registeredInstance["clone"]();
|
||
this.destructor(ptr);
|
||
return rv;
|
||
}
|
||
}
|
||
function makeDefaultHandle() {
|
||
if (this.isSmartPointer) {
|
||
return makeClassHandle(this.registeredClass.instancePrototype, {
|
||
ptrType: this.pointeeType,
|
||
ptr: rawPointer,
|
||
smartPtrType: this,
|
||
smartPtr: ptr
|
||
});
|
||
} else {
|
||
return makeClassHandle(this.registeredClass.instancePrototype, {
|
||
ptrType: this,
|
||
ptr: ptr
|
||
});
|
||
}
|
||
}
|
||
var actualType = this.registeredClass.getActualType(rawPointer);
|
||
var registeredPointerRecord = registeredPointers[actualType];
|
||
if (!registeredPointerRecord) {
|
||
return makeDefaultHandle.call(this);
|
||
}
|
||
var toType;
|
||
if (this.isConst) {
|
||
toType = registeredPointerRecord.constPointerType;
|
||
} else {
|
||
toType = registeredPointerRecord.pointerType;
|
||
}
|
||
var dp = downcastPointer(rawPointer, this.registeredClass, toType.registeredClass);
|
||
if (dp === null) {
|
||
return makeDefaultHandle.call(this);
|
||
}
|
||
if (this.isSmartPointer) {
|
||
return makeClassHandle(toType.registeredClass.instancePrototype, {
|
||
ptrType: toType,
|
||
ptr: dp,
|
||
smartPtrType: this,
|
||
smartPtr: ptr
|
||
});
|
||
} else {
|
||
return makeClassHandle(toType.registeredClass.instancePrototype, {
|
||
ptrType: toType,
|
||
ptr: dp
|
||
});
|
||
}
|
||
}
|
||
function attachFinalizer(handle) {
|
||
if ("undefined" === typeof FinalizationRegistry) {
|
||
attachFinalizer = handle => handle;
|
||
return handle;
|
||
}
|
||
finalizationRegistry = new FinalizationRegistry(info => {
|
||
releaseClassHandle(info.$$);
|
||
});
|
||
attachFinalizer = handle => {
|
||
var $$ = handle.$$;
|
||
var hasSmartPtr = !!$$.smartPtr;
|
||
if (hasSmartPtr) {
|
||
var info = {
|
||
$$: $$
|
||
};
|
||
finalizationRegistry.register(handle, info, handle);
|
||
}
|
||
return handle;
|
||
};
|
||
detachFinalizer = handle => finalizationRegistry.unregister(handle);
|
||
return attachFinalizer(handle);
|
||
}
|
||
function ClassHandle_clone() {
|
||
if (!this.$$.ptr) {
|
||
throwInstanceAlreadyDeleted(this);
|
||
}
|
||
if (this.$$.preservePointerOnDelete) {
|
||
this.$$.count.value += 1;
|
||
return this;
|
||
} else {
|
||
var clone = attachFinalizer(Object.create(Object.getPrototypeOf(this), {
|
||
$$: {
|
||
value: shallowCopyInternalPointer(this.$$)
|
||
}
|
||
}));
|
||
clone.$$.count.value += 1;
|
||
clone.$$.deleteScheduled = false;
|
||
return clone;
|
||
}
|
||
}
|
||
function ClassHandle_delete() {
|
||
if (!this.$$.ptr) {
|
||
throwInstanceAlreadyDeleted(this);
|
||
}
|
||
if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) {
|
||
throwBindingError("Object already scheduled for deletion");
|
||
}
|
||
detachFinalizer(this);
|
||
releaseClassHandle(this.$$);
|
||
if (!this.$$.preservePointerOnDelete) {
|
||
this.$$.smartPtr = undefined;
|
||
this.$$.ptr = undefined;
|
||
}
|
||
}
|
||
function ClassHandle_isDeleted() {
|
||
return !this.$$.ptr;
|
||
}
|
||
function ClassHandle_deleteLater() {
|
||
if (!this.$$.ptr) {
|
||
throwInstanceAlreadyDeleted(this);
|
||
}
|
||
if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) {
|
||
throwBindingError("Object already scheduled for deletion");
|
||
}
|
||
deletionQueue.push(this);
|
||
if (deletionQueue.length === 1 && delayFunction) {
|
||
delayFunction(flushPendingDeletes);
|
||
}
|
||
this.$$.deleteScheduled = true;
|
||
return this;
|
||
}
|
||
function init_ClassHandle() {
|
||
ClassHandle.prototype["isAliasOf"] = ClassHandle_isAliasOf;
|
||
ClassHandle.prototype["clone"] = ClassHandle_clone;
|
||
ClassHandle.prototype["delete"] = ClassHandle_delete;
|
||
ClassHandle.prototype["isDeleted"] = ClassHandle_isDeleted;
|
||
ClassHandle.prototype["deleteLater"] = ClassHandle_deleteLater;
|
||
}
|
||
function ClassHandle() {}
|
||
function ensureOverloadTable(proto, methodName, humanName) {
|
||
if (undefined === proto[methodName].overloadTable) {
|
||
var prevFunc = proto[methodName];
|
||
proto[methodName] = function () {
|
||
if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) {
|
||
throwBindingError("Function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!");
|
||
}
|
||
return proto[methodName].overloadTable[arguments.length].apply(this, arguments);
|
||
};
|
||
proto[methodName].overloadTable = [];
|
||
proto[methodName].overloadTable[prevFunc.argCount] = prevFunc;
|
||
}
|
||
}
|
||
function exposePublicSymbol(name, value, numArguments) {
|
||
if (Module.hasOwnProperty(name)) {
|
||
if (undefined === numArguments || undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments]) {
|
||
throwBindingError("Cannot register public name '" + name + "' twice");
|
||
}
|
||
ensureOverloadTable(Module, name, name);
|
||
if (Module.hasOwnProperty(numArguments)) {
|
||
throwBindingError("Cannot register multiple overloads of a function with the same number of arguments (" + numArguments + ")!");
|
||
}
|
||
Module[name].overloadTable[numArguments] = value;
|
||
} else {
|
||
Module[name] = value;
|
||
if (undefined !== numArguments) {
|
||
Module[name].numArguments = numArguments;
|
||
}
|
||
}
|
||
}
|
||
function RegisteredClass(name, constructor, instancePrototype, rawDestructor, baseClass, getActualType, upcast, downcast) {
|
||
this.name = name;
|
||
this.constructor = constructor;
|
||
this.instancePrototype = instancePrototype;
|
||
this.rawDestructor = rawDestructor;
|
||
this.baseClass = baseClass;
|
||
this.getActualType = getActualType;
|
||
this.upcast = upcast;
|
||
this.downcast = downcast;
|
||
this.pureVirtualFunctions = [];
|
||
}
|
||
function upcastPointer(ptr, ptrClass, desiredClass) {
|
||
while (ptrClass !== desiredClass) {
|
||
if (!ptrClass.upcast) {
|
||
throwBindingError("Expected null or instance of " + desiredClass.name + ", got an instance of " + ptrClass.name);
|
||
}
|
||
ptr = ptrClass.upcast(ptr);
|
||
ptrClass = ptrClass.baseClass;
|
||
}
|
||
return ptr;
|
||
}
|
||
function constNoSmartPtrRawPointerToWireType(destructors, handle) {
|
||
if (handle === null) {
|
||
if (this.isReference) {
|
||
throwBindingError("null is not a valid " + this.name);
|
||
}
|
||
return 0;
|
||
}
|
||
if (!handle.$$) {
|
||
throwBindingError('Cannot pass "' + embindRepr(handle) + '" as a ' + this.name);
|
||
}
|
||
if (!handle.$$.ptr) {
|
||
throwBindingError("Cannot pass deleted object as a pointer of type " + this.name);
|
||
}
|
||
var handleClass = handle.$$.ptrType.registeredClass;
|
||
var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
|
||
return ptr;
|
||
}
|
||
function genericPointerToWireType(destructors, handle) {
|
||
var ptr;
|
||
if (handle === null) {
|
||
if (this.isReference) {
|
||
throwBindingError("null is not a valid " + this.name);
|
||
}
|
||
if (this.isSmartPointer) {
|
||
ptr = this.rawConstructor();
|
||
if (destructors !== null) {
|
||
destructors.push(this.rawDestructor, ptr);
|
||
}
|
||
return ptr;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
if (!handle.$$) {
|
||
throwBindingError('Cannot pass "' + embindRepr(handle) + '" as a ' + this.name);
|
||
}
|
||
if (!handle.$$.ptr) {
|
||
throwBindingError("Cannot pass deleted object as a pointer of type " + this.name);
|
||
}
|
||
if (!this.isConst && handle.$$.ptrType.isConst) {
|
||
throwBindingError("Cannot convert argument of type " + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + " to parameter type " + this.name);
|
||
}
|
||
var handleClass = handle.$$.ptrType.registeredClass;
|
||
ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
|
||
if (this.isSmartPointer) {
|
||
if (undefined === handle.$$.smartPtr) {
|
||
throwBindingError("Passing raw pointer to smart pointer is illegal");
|
||
}
|
||
switch (this.sharingPolicy) {
|
||
case 0:
|
||
if (handle.$$.smartPtrType === this) {
|
||
ptr = handle.$$.smartPtr;
|
||
} else {
|
||
throwBindingError("Cannot convert argument of type " + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + " to parameter type " + this.name);
|
||
}
|
||
break;
|
||
case 1:
|
||
ptr = handle.$$.smartPtr;
|
||
break;
|
||
case 2:
|
||
if (handle.$$.smartPtrType === this) {
|
||
ptr = handle.$$.smartPtr;
|
||
} else {
|
||
var clonedHandle = handle["clone"]();
|
||
ptr = this.rawShare(ptr, Emval.toHandle(function () {
|
||
clonedHandle["delete"]();
|
||
}));
|
||
if (destructors !== null) {
|
||
destructors.push(this.rawDestructor, ptr);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
throwBindingError("Unsupporting sharing policy");
|
||
}
|
||
}
|
||
return ptr;
|
||
}
|
||
function nonConstNoSmartPtrRawPointerToWireType(destructors, handle) {
|
||
if (handle === null) {
|
||
if (this.isReference) {
|
||
throwBindingError("null is not a valid " + this.name);
|
||
}
|
||
return 0;
|
||
}
|
||
if (!handle.$$) {
|
||
throwBindingError('Cannot pass "' + embindRepr(handle) + '" as a ' + this.name);
|
||
}
|
||
if (!handle.$$.ptr) {
|
||
throwBindingError("Cannot pass deleted object as a pointer of type " + this.name);
|
||
}
|
||
if (handle.$$.ptrType.isConst) {
|
||
throwBindingError("Cannot convert argument of type " + handle.$$.ptrType.name + " to parameter type " + this.name);
|
||
}
|
||
var handleClass = handle.$$.ptrType.registeredClass;
|
||
var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
|
||
return ptr;
|
||
}
|
||
function simpleReadValueFromPointer(pointer) {
|
||
return this["fromWireType"](HEAP32[pointer >> 2]);
|
||
}
|
||
function RegisteredPointer_getPointee(ptr) {
|
||
if (this.rawGetPointee) {
|
||
ptr = this.rawGetPointee(ptr);
|
||
}
|
||
return ptr;
|
||
}
|
||
function RegisteredPointer_destructor(ptr) {
|
||
if (this.rawDestructor) {
|
||
this.rawDestructor(ptr);
|
||
}
|
||
}
|
||
function RegisteredPointer_deleteObject(handle) {
|
||
if (handle !== null) {
|
||
handle["delete"]();
|
||
}
|
||
}
|
||
function init_RegisteredPointer() {
|
||
RegisteredPointer.prototype.getPointee = RegisteredPointer_getPointee;
|
||
RegisteredPointer.prototype.destructor = RegisteredPointer_destructor;
|
||
RegisteredPointer.prototype["argPackAdvance"] = 8;
|
||
RegisteredPointer.prototype["readValueFromPointer"] = simpleReadValueFromPointer;
|
||
RegisteredPointer.prototype["deleteObject"] = RegisteredPointer_deleteObject;
|
||
RegisteredPointer.prototype["fromWireType"] = RegisteredPointer_fromWireType;
|
||
}
|
||
function RegisteredPointer(name, registeredClass, isReference, isConst, isSmartPointer, pointeeType, sharingPolicy, rawGetPointee, rawConstructor, rawShare, rawDestructor) {
|
||
this.name = name;
|
||
this.registeredClass = registeredClass;
|
||
this.isReference = isReference;
|
||
this.isConst = isConst;
|
||
this.isSmartPointer = isSmartPointer;
|
||
this.pointeeType = pointeeType;
|
||
this.sharingPolicy = sharingPolicy;
|
||
this.rawGetPointee = rawGetPointee;
|
||
this.rawConstructor = rawConstructor;
|
||
this.rawShare = rawShare;
|
||
this.rawDestructor = rawDestructor;
|
||
if (!isSmartPointer && registeredClass.baseClass === undefined) {
|
||
if (isConst) {
|
||
this["toWireType"] = constNoSmartPtrRawPointerToWireType;
|
||
this.destructorFunction = null;
|
||
} else {
|
||
this["toWireType"] = nonConstNoSmartPtrRawPointerToWireType;
|
||
this.destructorFunction = null;
|
||
}
|
||
} else {
|
||
this["toWireType"] = genericPointerToWireType;
|
||
}
|
||
}
|
||
function replacePublicSymbol(name, value, numArguments) {
|
||
if (!Module.hasOwnProperty(name)) {
|
||
throwInternalError("Replacing nonexistant public symbol");
|
||
}
|
||
if (undefined !== Module[name].overloadTable && undefined !== numArguments) {
|
||
Module[name].overloadTable[numArguments] = value;
|
||
} else {
|
||
Module[name] = value;
|
||
Module[name].argCount = numArguments;
|
||
}
|
||
}
|
||
function dynCallLegacy(sig, ptr, args) {
|
||
var f = Module["dynCall_" + sig];
|
||
return args && args.length ? f.apply(null, [ptr].concat(args)) : f.call(null, ptr);
|
||
}
|
||
var wasmTableMirror = [];
|
||
function getWasmTableEntry(funcPtr) {
|
||
var func = wasmTableMirror[funcPtr];
|
||
if (!func) {
|
||
if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1;
|
||
wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr);
|
||
}
|
||
return func;
|
||
}
|
||
function dynCall(sig, ptr, args) {
|
||
if (sig.includes("j")) {
|
||
return dynCallLegacy(sig, ptr, args);
|
||
}
|
||
var rtn = getWasmTableEntry(ptr).apply(null, args);
|
||
return rtn;
|
||
}
|
||
function getDynCaller(sig, ptr) {
|
||
var argCache = [];
|
||
return function () {
|
||
argCache.length = 0;
|
||
Object.assign(argCache, arguments);
|
||
return dynCall(sig, ptr, argCache);
|
||
};
|
||
}
|
||
function embind__requireFunction(signature, rawFunction) {
|
||
signature = readLatin1String(signature);
|
||
function makeDynCaller() {
|
||
if (signature.includes("j")) {
|
||
return getDynCaller(signature, rawFunction);
|
||
}
|
||
return getWasmTableEntry(rawFunction);
|
||
}
|
||
var fp = makeDynCaller();
|
||
if (typeof fp != "function") {
|
||
throwBindingError("unknown function pointer with signature " + signature + ": " + rawFunction);
|
||
}
|
||
return fp;
|
||
}
|
||
var UnboundTypeError = undefined;
|
||
function getTypeName(type) {
|
||
var ptr = ___getTypeName(type);
|
||
var rv = readLatin1String(ptr);
|
||
_free(ptr);
|
||
return rv;
|
||
}
|
||
function throwUnboundTypeError(message, types) {
|
||
var unboundTypes = [];
|
||
var seen = {};
|
||
function visit(type) {
|
||
if (seen[type]) {
|
||
return;
|
||
}
|
||
if (registeredTypes[type]) {
|
||
return;
|
||
}
|
||
if (typeDependencies[type]) {
|
||
typeDependencies[type].forEach(visit);
|
||
return;
|
||
}
|
||
unboundTypes.push(type);
|
||
seen[type] = true;
|
||
}
|
||
types.forEach(visit);
|
||
throw new UnboundTypeError(message + ": " + unboundTypes.map(getTypeName).join([", "]));
|
||
}
|
||
function __embind_register_class(rawType, rawPointerType, rawConstPointerType, baseClassRawType, getActualTypeSignature, getActualType, upcastSignature, upcast, downcastSignature, downcast, name, destructorSignature, rawDestructor) {
|
||
name = readLatin1String(name);
|
||
getActualType = embind__requireFunction(getActualTypeSignature, getActualType);
|
||
if (upcast) {
|
||
upcast = embind__requireFunction(upcastSignature, upcast);
|
||
}
|
||
if (downcast) {
|
||
downcast = embind__requireFunction(downcastSignature, downcast);
|
||
}
|
||
rawDestructor = embind__requireFunction(destructorSignature, rawDestructor);
|
||
var legalFunctionName = makeLegalFunctionName(name);
|
||
exposePublicSymbol(legalFunctionName, function () {
|
||
throwUnboundTypeError("Cannot construct " + name + " due to unbound types", [baseClassRawType]);
|
||
});
|
||
whenDependentTypesAreResolved([rawType, rawPointerType, rawConstPointerType], baseClassRawType ? [baseClassRawType] : [], function (base) {
|
||
base = base[0];
|
||
var baseClass;
|
||
var basePrototype;
|
||
if (baseClassRawType) {
|
||
baseClass = base.registeredClass;
|
||
basePrototype = baseClass.instancePrototype;
|
||
} else {
|
||
basePrototype = ClassHandle.prototype;
|
||
}
|
||
var constructor = createNamedFunction(legalFunctionName, function () {
|
||
if (Object.getPrototypeOf(this) !== instancePrototype) {
|
||
throw new BindingError("Use 'new' to construct " + name);
|
||
}
|
||
if (undefined === registeredClass.constructor_body) {
|
||
throw new BindingError(name + " has no accessible constructor");
|
||
}
|
||
var body = registeredClass.constructor_body[arguments.length];
|
||
if (undefined === body) {
|
||
throw new BindingError("Tried to invoke ctor of " + name + " with invalid number of parameters (" + arguments.length + ") - expected (" + Object.keys(registeredClass.constructor_body).toString() + ") parameters instead!");
|
||
}
|
||
return body.apply(this, arguments);
|
||
});
|
||
var instancePrototype = Object.create(basePrototype, {
|
||
constructor: {
|
||
value: constructor
|
||
}
|
||
});
|
||
constructor.prototype = instancePrototype;
|
||
var registeredClass = new RegisteredClass(name, constructor, instancePrototype, rawDestructor, baseClass, getActualType, upcast, downcast);
|
||
var referenceConverter = new RegisteredPointer(name, registeredClass, true, false, false);
|
||
var pointerConverter = new RegisteredPointer(name + "*", registeredClass, false, false, false);
|
||
var constPointerConverter = new RegisteredPointer(name + " const*", registeredClass, false, true, false);
|
||
registeredPointers[rawType] = {
|
||
pointerType: pointerConverter,
|
||
constPointerType: constPointerConverter
|
||
};
|
||
replacePublicSymbol(legalFunctionName, constructor);
|
||
return [referenceConverter, pointerConverter, constPointerConverter];
|
||
});
|
||
}
|
||
function heap32VectorToArray(count, firstElement) {
|
||
var array = [];
|
||
for (var i = 0; i < count; i++) {
|
||
array.push(HEAPU32[firstElement + i * 4 >> 2]);
|
||
}
|
||
return array;
|
||
}
|
||
function runDestructors(destructors) {
|
||
while (destructors.length) {
|
||
var ptr = destructors.pop();
|
||
var del = destructors.pop();
|
||
del(ptr);
|
||
}
|
||
}
|
||
function new_(constructor, argumentList) {
|
||
if (!(constructor instanceof Function)) {
|
||
throw new TypeError("new_ called with constructor type " + typeof constructor + " which is not a function");
|
||
}
|
||
var dummy = createNamedFunction(constructor.name || "unknownFunctionName", function () {});
|
||
dummy.prototype = constructor.prototype;
|
||
var obj = new dummy();
|
||
var r = constructor.apply(obj, argumentList);
|
||
return r instanceof Object ? r : obj;
|
||
}
|
||
function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc) {
|
||
var argCount = argTypes.length;
|
||
if (argCount < 2) {
|
||
throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!");
|
||
}
|
||
var isClassMethodFunc = argTypes[1] !== null && classType !== null;
|
||
var needsDestructorStack = false;
|
||
for (var i = 1; i < argTypes.length; ++i) {
|
||
if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) {
|
||
needsDestructorStack = true;
|
||
break;
|
||
}
|
||
}
|
||
var returns = argTypes[0].name !== "void";
|
||
var argsList = "";
|
||
var argsListWired = "";
|
||
for (var i = 0; i < argCount - 2; ++i) {
|
||
argsList += (i !== 0 ? ", " : "") + "arg" + i;
|
||
argsListWired += (i !== 0 ? ", " : "") + "arg" + i + "Wired";
|
||
}
|
||
var invokerFnBody = "return function " + makeLegalFunctionName(humanName) + "(" + argsList + ") {\n" + "if (arguments.length !== " + (argCount - 2) + ") {\n" + "throwBindingError('function " + humanName + " called with ' + arguments.length + ' arguments, expected " + (argCount - 2) + " args!');\n" + "}\n";
|
||
if (needsDestructorStack) {
|
||
invokerFnBody += "var destructors = [];\n";
|
||
}
|
||
var dtorStack = needsDestructorStack ? "destructors" : "null";
|
||
var args1 = ["throwBindingError", "invoker", "fn", "runDestructors", "retType", "classParam"];
|
||
var args2 = [throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]];
|
||
if (isClassMethodFunc) {
|
||
invokerFnBody += "var thisWired = classParam.toWireType(" + dtorStack + ", this);\n";
|
||
}
|
||
for (var i = 0; i < argCount - 2; ++i) {
|
||
invokerFnBody += "var arg" + i + "Wired = argType" + i + ".toWireType(" + dtorStack + ", arg" + i + "); // " + argTypes[i + 2].name + "\n";
|
||
args1.push("argType" + i);
|
||
args2.push(argTypes[i + 2]);
|
||
}
|
||
if (isClassMethodFunc) {
|
||
argsListWired = "thisWired" + (argsListWired.length > 0 ? ", " : "") + argsListWired;
|
||
}
|
||
invokerFnBody += (returns ? "var rv = " : "") + "invoker(fn" + (argsListWired.length > 0 ? ", " : "") + argsListWired + ");\n";
|
||
if (needsDestructorStack) {
|
||
invokerFnBody += "runDestructors(destructors);\n";
|
||
} else {
|
||
for (var i = isClassMethodFunc ? 1 : 2; i < argTypes.length; ++i) {
|
||
var paramName = i === 1 ? "thisWired" : "arg" + (i - 2) + "Wired";
|
||
if (argTypes[i].destructorFunction !== null) {
|
||
invokerFnBody += paramName + "_dtor(" + paramName + "); // " + argTypes[i].name + "\n";
|
||
args1.push(paramName + "_dtor");
|
||
args2.push(argTypes[i].destructorFunction);
|
||
}
|
||
}
|
||
}
|
||
if (returns) {
|
||
invokerFnBody += "var ret = retType.fromWireType(rv);\n" + "return ret;\n";
|
||
}
|
||
invokerFnBody += "}\n";
|
||
args1.push(invokerFnBody);
|
||
var invokerFunction = new_(Function, args1).apply(null, args2);
|
||
return invokerFunction;
|
||
}
|
||
function __embind_register_class_constructor(rawClassType, argCount, rawArgTypesAddr, invokerSignature, invoker, rawConstructor) {
|
||
assert(argCount > 0);
|
||
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
|
||
invoker = embind__requireFunction(invokerSignature, invoker);
|
||
whenDependentTypesAreResolved([], [rawClassType], function (classType) {
|
||
classType = classType[0];
|
||
var humanName = "constructor " + classType.name;
|
||
if (undefined === classType.registeredClass.constructor_body) {
|
||
classType.registeredClass.constructor_body = [];
|
||
}
|
||
if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) {
|
||
throw new BindingError("Cannot register multiple constructors with identical number of parameters (" + (argCount - 1) + ") for class '" + classType.name + "'! Overload resolution is currently only performed using the parameter count, not actual type info!");
|
||
}
|
||
classType.registeredClass.constructor_body[argCount - 1] = () => {
|
||
throwUnboundTypeError("Cannot construct " + classType.name + " due to unbound types", rawArgTypes);
|
||
};
|
||
whenDependentTypesAreResolved([], rawArgTypes, function (argTypes) {
|
||
argTypes.splice(1, 0, null);
|
||
classType.registeredClass.constructor_body[argCount - 1] = craftInvokerFunction(humanName, argTypes, null, invoker, rawConstructor);
|
||
return [];
|
||
});
|
||
return [];
|
||
});
|
||
}
|
||
function __embind_register_class_function(rawClassType, methodName, argCount, rawArgTypesAddr, invokerSignature, rawInvoker, context, isPureVirtual) {
|
||
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
|
||
methodName = readLatin1String(methodName);
|
||
rawInvoker = embind__requireFunction(invokerSignature, rawInvoker);
|
||
whenDependentTypesAreResolved([], [rawClassType], function (classType) {
|
||
classType = classType[0];
|
||
var humanName = classType.name + "." + methodName;
|
||
if (methodName.startsWith("@@")) {
|
||
methodName = Symbol[methodName.substring(2)];
|
||
}
|
||
if (isPureVirtual) {
|
||
classType.registeredClass.pureVirtualFunctions.push(methodName);
|
||
}
|
||
function unboundTypesHandler() {
|
||
throwUnboundTypeError("Cannot call " + humanName + " due to unbound types", rawArgTypes);
|
||
}
|
||
var proto = classType.registeredClass.instancePrototype;
|
||
var method = proto[methodName];
|
||
if (undefined === method || undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount - 2) {
|
||
unboundTypesHandler.argCount = argCount - 2;
|
||
unboundTypesHandler.className = classType.name;
|
||
proto[methodName] = unboundTypesHandler;
|
||
} else {
|
||
ensureOverloadTable(proto, methodName, humanName);
|
||
proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler;
|
||
}
|
||
whenDependentTypesAreResolved([], rawArgTypes, function (argTypes) {
|
||
var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context);
|
||
if (undefined === proto[methodName].overloadTable) {
|
||
memberFunction.argCount = argCount - 2;
|
||
proto[methodName] = memberFunction;
|
||
} else {
|
||
proto[methodName].overloadTable[argCount - 2] = memberFunction;
|
||
}
|
||
return [];
|
||
});
|
||
return [];
|
||
});
|
||
}
|
||
var emval_free_list = [];
|
||
var emval_handle_array = [{}, {
|
||
value: undefined
|
||
}, {
|
||
value: null
|
||
}, {
|
||
value: true
|
||
}, {
|
||
value: false
|
||
}];
|
||
function __emval_decref(handle) {
|
||
if (handle > 4 && 0 === --emval_handle_array[handle].refcount) {
|
||
emval_handle_array[handle] = undefined;
|
||
emval_free_list.push(handle);
|
||
}
|
||
}
|
||
function count_emval_handles() {
|
||
var count = 0;
|
||
for (var i = 5; i < emval_handle_array.length; ++i) {
|
||
if (emval_handle_array[i] !== undefined) {
|
||
++count;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
function get_first_emval() {
|
||
for (var i = 5; i < emval_handle_array.length; ++i) {
|
||
if (emval_handle_array[i] !== undefined) {
|
||
return emval_handle_array[i];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function init_emval() {
|
||
Module["count_emval_handles"] = count_emval_handles;
|
||
Module["get_first_emval"] = get_first_emval;
|
||
}
|
||
var Emval = {
|
||
toValue: handle => {
|
||
if (!handle) {
|
||
throwBindingError("Cannot use deleted val. handle = " + handle);
|
||
}
|
||
return emval_handle_array[handle].value;
|
||
},
|
||
toHandle: value => {
|
||
switch (value) {
|
||
case undefined:
|
||
return 1;
|
||
case null:
|
||
return 2;
|
||
case true:
|
||
return 3;
|
||
case false:
|
||
return 4;
|
||
default:
|
||
{
|
||
var handle = emval_free_list.length ? emval_free_list.pop() : emval_handle_array.length;
|
||
emval_handle_array[handle] = {
|
||
refcount: 1,
|
||
value: value
|
||
};
|
||
return handle;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
function __embind_register_emval(rawType, name) {
|
||
name = readLatin1String(name);
|
||
registerType(rawType, {
|
||
name: name,
|
||
"fromWireType": function (handle) {
|
||
var rv = Emval.toValue(handle);
|
||
__emval_decref(handle);
|
||
return rv;
|
||
},
|
||
"toWireType": function (destructors, value) {
|
||
return Emval.toHandle(value);
|
||
},
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": simpleReadValueFromPointer,
|
||
destructorFunction: null
|
||
});
|
||
}
|
||
function embindRepr(v) {
|
||
if (v === null) {
|
||
return "null";
|
||
}
|
||
var t = typeof v;
|
||
if (t === "object" || t === "array" || t === "function") {
|
||
return v.toString();
|
||
} else {
|
||
return "" + v;
|
||
}
|
||
}
|
||
function floatReadValueFromPointer(name, shift) {
|
||
switch (shift) {
|
||
case 2:
|
||
return function (pointer) {
|
||
return this["fromWireType"](HEAPF32[pointer >> 2]);
|
||
};
|
||
case 3:
|
||
return function (pointer) {
|
||
return this["fromWireType"](HEAPF64[pointer >> 3]);
|
||
};
|
||
default:
|
||
throw new TypeError("Unknown float type: " + name);
|
||
}
|
||
}
|
||
function __embind_register_float(rawType, name, size) {
|
||
var shift = getShiftFromSize(size);
|
||
name = readLatin1String(name);
|
||
registerType(rawType, {
|
||
name: name,
|
||
"fromWireType": function (value) {
|
||
return value;
|
||
},
|
||
"toWireType": function (destructors, value) {
|
||
return value;
|
||
},
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": floatReadValueFromPointer(name, shift),
|
||
destructorFunction: null
|
||
});
|
||
}
|
||
function integerReadValueFromPointer(name, shift, signed) {
|
||
switch (shift) {
|
||
case 0:
|
||
return signed ? function readS8FromPointer(pointer) {
|
||
return HEAP8[pointer];
|
||
} : function readU8FromPointer(pointer) {
|
||
return HEAPU8[pointer];
|
||
};
|
||
case 1:
|
||
return signed ? function readS16FromPointer(pointer) {
|
||
return HEAP16[pointer >> 1];
|
||
} : function readU16FromPointer(pointer) {
|
||
return HEAPU16[pointer >> 1];
|
||
};
|
||
case 2:
|
||
return signed ? function readS32FromPointer(pointer) {
|
||
return HEAP32[pointer >> 2];
|
||
} : function readU32FromPointer(pointer) {
|
||
return HEAPU32[pointer >> 2];
|
||
};
|
||
default:
|
||
throw new TypeError("Unknown integer type: " + name);
|
||
}
|
||
}
|
||
function __embind_register_integer(primitiveType, name, size, minRange, maxRange) {
|
||
name = readLatin1String(name);
|
||
var shift = getShiftFromSize(size);
|
||
var fromWireType = value => value;
|
||
if (minRange === 0) {
|
||
var bitshift = 32 - 8 * size;
|
||
fromWireType = value => value << bitshift >>> bitshift;
|
||
}
|
||
var isUnsignedType = name.includes("unsigned");
|
||
var checkAssertions = (value, toTypeName) => {};
|
||
var toWireType;
|
||
if (isUnsignedType) {
|
||
toWireType = function (destructors, value) {
|
||
checkAssertions(value, this.name);
|
||
return value >>> 0;
|
||
};
|
||
} else {
|
||
toWireType = function (destructors, value) {
|
||
checkAssertions(value, this.name);
|
||
return value;
|
||
};
|
||
}
|
||
registerType(primitiveType, {
|
||
name: name,
|
||
"fromWireType": fromWireType,
|
||
"toWireType": toWireType,
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": integerReadValueFromPointer(name, shift, minRange !== 0),
|
||
destructorFunction: null
|
||
});
|
||
}
|
||
function __embind_register_memory_view(rawType, dataTypeIndex, name) {
|
||
var typeMapping = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array];
|
||
var TA = typeMapping[dataTypeIndex];
|
||
function decodeMemoryView(handle) {
|
||
handle = handle >> 2;
|
||
var heap = HEAPU32;
|
||
var size = heap[handle];
|
||
var data = heap[handle + 1];
|
||
return new TA(buffer, data, size);
|
||
}
|
||
name = readLatin1String(name);
|
||
registerType(rawType, {
|
||
name: name,
|
||
"fromWireType": decodeMemoryView,
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": decodeMemoryView
|
||
}, {
|
||
ignoreDuplicateRegistrations: true
|
||
});
|
||
}
|
||
function __embind_register_std_string(rawType, name) {
|
||
name = readLatin1String(name);
|
||
var stdStringIsUTF8 = name === "std::string";
|
||
registerType(rawType, {
|
||
name: name,
|
||
"fromWireType": function (value) {
|
||
var length = HEAPU32[value >> 2];
|
||
var payload = value + 4;
|
||
var str;
|
||
if (stdStringIsUTF8) {
|
||
var decodeStartPtr = payload;
|
||
for (var i = 0; i <= length; ++i) {
|
||
var currentBytePtr = payload + i;
|
||
if (i == length || HEAPU8[currentBytePtr] == 0) {
|
||
var maxRead = currentBytePtr - decodeStartPtr;
|
||
var stringSegment = UTF8ToString(decodeStartPtr, maxRead);
|
||
if (str === undefined) {
|
||
str = stringSegment;
|
||
} else {
|
||
str += String.fromCharCode(0);
|
||
str += stringSegment;
|
||
}
|
||
decodeStartPtr = currentBytePtr + 1;
|
||
}
|
||
}
|
||
} else {
|
||
var a = new Array(length);
|
||
for (var i = 0; i < length; ++i) {
|
||
a[i] = String.fromCharCode(HEAPU8[payload + i]);
|
||
}
|
||
str = a.join("");
|
||
}
|
||
_free(value);
|
||
return str;
|
||
},
|
||
"toWireType": function (destructors, value) {
|
||
if (value instanceof ArrayBuffer) {
|
||
value = new Uint8Array(value);
|
||
}
|
||
var length;
|
||
var valueIsOfTypeString = typeof value == "string";
|
||
if (!(valueIsOfTypeString || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Int8Array)) {
|
||
throwBindingError("Cannot pass non-string to std::string");
|
||
}
|
||
if (stdStringIsUTF8 && valueIsOfTypeString) {
|
||
length = lengthBytesUTF8(value);
|
||
} else {
|
||
length = value.length;
|
||
}
|
||
var base = _malloc(4 + length + 1);
|
||
var ptr = base + 4;
|
||
HEAPU32[base >> 2] = length;
|
||
if (stdStringIsUTF8 && valueIsOfTypeString) {
|
||
stringToUTF8(value, ptr, length + 1);
|
||
} else {
|
||
if (valueIsOfTypeString) {
|
||
for (var i = 0; i < length; ++i) {
|
||
var charCode = value.charCodeAt(i);
|
||
if (charCode > 255) {
|
||
_free(ptr);
|
||
throwBindingError("String has UTF-16 code units that do not fit in 8 bits");
|
||
}
|
||
HEAPU8[ptr + i] = charCode;
|
||
}
|
||
} else {
|
||
for (var i = 0; i < length; ++i) {
|
||
HEAPU8[ptr + i] = value[i];
|
||
}
|
||
}
|
||
}
|
||
if (destructors !== null) {
|
||
destructors.push(_free, base);
|
||
}
|
||
return base;
|
||
},
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": simpleReadValueFromPointer,
|
||
destructorFunction: function (ptr) {
|
||
_free(ptr);
|
||
}
|
||
});
|
||
}
|
||
var UTF16Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf-16le") : undefined;
|
||
function UTF16ToString(ptr, maxBytesToRead) {
|
||
var endPtr = ptr;
|
||
var idx = endPtr >> 1;
|
||
var maxIdx = idx + maxBytesToRead / 2;
|
||
while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx;
|
||
endPtr = idx << 1;
|
||
if (endPtr - ptr > 32 && UTF16Decoder) {
|
||
return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr));
|
||
} else {
|
||
var str = "";
|
||
for (var i = 0; !(i >= maxBytesToRead / 2); ++i) {
|
||
var codeUnit = HEAP16[ptr + i * 2 >> 1];
|
||
if (codeUnit == 0) break;
|
||
str += String.fromCharCode(codeUnit);
|
||
}
|
||
return str;
|
||
}
|
||
}
|
||
function stringToUTF16(str, outPtr, maxBytesToWrite) {
|
||
if (maxBytesToWrite === undefined) {
|
||
maxBytesToWrite = 2147483647;
|
||
}
|
||
if (maxBytesToWrite < 2) return 0;
|
||
maxBytesToWrite -= 2;
|
||
var startPtr = outPtr;
|
||
var numCharsToWrite = maxBytesToWrite < str.length * 2 ? maxBytesToWrite / 2 : str.length;
|
||
for (var i = 0; i < numCharsToWrite; ++i) {
|
||
var codeUnit = str.charCodeAt(i);
|
||
HEAP16[outPtr >> 1] = codeUnit;
|
||
outPtr += 2;
|
||
}
|
||
HEAP16[outPtr >> 1] = 0;
|
||
return outPtr - startPtr;
|
||
}
|
||
function lengthBytesUTF16(str) {
|
||
return str.length * 2;
|
||
}
|
||
function UTF32ToString(ptr, maxBytesToRead) {
|
||
var i = 0;
|
||
var str = "";
|
||
while (!(i >= maxBytesToRead / 4)) {
|
||
var utf32 = HEAP32[ptr + i * 4 >> 2];
|
||
if (utf32 == 0) break;
|
||
++i;
|
||
if (utf32 >= 65536) {
|
||
var ch = utf32 - 65536;
|
||
str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
|
||
} else {
|
||
str += String.fromCharCode(utf32);
|
||
}
|
||
}
|
||
return str;
|
||
}
|
||
function stringToUTF32(str, outPtr, maxBytesToWrite) {
|
||
if (maxBytesToWrite === undefined) {
|
||
maxBytesToWrite = 2147483647;
|
||
}
|
||
if (maxBytesToWrite < 4) return 0;
|
||
var startPtr = outPtr;
|
||
var endPtr = startPtr + maxBytesToWrite - 4;
|
||
for (var i = 0; i < str.length; ++i) {
|
||
var codeUnit = str.charCodeAt(i);
|
||
if (codeUnit >= 55296 && codeUnit <= 57343) {
|
||
var trailSurrogate = str.charCodeAt(++i);
|
||
codeUnit = 65536 + ((codeUnit & 1023) << 10) | trailSurrogate & 1023;
|
||
}
|
||
HEAP32[outPtr >> 2] = codeUnit;
|
||
outPtr += 4;
|
||
if (outPtr + 4 > endPtr) break;
|
||
}
|
||
HEAP32[outPtr >> 2] = 0;
|
||
return outPtr - startPtr;
|
||
}
|
||
function lengthBytesUTF32(str) {
|
||
var len = 0;
|
||
for (var i = 0; i < str.length; ++i) {
|
||
var codeUnit = str.charCodeAt(i);
|
||
if (codeUnit >= 55296 && codeUnit <= 57343) ++i;
|
||
len += 4;
|
||
}
|
||
return len;
|
||
}
|
||
function __embind_register_std_wstring(rawType, charSize, name) {
|
||
name = readLatin1String(name);
|
||
var decodeString, encodeString, getHeap, lengthBytesUTF, shift;
|
||
if (charSize === 2) {
|
||
decodeString = UTF16ToString;
|
||
encodeString = stringToUTF16;
|
||
lengthBytesUTF = lengthBytesUTF16;
|
||
getHeap = () => HEAPU16;
|
||
shift = 1;
|
||
} else if (charSize === 4) {
|
||
decodeString = UTF32ToString;
|
||
encodeString = stringToUTF32;
|
||
lengthBytesUTF = lengthBytesUTF32;
|
||
getHeap = () => HEAPU32;
|
||
shift = 2;
|
||
}
|
||
registerType(rawType, {
|
||
name: name,
|
||
"fromWireType": function (value) {
|
||
var length = HEAPU32[value >> 2];
|
||
var HEAP = getHeap();
|
||
var str;
|
||
var decodeStartPtr = value + 4;
|
||
for (var i = 0; i <= length; ++i) {
|
||
var currentBytePtr = value + 4 + i * charSize;
|
||
if (i == length || HEAP[currentBytePtr >> shift] == 0) {
|
||
var maxReadBytes = currentBytePtr - decodeStartPtr;
|
||
var stringSegment = decodeString(decodeStartPtr, maxReadBytes);
|
||
if (str === undefined) {
|
||
str = stringSegment;
|
||
} else {
|
||
str += String.fromCharCode(0);
|
||
str += stringSegment;
|
||
}
|
||
decodeStartPtr = currentBytePtr + charSize;
|
||
}
|
||
}
|
||
_free(value);
|
||
return str;
|
||
},
|
||
"toWireType": function (destructors, value) {
|
||
if (!(typeof value == "string")) {
|
||
throwBindingError("Cannot pass non-string to C++ string type " + name);
|
||
}
|
||
var length = lengthBytesUTF(value);
|
||
var ptr = _malloc(4 + length + charSize);
|
||
HEAPU32[ptr >> 2] = length >> shift;
|
||
encodeString(value, ptr + 4, length + charSize);
|
||
if (destructors !== null) {
|
||
destructors.push(_free, ptr);
|
||
}
|
||
return ptr;
|
||
},
|
||
"argPackAdvance": 8,
|
||
"readValueFromPointer": simpleReadValueFromPointer,
|
||
destructorFunction: function (ptr) {
|
||
_free(ptr);
|
||
}
|
||
});
|
||
}
|
||
function __embind_register_void(rawType, name) {
|
||
name = readLatin1String(name);
|
||
registerType(rawType, {
|
||
isVoid: true,
|
||
name: name,
|
||
"argPackAdvance": 0,
|
||
"fromWireType": function () {
|
||
return undefined;
|
||
},
|
||
"toWireType": function (destructors, o) {
|
||
return undefined;
|
||
}
|
||
});
|
||
}
|
||
function __emscripten_date_now() {
|
||
return Date.now();
|
||
}
|
||
var emval_symbols = {};
|
||
function getStringOrSymbol(address) {
|
||
var symbol = emval_symbols[address];
|
||
if (symbol === undefined) {
|
||
return readLatin1String(address);
|
||
}
|
||
return symbol;
|
||
}
|
||
var emval_methodCallers = [];
|
||
function __emval_call_void_method(caller, handle, methodName, args) {
|
||
caller = emval_methodCallers[caller];
|
||
handle = Emval.toValue(handle);
|
||
methodName = getStringOrSymbol(methodName);
|
||
caller(handle, methodName, null, args);
|
||
}
|
||
function emval_addMethodCaller(caller) {
|
||
var id = emval_methodCallers.length;
|
||
emval_methodCallers.push(caller);
|
||
return id;
|
||
}
|
||
function requireRegisteredType(rawType, humanName) {
|
||
var impl = registeredTypes[rawType];
|
||
if (undefined === impl) {
|
||
throwBindingError(humanName + " has unknown type " + getTypeName(rawType));
|
||
}
|
||
return impl;
|
||
}
|
||
function emval_lookupTypes(argCount, argTypes) {
|
||
var a = new Array(argCount);
|
||
for (var i = 0; i < argCount; ++i) {
|
||
a[i] = requireRegisteredType(HEAPU32[argTypes + i * POINTER_SIZE >> 2], "parameter " + i);
|
||
}
|
||
return a;
|
||
}
|
||
var emval_registeredMethods = [];
|
||
function __emval_get_method_caller(argCount, argTypes) {
|
||
var types = emval_lookupTypes(argCount, argTypes);
|
||
var retType = types[0];
|
||
var signatureName = retType.name + "_$" + types.slice(1).map(function (t) {
|
||
return t.name;
|
||
}).join("_") + "$";
|
||
var returnId = emval_registeredMethods[signatureName];
|
||
if (returnId !== undefined) {
|
||
return returnId;
|
||
}
|
||
var params = ["retType"];
|
||
var args = [retType];
|
||
var argsList = "";
|
||
for (var i = 0; i < argCount - 1; ++i) {
|
||
argsList += (i !== 0 ? ", " : "") + "arg" + i;
|
||
params.push("argType" + i);
|
||
args.push(types[1 + i]);
|
||
}
|
||
var functionName = makeLegalFunctionName("methodCaller_" + signatureName);
|
||
var functionBody = "return function " + functionName + "(handle, name, destructors, args) {\n";
|
||
var offset = 0;
|
||
for (var i = 0; i < argCount - 1; ++i) {
|
||
functionBody += " var arg" + i + " = argType" + i + ".readValueFromPointer(args" + (offset ? "+" + offset : "") + ");\n";
|
||
offset += types[i + 1]["argPackAdvance"];
|
||
}
|
||
functionBody += " var rv = handle[name](" + argsList + ");\n";
|
||
for (var i = 0; i < argCount - 1; ++i) {
|
||
if (types[i + 1]["deleteObject"]) {
|
||
functionBody += " argType" + i + ".deleteObject(arg" + i + ");\n";
|
||
}
|
||
}
|
||
if (!retType.isVoid) {
|
||
functionBody += " return retType.toWireType(destructors, rv);\n";
|
||
}
|
||
functionBody += "};\n";
|
||
params.push(functionBody);
|
||
var invokerFunction = new_(Function, params).apply(null, args);
|
||
returnId = emval_addMethodCaller(invokerFunction);
|
||
emval_registeredMethods[signatureName] = returnId;
|
||
return returnId;
|
||
}
|
||
function _abort() {
|
||
abort("");
|
||
}
|
||
function _emscripten_memcpy_big(dest, src, num) {
|
||
HEAPU8.copyWithin(dest, src, src + num);
|
||
}
|
||
function abortOnCannotGrowMemory(requestedSize) {
|
||
abort("OOM");
|
||
}
|
||
function _emscripten_resize_heap(requestedSize) {
|
||
HEAPU8.length;
|
||
abortOnCannotGrowMemory();
|
||
}
|
||
var ENV = {};
|
||
function getExecutableName() {
|
||
return thisProgram || "./this.program";
|
||
}
|
||
function getEnvStrings() {
|
||
if (!getEnvStrings.strings) {
|
||
var lang = (typeof navigator == "object" && navigator.languages && navigator.languages[0] || "C").replace("-", "_") + ".UTF-8";
|
||
var env = {
|
||
"USER": "web_user",
|
||
"LOGNAME": "web_user",
|
||
"PATH": "/",
|
||
"PWD": "/",
|
||
"HOME": "/home/web_user",
|
||
"LANG": lang,
|
||
"_": getExecutableName()
|
||
};
|
||
for (var x in ENV) {
|
||
if (ENV[x] === undefined) delete env[x];else env[x] = ENV[x];
|
||
}
|
||
var strings = [];
|
||
for (var x in env) {
|
||
strings.push(x + "=" + env[x]);
|
||
}
|
||
getEnvStrings.strings = strings;
|
||
}
|
||
return getEnvStrings.strings;
|
||
}
|
||
function writeAsciiToMemory(str, buffer, dontAddNull) {
|
||
for (var i = 0; i < str.length; ++i) {
|
||
HEAP8[buffer++ >> 0] = str.charCodeAt(i);
|
||
}
|
||
if (!dontAddNull) HEAP8[buffer >> 0] = 0;
|
||
}
|
||
function _environ_get(__environ, environ_buf) {
|
||
var bufSize = 0;
|
||
getEnvStrings().forEach(function (string, i) {
|
||
var ptr = environ_buf + bufSize;
|
||
HEAPU32[__environ + i * 4 >> 2] = ptr;
|
||
writeAsciiToMemory(string, ptr);
|
||
bufSize += string.length + 1;
|
||
});
|
||
return 0;
|
||
}
|
||
function _environ_sizes_get(penviron_count, penviron_buf_size) {
|
||
var strings = getEnvStrings();
|
||
HEAPU32[penviron_count >> 2] = strings.length;
|
||
var bufSize = 0;
|
||
strings.forEach(function (string) {
|
||
bufSize += string.length + 1;
|
||
});
|
||
HEAPU32[penviron_buf_size >> 2] = bufSize;
|
||
return 0;
|
||
}
|
||
function _fd_close(fd) {
|
||
try {
|
||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||
FS.close(stream);
|
||
return 0;
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return e.errno;
|
||
}
|
||
}
|
||
function _fd_fdstat_get(fd, pbuf) {
|
||
try {
|
||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||
var type = stream.tty ? 2 : FS.isDir(stream.mode) ? 3 : FS.isLink(stream.mode) ? 7 : 4;
|
||
HEAP8[pbuf >> 0] = type;
|
||
return 0;
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return e.errno;
|
||
}
|
||
}
|
||
function doReadv(stream, iov, iovcnt, offset) {
|
||
var ret = 0;
|
||
for (var i = 0; i < iovcnt; i++) {
|
||
var ptr = HEAPU32[iov >> 2];
|
||
var len = HEAPU32[iov + 4 >> 2];
|
||
iov += 8;
|
||
var curr = FS.read(stream, HEAP8, ptr, len, offset);
|
||
if (curr < 0) return -1;
|
||
ret += curr;
|
||
if (curr < len) break;
|
||
}
|
||
return ret;
|
||
}
|
||
function _fd_read(fd, iov, iovcnt, pnum) {
|
||
try {
|
||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||
var num = doReadv(stream, iov, iovcnt);
|
||
HEAP32[pnum >> 2] = num;
|
||
return 0;
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return e.errno;
|
||
}
|
||
}
|
||
function convertI32PairToI53Checked(lo, hi) {
|
||
return hi + 2097152 >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN;
|
||
}
|
||
function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {
|
||
try {
|
||
var offset = convertI32PairToI53Checked(offset_low, offset_high);
|
||
if (isNaN(offset)) return 61;
|
||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||
FS.llseek(stream, offset, whence);
|
||
tempI64 = [stream.position >>> 0, (tempDouble = stream.position, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0)], HEAP32[newOffset >> 2] = tempI64[0], HEAP32[newOffset + 4 >> 2] = tempI64[1];
|
||
if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null;
|
||
return 0;
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return e.errno;
|
||
}
|
||
}
|
||
function doWritev(stream, iov, iovcnt, offset) {
|
||
var ret = 0;
|
||
for (var i = 0; i < iovcnt; i++) {
|
||
var ptr = HEAPU32[iov >> 2];
|
||
var len = HEAPU32[iov + 4 >> 2];
|
||
iov += 8;
|
||
var curr = FS.write(stream, HEAP8, ptr, len, offset);
|
||
if (curr < 0) return -1;
|
||
ret += curr;
|
||
}
|
||
return ret;
|
||
}
|
||
function _fd_write(fd, iov, iovcnt, pnum) {
|
||
try {
|
||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||
var num = doWritev(stream, iov, iovcnt);
|
||
HEAPU32[pnum >> 2] = num;
|
||
return 0;
|
||
} catch (e) {
|
||
if (typeof FS == "undefined" || !(e instanceof FS.ErrnoError)) throw e;
|
||
return e.errno;
|
||
}
|
||
}
|
||
function _setTempRet0(val) {
|
||
}
|
||
var FSNode = function (parent, name, mode, rdev) {
|
||
if (!parent) {
|
||
parent = this;
|
||
}
|
||
this.parent = parent;
|
||
this.mount = parent.mount;
|
||
this.mounted = null;
|
||
this.id = FS.nextInode++;
|
||
this.name = name;
|
||
this.mode = mode;
|
||
this.node_ops = {};
|
||
this.stream_ops = {};
|
||
this.rdev = rdev;
|
||
};
|
||
var readMode = 292 | 73;
|
||
var writeMode = 146;
|
||
Object.defineProperties(FSNode.prototype, {
|
||
read: {
|
||
get: function () {
|
||
return (this.mode & readMode) === readMode;
|
||
},
|
||
set: function (val) {
|
||
val ? this.mode |= readMode : this.mode &= ~readMode;
|
||
}
|
||
},
|
||
write: {
|
||
get: function () {
|
||
return (this.mode & writeMode) === writeMode;
|
||
},
|
||
set: function (val) {
|
||
val ? this.mode |= writeMode : this.mode &= ~writeMode;
|
||
}
|
||
},
|
||
isFolder: {
|
||
get: function () {
|
||
return FS.isDir(this.mode);
|
||
}
|
||
},
|
||
isDevice: {
|
||
get: function () {
|
||
return FS.isChrdev(this.mode);
|
||
}
|
||
}
|
||
});
|
||
FS.FSNode = FSNode;
|
||
FS.staticInit();
|
||
embind_init_charCodes();
|
||
BindingError = Module["BindingError"] = extendError(Error, "BindingError");
|
||
InternalError = Module["InternalError"] = extendError(Error, "InternalError");
|
||
init_ClassHandle();
|
||
init_embind();
|
||
init_RegisteredPointer();
|
||
UnboundTypeError = Module["UnboundTypeError"] = extendError(Error, "UnboundTypeError");
|
||
init_emval();
|
||
var asmLibraryArg = {
|
||
"s": ___cxa_allocate_exception,
|
||
"r": ___cxa_throw,
|
||
"C": ___syscall_fcntl64,
|
||
"v": ___syscall_openat,
|
||
"u": __embind_register_bigint,
|
||
"E": __embind_register_bool,
|
||
"m": __embind_register_class,
|
||
"l": __embind_register_class_constructor,
|
||
"d": __embind_register_class_function,
|
||
"D": __embind_register_emval,
|
||
"o": __embind_register_float,
|
||
"c": __embind_register_integer,
|
||
"b": __embind_register_memory_view,
|
||
"p": __embind_register_std_string,
|
||
"k": __embind_register_std_wstring,
|
||
"q": __embind_register_void,
|
||
"j": __emscripten_date_now,
|
||
"g": __emval_call_void_method,
|
||
"e": __emval_decref,
|
||
"f": __emval_get_method_caller,
|
||
"a": _abort,
|
||
"z": _emscripten_memcpy_big,
|
||
"i": _emscripten_resize_heap,
|
||
"x": _environ_get,
|
||
"y": _environ_sizes_get,
|
||
"n": _fd_close,
|
||
"w": _fd_fdstat_get,
|
||
"B": _fd_read,
|
||
"t": _fd_seek,
|
||
"A": _fd_write,
|
||
"h": _setTempRet0
|
||
};
|
||
createWasm();
|
||
Module["___wasm_call_ctors"] = function () {
|
||
return (Module["___wasm_call_ctors"] = Module["asm"]["G"]).apply(null, arguments);
|
||
};
|
||
var _free = Module["_free"] = function () {
|
||
return (_free = Module["_free"] = Module["asm"]["H"]).apply(null, arguments);
|
||
};
|
||
var _malloc = Module["_malloc"] = function () {
|
||
return (_malloc = Module["_malloc"] = Module["asm"]["I"]).apply(null, arguments);
|
||
};
|
||
var ___errno_location = Module["___errno_location"] = function () {
|
||
return (___errno_location = Module["___errno_location"] = Module["asm"]["K"]).apply(null, arguments);
|
||
};
|
||
var ___getTypeName = Module["___getTypeName"] = function () {
|
||
return (___getTypeName = Module["___getTypeName"] = Module["asm"]["L"]).apply(null, arguments);
|
||
};
|
||
Module["___embind_register_native_and_builtin_types"] = function () {
|
||
return (Module["___embind_register_native_and_builtin_types"] = Module["asm"]["M"]).apply(null, arguments);
|
||
};
|
||
var _emscripten_builtin_memalign = Module["_emscripten_builtin_memalign"] = function () {
|
||
return (_emscripten_builtin_memalign = Module["_emscripten_builtin_memalign"] = Module["asm"]["N"]).apply(null, arguments);
|
||
};
|
||
var ___cxa_is_pointer_type = Module["___cxa_is_pointer_type"] = function () {
|
||
return (___cxa_is_pointer_type = Module["___cxa_is_pointer_type"] = Module["asm"]["O"]).apply(null, arguments);
|
||
};
|
||
Module["dynCall_viiijj"] = function () {
|
||
return (Module["dynCall_viiijj"] = Module["asm"]["P"]).apply(null, arguments);
|
||
};
|
||
Module["dynCall_jij"] = function () {
|
||
return (Module["dynCall_jij"] = Module["asm"]["Q"]).apply(null, arguments);
|
||
};
|
||
Module["dynCall_jii"] = function () {
|
||
return (Module["dynCall_jii"] = Module["asm"]["R"]).apply(null, arguments);
|
||
};
|
||
Module["dynCall_jiji"] = function () {
|
||
return (Module["dynCall_jiji"] = Module["asm"]["S"]).apply(null, arguments);
|
||
};
|
||
Module["_ff_h264_cabac_tables"] = 215420;
|
||
var calledRun;
|
||
dependenciesFulfilled = function runCaller() {
|
||
if (!calledRun) run();
|
||
if (!calledRun) dependenciesFulfilled = runCaller;
|
||
};
|
||
function run(args) {
|
||
if (runDependencies > 0) {
|
||
return;
|
||
}
|
||
preRun();
|
||
if (runDependencies > 0) {
|
||
return;
|
||
}
|
||
function doRun() {
|
||
if (calledRun) return;
|
||
calledRun = true;
|
||
Module["calledRun"] = true;
|
||
if (ABORT) return;
|
||
initRuntime();
|
||
if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"]();
|
||
postRun();
|
||
}
|
||
if (Module["setStatus"]) {
|
||
Module["setStatus"]("Running...");
|
||
setTimeout(function () {
|
||
setTimeout(function () {
|
||
Module["setStatus"]("");
|
||
}, 1);
|
||
doRun();
|
||
}, 1);
|
||
} else {
|
||
doRun();
|
||
}
|
||
}
|
||
if (Module["preInit"]) {
|
||
if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]];
|
||
while (Module["preInit"].length > 0) {
|
||
Module["preInit"].pop()();
|
||
}
|
||
}
|
||
run();
|
||
module.exports = Module;
|
||
});
|
||
|
||
/**
|
||
* Common utilities
|
||
* @module glMatrix
|
||
*/
|
||
// Configuration Constants
|
||
var EPSILON = 0.000001;
|
||
var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array;
|
||
if (!Math.hypot) Math.hypot = function () {
|
||
var y = 0,
|
||
i = arguments.length;
|
||
|
||
while (i--) {
|
||
y += arguments[i] * arguments[i];
|
||
}
|
||
|
||
return Math.sqrt(y);
|
||
};
|
||
|
||
/**
|
||
* 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.
|
||
* @module mat4
|
||
*/
|
||
|
||
/**
|
||
* Creates a new identity mat4
|
||
*
|
||
* @returns {mat4} a new 4x4 matrix
|
||
*/
|
||
|
||
function create$1() {
|
||
var out = new ARRAY_TYPE(16);
|
||
|
||
if (ARRAY_TYPE != Float32Array) {
|
||
out[1] = 0;
|
||
out[2] = 0;
|
||
out[3] = 0;
|
||
out[4] = 0;
|
||
out[6] = 0;
|
||
out[7] = 0;
|
||
out[8] = 0;
|
||
out[9] = 0;
|
||
out[11] = 0;
|
||
out[12] = 0;
|
||
out[13] = 0;
|
||
out[14] = 0;
|
||
}
|
||
|
||
out[0] = 1;
|
||
out[5] = 1;
|
||
out[10] = 1;
|
||
out[15] = 1;
|
||
return out;
|
||
}
|
||
/**
|
||
* Set a mat4 to the identity matrix
|
||
*
|
||
* @param {mat4} out the receiving matrix
|
||
* @returns {mat4} out
|
||
*/
|
||
|
||
function identity(out) {
|
||
out[0] = 1;
|
||
out[1] = 0;
|
||
out[2] = 0;
|
||
out[3] = 0;
|
||
out[4] = 0;
|
||
out[5] = 1;
|
||
out[6] = 0;
|
||
out[7] = 0;
|
||
out[8] = 0;
|
||
out[9] = 0;
|
||
out[10] = 1;
|
||
out[11] = 0;
|
||
out[12] = 0;
|
||
out[13] = 0;
|
||
out[14] = 0;
|
||
out[15] = 1;
|
||
return out;
|
||
}
|
||
/**
|
||
* Generates a orthogonal projection matrix with the given bounds.
|
||
* The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
|
||
* which matches WebGL/OpenGL's clip volume.
|
||
*
|
||
* @param {mat4} out mat4 frustum matrix will be written into
|
||
* @param {number} left Left bound of the frustum
|
||
* @param {number} right Right bound of the frustum
|
||
* @param {number} bottom Bottom bound of the frustum
|
||
* @param {number} top Top bound of the frustum
|
||
* @param {number} near Near bound of the frustum
|
||
* @param {number} far Far bound of the frustum
|
||
* @returns {mat4} out
|
||
*/
|
||
|
||
function orthoNO(out, left, right, bottom, top, near, far) {
|
||
var lr = 1 / (left - right);
|
||
var bt = 1 / (bottom - top);
|
||
var nf = 1 / (near - far);
|
||
out[0] = -2 * lr;
|
||
out[1] = 0;
|
||
out[2] = 0;
|
||
out[3] = 0;
|
||
out[4] = 0;
|
||
out[5] = -2 * bt;
|
||
out[6] = 0;
|
||
out[7] = 0;
|
||
out[8] = 0;
|
||
out[9] = 0;
|
||
out[10] = 2 * nf;
|
||
out[11] = 0;
|
||
out[12] = (left + right) * lr;
|
||
out[13] = (top + bottom) * bt;
|
||
out[14] = (far + near) * nf;
|
||
out[15] = 1;
|
||
return out;
|
||
}
|
||
/**
|
||
* Alias for {@link mat4.orthoNO}
|
||
* @function
|
||
*/
|
||
|
||
var ortho = orthoNO;
|
||
/**
|
||
* Generates a look-at matrix with the given eye position, focal point, and up axis.
|
||
* If you want a matrix that actually makes an object look at another object, you should use targetTo instead.
|
||
*
|
||
* @param {mat4} out mat4 frustum matrix will be written into
|
||
* @param {ReadonlyVec3} eye Position of the viewer
|
||
* @param {ReadonlyVec3} center Point the viewer is looking at
|
||
* @param {ReadonlyVec3} up vec3 pointing up
|
||
* @returns {mat4} out
|
||
*/
|
||
|
||
function lookAt(out, eye, center, up) {
|
||
var x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
|
||
var eyex = eye[0];
|
||
var eyey = eye[1];
|
||
var eyez = eye[2];
|
||
var upx = up[0];
|
||
var upy = up[1];
|
||
var upz = up[2];
|
||
var centerx = center[0];
|
||
var centery = center[1];
|
||
var centerz = center[2];
|
||
|
||
if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) {
|
||
return identity(out);
|
||
}
|
||
|
||
z0 = eyex - centerx;
|
||
z1 = eyey - centery;
|
||
z2 = eyez - centerz;
|
||
len = 1 / Math.hypot(z0, z1, z2);
|
||
z0 *= len;
|
||
z1 *= len;
|
||
z2 *= len;
|
||
x0 = upy * z2 - upz * z1;
|
||
x1 = upz * z0 - upx * z2;
|
||
x2 = upx * z1 - upy * z0;
|
||
len = Math.hypot(x0, x1, x2);
|
||
|
||
if (!len) {
|
||
x0 = 0;
|
||
x1 = 0;
|
||
x2 = 0;
|
||
} else {
|
||
len = 1 / len;
|
||
x0 *= len;
|
||
x1 *= len;
|
||
x2 *= len;
|
||
}
|
||
|
||
y0 = z1 * x2 - z2 * x1;
|
||
y1 = z2 * x0 - z0 * x2;
|
||
y2 = z0 * x1 - z1 * x0;
|
||
len = Math.hypot(y0, y1, y2);
|
||
|
||
if (!len) {
|
||
y0 = 0;
|
||
y1 = 0;
|
||
y2 = 0;
|
||
} else {
|
||
len = 1 / len;
|
||
y0 *= len;
|
||
y1 *= len;
|
||
y2 *= len;
|
||
}
|
||
|
||
out[0] = x0;
|
||
out[1] = y0;
|
||
out[2] = z0;
|
||
out[3] = 0;
|
||
out[4] = x1;
|
||
out[5] = y1;
|
||
out[6] = z1;
|
||
out[7] = 0;
|
||
out[8] = x2;
|
||
out[9] = y2;
|
||
out[10] = z2;
|
||
out[11] = 0;
|
||
out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
|
||
out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
|
||
out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
|
||
out[15] = 1;
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 3 Dimensional Vector
|
||
* @module vec3
|
||
*/
|
||
|
||
/**
|
||
* Creates a new, empty vec3
|
||
*
|
||
* @returns {vec3} a new 3D vector
|
||
*/
|
||
|
||
function create() {
|
||
var out = new ARRAY_TYPE(3);
|
||
|
||
if (ARRAY_TYPE != Float32Array) {
|
||
out[0] = 0;
|
||
out[1] = 0;
|
||
out[2] = 0;
|
||
}
|
||
|
||
return out;
|
||
}
|
||
/**
|
||
* Creates a new vec3 initialized with the given values
|
||
*
|
||
* @param {Number} x X component
|
||
* @param {Number} y Y component
|
||
* @param {Number} z Z component
|
||
* @returns {vec3} a new 3D vector
|
||
*/
|
||
|
||
function fromValues(x, y, z) {
|
||
var out = new ARRAY_TYPE(3);
|
||
out[0] = x;
|
||
out[1] = y;
|
||
out[2] = z;
|
||
return out;
|
||
}
|
||
/**
|
||
* Perform some operation over an array of vec3s.
|
||
*
|
||
* @param {Array} a the array of vectors to iterate over
|
||
* @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
|
||
* @param {Number} offset Number of elements to skip at the beginning of the array
|
||
* @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
|
||
* @param {Function} fn Function to call for each vector in the array
|
||
* @param {Object} [arg] additional argument to pass to fn
|
||
* @returns {Array} a
|
||
* @function
|
||
*/
|
||
|
||
(function () {
|
||
var vec = create();
|
||
return function (a, stride, offset, count, fn, arg) {
|
||
var i, l;
|
||
|
||
if (!stride) {
|
||
stride = 3;
|
||
}
|
||
|
||
if (!offset) {
|
||
offset = 0;
|
||
}
|
||
|
||
if (count) {
|
||
l = Math.min(count * stride + offset, a.length);
|
||
} else {
|
||
l = a.length;
|
||
}
|
||
|
||
for (i = offset; i < l; i += stride) {
|
||
vec[0] = a[i];
|
||
vec[1] = a[i + 1];
|
||
vec[2] = a[i + 2];
|
||
fn(vec, vec, arg);
|
||
a[i] = vec[0];
|
||
a[i + 1] = vec[1];
|
||
a[i + 2] = vec[2];
|
||
}
|
||
|
||
return a;
|
||
};
|
||
})();
|
||
|
||
var createWebGL = ((gl, openWebglAlignment) => {
|
||
const vertexShaderScript = `
|
||
attribute vec4 aVertexPosition;
|
||
attribute vec2 aTexturePosition;
|
||
uniform mat4 uModelMatrix;
|
||
uniform mat4 uViewMatrix;
|
||
uniform mat4 uProjectionMatrix;
|
||
varying lowp vec2 vTexturePosition;
|
||
void main(void) {
|
||
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * aVertexPosition;
|
||
vTexturePosition = aTexturePosition;
|
||
}
|
||
`;
|
||
const fragmentShaderScript = `
|
||
precision highp float;
|
||
varying highp vec2 vTexturePosition;
|
||
uniform int isyuv;
|
||
uniform sampler2D rgbaTexture;
|
||
uniform sampler2D yTexture;
|
||
uniform sampler2D uTexture;
|
||
uniform sampler2D vTexture;
|
||
|
||
const mat4 YUV2RGB = mat4( 1.1643828125, 0, 1.59602734375, -.87078515625,
|
||
1.1643828125, -.39176171875, -.81296875, .52959375,
|
||
1.1643828125, 2.017234375, 0, -1.081390625,
|
||
0, 0, 0, 1);
|
||
|
||
|
||
void main(void) {
|
||
|
||
if (isyuv>0) {
|
||
|
||
highp float y = texture2D(yTexture, vTexturePosition).r;
|
||
highp float u = texture2D(uTexture, vTexturePosition).r;
|
||
highp float v = texture2D(vTexture, vTexturePosition).r;
|
||
gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;
|
||
|
||
} else {
|
||
gl_FragColor = texture2D(rgbaTexture, vTexturePosition);
|
||
}
|
||
}
|
||
`;
|
||
if (openWebglAlignment) {
|
||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||
}
|
||
const shaderProgram = _initShaderProgram();
|
||
const _programInfo = {
|
||
program: shaderProgram,
|
||
attribLocations: {
|
||
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
|
||
texturePosition: gl.getAttribLocation(shaderProgram, 'aTexturePosition')
|
||
},
|
||
uniformLocations: {
|
||
projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
|
||
modelMatrix: gl.getUniformLocation(shaderProgram, 'uModelMatrix'),
|
||
viewMatrix: gl.getUniformLocation(shaderProgram, 'uViewMatrix'),
|
||
rgbatexture: gl.getUniformLocation(shaderProgram, 'rgbaTexture'),
|
||
ytexture: gl.getUniformLocation(shaderProgram, 'yTexture'),
|
||
utexture: gl.getUniformLocation(shaderProgram, 'uTexture'),
|
||
vtexture: gl.getUniformLocation(shaderProgram, 'vTexture'),
|
||
isyuv: gl.getUniformLocation(shaderProgram, 'isyuv')
|
||
}
|
||
};
|
||
const _buffers = _initBuffers();
|
||
const _rgbatexture = _createTexture();
|
||
const _ytexture = _createTexture();
|
||
const _utexture = _createTexture();
|
||
const _vtexture = _createTexture();
|
||
function _initBuffers() {
|
||
// Create a buffer for the cube's vertex positions.
|
||
const positionBuffer = gl.createBuffer();
|
||
|
||
// Select the positionBuffer as the one to apply buffer
|
||
// operations to from here out.
|
||
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||
|
||
// Now create an array of positions for the cube.
|
||
|
||
const positions = [
|
||
// Front face
|
||
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0];
|
||
|
||
// Now pass the list of positions into WebGL to build the
|
||
// shape. We do this by creating a Float32Array from the
|
||
// JavaScript array, then use it to fill the current buffer.
|
||
|
||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
|
||
|
||
// Now set up the colors for the faces. We'll use solid colors
|
||
// for each face.
|
||
|
||
// const facePos = [
|
||
// [0.0, 0.0],
|
||
// [1.0, 0.0],
|
||
// [1.0, 1.0],
|
||
// [0.0, 1.0]
|
||
// ];
|
||
|
||
const facePos = [[0.0, 1.0], [1.0, 1.0], [1.0, 0.0], [0.0, 0.0]];
|
||
|
||
// Convert the array of colors into a table for all the vertices.
|
||
|
||
var texturePos = [];
|
||
texturePos = texturePos.concat(...facePos);
|
||
const texpositionBuffer = gl.createBuffer();
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, texpositionBuffer);
|
||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texturePos), gl.STATIC_DRAW);
|
||
|
||
// Build the element array buffer; this specifies the indices
|
||
// into the vertex arrays for each face's vertices.
|
||
|
||
const indexBuffer = gl.createBuffer();
|
||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||
|
||
// This array defines each face as two triangles, using the
|
||
// indices into the vertex array to specify each triangle's
|
||
// position.
|
||
|
||
const indices = [0, 1, 2, 0, 2, 3];
|
||
|
||
// Now send the element array to GL
|
||
|
||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
|
||
return {
|
||
position: positionBuffer,
|
||
texPosition: texpositionBuffer,
|
||
indices: indexBuffer
|
||
};
|
||
}
|
||
function _createTexture() {
|
||
let texture = gl.createTexture();
|
||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||
return texture;
|
||
}
|
||
function _loadShader(type, source) {
|
||
const shader = gl.createShader(type);
|
||
|
||
// Send the source to the shader object
|
||
|
||
gl.shaderSource(shader, source);
|
||
|
||
// Compile the shader program
|
||
|
||
gl.compileShader(shader);
|
||
|
||
// See if it compiled successfully
|
||
|
||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||
console.log('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
|
||
gl.deleteShader(shader);
|
||
return null;
|
||
}
|
||
return shader;
|
||
}
|
||
function _initShaderProgram() {
|
||
const vertexShader = _loadShader(gl.VERTEX_SHADER, vertexShaderScript);
|
||
const fragmentShader = _loadShader(gl.FRAGMENT_SHADER, fragmentShaderScript);
|
||
|
||
// Create the shader program
|
||
|
||
const shaderProgram = gl.createProgram();
|
||
gl.attachShader(shaderProgram, vertexShader);
|
||
gl.attachShader(shaderProgram, fragmentShader);
|
||
gl.linkProgram(shaderProgram);
|
||
|
||
// If creating the shader program failed, alert
|
||
|
||
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
||
console.log('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
|
||
return null;
|
||
}
|
||
return shaderProgram;
|
||
}
|
||
function _drawScene(w, h) {
|
||
gl.viewport(0, 0, w, h);
|
||
gl.clearColor(0.0, 0.0, 0.0, 0.0); // Clear to black, fully opaque
|
||
gl.clearDepth(1.0); // Clear everything
|
||
gl.enable(gl.DEPTH_TEST); // Enable depth testing
|
||
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
|
||
|
||
// Clear the canvas before we start drawing on it.
|
||
|
||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||
const zNear = 0.1;
|
||
const zFar = 100.0;
|
||
const projectionMatrix = create$1();
|
||
ortho(projectionMatrix, -1, 1, -1, 1, zNear, zFar);
|
||
|
||
// Set the drawing position to the "identity" point, which is
|
||
// the center of the scene.
|
||
const modelMatrix = create$1();
|
||
identity(modelMatrix);
|
||
const viewMatrix = create$1();
|
||
lookAt(viewMatrix, fromValues(0, 0, 0), fromValues(0, 0, -1), fromValues(0, 1, 0));
|
||
|
||
// Tell WebGL how to pull out the positions from the position
|
||
// buffer into the vertexPosition attribute
|
||
{
|
||
const numComponents = 3;
|
||
const type = gl.FLOAT;
|
||
const normalize = false;
|
||
const stride = 0;
|
||
const offset = 0;
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, _buffers.position);
|
||
gl.vertexAttribPointer(_programInfo.attribLocations.vertexPosition, numComponents, type, normalize, stride, offset);
|
||
gl.enableVertexAttribArray(_programInfo.attribLocations.vertexPosition);
|
||
}
|
||
|
||
// Tell WebGL how to pull out the colors from the color buffer
|
||
// into the vertexColor attribute.
|
||
{
|
||
const numComponents = 2;
|
||
const type = gl.FLOAT;
|
||
const normalize = false;
|
||
const stride = 0;
|
||
const offset = 0;
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, _buffers.texPosition);
|
||
gl.vertexAttribPointer(_programInfo.attribLocations.texturePosition, numComponents, type, normalize, stride, offset);
|
||
gl.enableVertexAttribArray(_programInfo.attribLocations.texturePosition);
|
||
}
|
||
let rgbatextunit = 2;
|
||
let ytextunit = rgbatextunit + 1;
|
||
let utextunit = rgbatextunit + 2;
|
||
let vtextunit = rgbatextunit + 3;
|
||
gl.activeTexture(gl.TEXTURE0 + ytextunit);
|
||
gl.bindTexture(gl.TEXTURE_2D, _ytexture);
|
||
gl.activeTexture(gl.TEXTURE0 + utextunit);
|
||
gl.bindTexture(gl.TEXTURE_2D, _utexture);
|
||
gl.activeTexture(gl.TEXTURE0 + vtextunit);
|
||
gl.bindTexture(gl.TEXTURE_2D, _vtexture);
|
||
|
||
// Tell WebGL which indices to use to index the vertices
|
||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _buffers.indices);
|
||
|
||
// Tell WebGL to use our program when drawing
|
||
|
||
gl.useProgram(_programInfo.program);
|
||
|
||
// Set the shader uniforms
|
||
|
||
gl.uniformMatrix4fv(_programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
|
||
gl.uniformMatrix4fv(_programInfo.uniformLocations.modelMatrix, false, modelMatrix);
|
||
gl.uniformMatrix4fv(_programInfo.uniformLocations.viewMatrix, false, viewMatrix);
|
||
gl.uniform1i(_programInfo.uniformLocations.rgbatexture, rgbatextunit);
|
||
gl.uniform1i(_programInfo.uniformLocations.ytexture, ytextunit);
|
||
gl.uniform1i(_programInfo.uniformLocations.utexture, utextunit);
|
||
gl.uniform1i(_programInfo.uniformLocations.vtexture, vtextunit);
|
||
gl.uniform1i(_programInfo.uniformLocations.isyuv, 1);
|
||
{
|
||
const vertexCount = 6;
|
||
const type = gl.UNSIGNED_SHORT;
|
||
const offset = 0;
|
||
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
|
||
}
|
||
|
||
// Update the rotation for the next draw
|
||
}
|
||
|
||
return {
|
||
render: function (width, height, y, u, v) {
|
||
gl.activeTexture(gl.TEXTURE0);
|
||
gl.bindTexture(gl.TEXTURE_2D, _ytexture);
|
||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, y);
|
||
gl.activeTexture(gl.TEXTURE1);
|
||
gl.bindTexture(gl.TEXTURE_2D, _utexture);
|
||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, u);
|
||
gl.activeTexture(gl.TEXTURE2);
|
||
gl.bindTexture(gl.TEXTURE_2D, _vtexture);
|
||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, v);
|
||
_drawScene(width, height);
|
||
},
|
||
renderYUV: function (width, height, data) {
|
||
let y = data.slice(0, width * height);
|
||
let u = data.slice(width * height, width * height * 5 / 4);
|
||
let v = data.slice(width * height * 5 / 4, width * height * 3 / 2);
|
||
gl.activeTexture(gl.TEXTURE0);
|
||
gl.bindTexture(gl.TEXTURE_2D, _ytexture);
|
||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, y);
|
||
gl.activeTexture(gl.TEXTURE1);
|
||
gl.bindTexture(gl.TEXTURE_2D, _utexture);
|
||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, u);
|
||
gl.activeTexture(gl.TEXTURE2);
|
||
gl.bindTexture(gl.TEXTURE_2D, _vtexture);
|
||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, v);
|
||
_drawScene(width, height);
|
||
},
|
||
destroy: function () {
|
||
gl.deleteProgram(_programInfo.program);
|
||
gl.deleteBuffer(_buffers.position);
|
||
gl.deleteBuffer(_buffers.texPosition);
|
||
gl.deleteBuffer(_buffers.indices);
|
||
gl.deleteTexture(_rgbatexture);
|
||
gl.deleteTexture(_ytexture);
|
||
gl.deleteTexture(_utexture);
|
||
gl.deleteTexture(_vtexture);
|
||
}
|
||
};
|
||
});
|
||
|
||
// 播放协议
|
||
const PLAYER_PLAY_PROTOCOL = {
|
||
websocket: 1,
|
||
fetch: 2,
|
||
hls: 3,
|
||
webrtc: 4,
|
||
webTransport: 5
|
||
};
|
||
|
||
// 播放
|
||
const PLAY_TYPE = {
|
||
player: "player",
|
||
playbackTF: 'playbackTF'
|
||
};
|
||
const FILE_SUFFIX = {
|
||
mp4: 'mp4',
|
||
webm: 'webm'
|
||
};
|
||
const DEMUX_TYPE = {
|
||
flv: 'flv',
|
||
m7s: 'm7s',
|
||
hls: 'hls',
|
||
webrtc: 'webrtc',
|
||
webTransport: 'webTransport',
|
||
nakedFlow: 'nakedFlow'
|
||
};
|
||
const DEBUG_LEVEL = {
|
||
debug: 'debug',
|
||
warn: 'warn'
|
||
};
|
||
const PTZ_ACTIVE_EVENT_TYPE = {
|
||
click: 'click',
|
||
mouseDownAndUp: 'mouseDownAndUp'
|
||
};
|
||
const DEFAULT_PLAYBACK_FORWARD_MAX_RATE_DECODE_IFRAME = 4; // default playback forward max rate decode iframe
|
||
|
||
const FRAME_TS_MAX_DIFF = 1000 * 60 * 60; // 1 hour
|
||
|
||
const SIMD_H264_DECODE_MAX_WIDTH = 4080;
|
||
|
||
// default player options
|
||
const DEFAULT_PLAYER_OPTIONS = {
|
||
playType: PLAY_TYPE.player,
|
||
//
|
||
container: '',
|
||
//
|
||
videoBuffer: 1 * 1000,
|
||
// 1* 1000ms == 1 second
|
||
videoBufferDelay: 1 * 1000,
|
||
// 1 * 1000ms
|
||
networkDelay: 10 * 1000,
|
||
// 10 * 1000ms
|
||
isResize: true,
|
||
//
|
||
isFullResize: false,
|
||
// full resize
|
||
isFlv: false,
|
||
// flv
|
||
isHls: false,
|
||
// hls(inner)
|
||
isWebrtc: false,
|
||
// webrtc (inner)
|
||
isWebrtcForZLM: false,
|
||
// webrtc for ZLM (inner)
|
||
isNakedFlow: false,
|
||
// 是否是裸流(264、265)
|
||
debug: false,
|
||
// debug log
|
||
debugLevel: DEBUG_LEVEL.warn,
|
||
// log level
|
||
debugUuid: '',
|
||
// debug uuid (inner)
|
||
isMulti: false,
|
||
// 是否多实例播放
|
||
hotKey: false,
|
||
// 快捷键
|
||
loadingTimeout: 10,
|
||
// loading timeout
|
||
heartTimeout: 10,
|
||
// heart timeout
|
||
timeout: 10,
|
||
// second
|
||
pageVisibilityHiddenTimeout: 5 * 60,
|
||
// 5 * 60 = 5 minute
|
||
loadingTimeoutReplay: true,
|
||
// loading timeout replay
|
||
heartTimeoutReplay: true,
|
||
// heart timeout replay。
|
||
loadingTimeoutReplayTimes: 3,
|
||
// loading timeout replay fail times
|
||
heartTimeoutReplayTimes: 3,
|
||
// heart timeout replay fail times
|
||
heartTimeoutReplayUseLastFrameShow: false,
|
||
// heart timeout replay use last frame
|
||
supportDblclickFullscreen: false,
|
||
showBandwidth: false,
|
||
//
|
||
showPerformance: false,
|
||
// 是否显示性能面板
|
||
keepScreenOn: true,
|
||
isNotMute: false,
|
||
hasAudio: true,
|
||
hasVideo: true,
|
||
operateBtns: {
|
||
fullscreen: false,
|
||
screenshot: false,
|
||
play: false,
|
||
audio: false,
|
||
record: false,
|
||
ptz: false,
|
||
quality: false,
|
||
zoom: false,
|
||
close: false,
|
||
scale: false,
|
||
performance: false,
|
||
aiFace: false,
|
||
aiObject: false,
|
||
fullscreenFn: null,
|
||
fullscreenExitFn: null,
|
||
screenshotFn: null,
|
||
playFn: null,
|
||
pauseFn: null,
|
||
recordFn: null,
|
||
recordStopFn: null
|
||
},
|
||
extendOperateBtns: [],
|
||
contextmenuBtns: [],
|
||
watermarkConfig: {},
|
||
// 局部水印设置
|
||
controlAutoHide: false,
|
||
hasControl: false,
|
||
// 内部参数
|
||
loadingIcon: true,
|
||
loadingText: '',
|
||
background: '',
|
||
backgroundLoadingShow: false,
|
||
// 加载过程中是否显示背景
|
||
loadingBackground: '',
|
||
// 内部参数 重新播放过程中,显示播放失败前的最后一帧数据。
|
||
decoder: 'decoder-pro.js',
|
||
decoderWASM: '',
|
||
url: '',
|
||
// 内部参数
|
||
rotate: 0,
|
||
mirrorRotate: 'none',
|
||
// 镜像xx
|
||
playbackConfig: {
|
||
playList: [],
|
||
// {start:xx,end:xx}
|
||
fps: '',
|
||
// fps值
|
||
showControl: true,
|
||
showRateBtn: false,
|
||
rateConfig: [],
|
||
// 播放倍率切换,支持[{label:'正常',value:1},{label:'2倍',value:2}]
|
||
isCacheBeforeDecodeForFpsRender: false,
|
||
// rfs渲染时,是否在解码前缓存数据
|
||
uiUsePlaybackPause: false,
|
||
// ui上面是否使用 playbackPause 方法
|
||
isPlaybackPauseClearCache: true,
|
||
// playbackPause是否清除缓存数据
|
||
isUseFpsRender: false,
|
||
// 是否使用固定的fps渲染,如果设置的fps小于流推过来的,会造成内存堆积甚至溢出
|
||
isUseLocalCalculateTime: false,
|
||
// 是否使用本地时间来计算playback时间
|
||
localOneFrameTimestamp: 40,
|
||
// 一帧 40ms, isUseLocalCalculateTime 为 true 生效。
|
||
supportWheel: false // 是否支持滚动轴切换精度。
|
||
},
|
||
|
||
qualityConfig: [],
|
||
// 支持 ['高清','超清','4K']
|
||
defaultStreamQuality: '',
|
||
scaleConfig: ['拉伸', '缩放', '正常'],
|
||
// text: '',
|
||
forceNoOffscreen: true,
|
||
// 默认是不采用
|
||
hiddenAutoPause: false,
|
||
protocol: PLAYER_PLAY_PROTOCOL.fetch,
|
||
// 内部参数
|
||
demuxType: DEMUX_TYPE.flv,
|
||
// 内部参数
|
||
useWasm: false,
|
||
//wasm 解码 (inner)默认
|
||
useWCS: false,
|
||
//
|
||
useSIMD: true,
|
||
// pro 默认优先使用wasm simd解码,不支持则使用wasm解码
|
||
wcsUseVideoRender: true,
|
||
// wcs 是否使用 video 渲染
|
||
wasmUseVideoRender: true,
|
||
// wasm 用video标签渲染
|
||
mseUseCanvasRender: false,
|
||
//mse 用canvas标签渲染
|
||
hlsUseCanvasRender: false,
|
||
// hls 用canvas标签渲染
|
||
useMSE: false,
|
||
//
|
||
useOffscreen: false,
|
||
// 内部参数
|
||
mseDecodeErrorReplay: true,
|
||
// mse 解码失败重新播放
|
||
wcsDecodeErrorReplay: true,
|
||
// wcs 解码失败重新播放
|
||
wasmDecodeErrorReplay: true,
|
||
// 解码失败重新播放。
|
||
autoWasm: true,
|
||
// 自动降级到 wasm 模式
|
||
webglAlignmentErrorReplay: true,
|
||
// webgl对齐失败重新播放。
|
||
openWebglAlignment: false,
|
||
// https://github.com/langhuihui/jessibuca/issues/152
|
||
syncAudioAndVideo: false,
|
||
// 音视频同步
|
||
// playback config
|
||
playbackDelayTime: 1000,
|
||
// TF卡流播放延迟时间 Inner
|
||
playbackFps: 25,
|
||
// Inner
|
||
playbackForwardMaxRateDecodeIFrame: DEFAULT_PLAYBACK_FORWARD_MAX_RATE_DECODE_IFRAME,
|
||
// max rate render i frame , Inner
|
||
playbackCurrentTimeMove: true,
|
||
// Inner
|
||
|
||
useVideoRender: true,
|
||
// 使用video标签渲染
|
||
useCanvasRender: false,
|
||
// 使用canvas渲染
|
||
networkDelayTimeoutReplay: false,
|
||
// 网络延迟重连
|
||
recordType: FILE_SUFFIX.mp4,
|
||
checkFirstIFrame: true,
|
||
// 检查第一帧是否是I帧
|
||
nakedFlowFps: 25,
|
||
// 裸流fps
|
||
audioEngine: null,
|
||
// 音频引擎
|
||
isShowRecordingUI: true,
|
||
// 是否显示录制中
|
||
isShowZoomingUI: true,
|
||
// 是否显示缩放中
|
||
useFaceDetector: false,
|
||
// 使用人脸检测
|
||
useObjectDetector: false,
|
||
// 使用物体检测
|
||
ptzClickType: PTZ_ACTIVE_EVENT_TYPE.click,
|
||
// PTZ 点击类型
|
||
// 微信安卓音频播放块大小
|
||
// 计算规则 48000 * ms / 1000 = size,
|
||
// 48000 * 200 /1000 = 9600 播放时长200ms
|
||
// 48000 * 175 /1000 = 8400 播放时长175ms
|
||
// 48000 * 150 /1000 = 7200 播放时长150ms
|
||
// 48000 * 125 /1000 = 6000 播放时长125ms
|
||
// 48000 * 100 /1000 = 4800 播放时长100ms default
|
||
// 48000 * 50 /1000 = 2400 播放时长50ms
|
||
// 48000 * 25 /1000 = 1200 播放时长25ms
|
||
// 48000 * 10 /1000 = 480 播放时长10ms
|
||
weiXinInAndroidAudioBufferSize: 4800,
|
||
isCrypto: false,
|
||
// 是否加密
|
||
cryptoKey: '',
|
||
// 加密key
|
||
cryptoIV: '',
|
||
// 加密iv
|
||
cryptoKeyUrl: '',
|
||
// 加密key获取域名
|
||
autoResize: false,
|
||
// 自动调整大小 inner
|
||
useWebFullScreen: false,
|
||
// 使用web全屏(旋转播放器90度)(只会在移动端生效)
|
||
ptsMaxDiff: 60 * 60,
|
||
//单位(秒),默认值是1H
|
||
aiFaceDetectWidth: 192,
|
||
// 人脸检测宽度
|
||
aiObjectDetectWidth: 192,
|
||
// 物体检测宽度
|
||
videoRenderSupportScale: true,
|
||
// video渲染支持Scale
|
||
mediaSourceTsIsMaxDiffReplay: true // 当ts间隔超过最大值之后,重新播放
|
||
};
|
||
|
||
const WORKER_CMD_TYPE = {
|
||
init: 'init',
|
||
initVideo: 'initVideo',
|
||
render: 'render',
|
||
playAudio: 'playAudio',
|
||
initAudio: 'initAudio',
|
||
kBps: 'kBps',
|
||
decode: 'decode',
|
||
audioCode: 'audioCode',
|
||
videoCode: 'videoCode',
|
||
videoCodec: 'videoCodec',
|
||
videoNalu: 'videoNalu',
|
||
wasmError: 'wasmError',
|
||
workerFetch: 'workerFetch',
|
||
iframeIntervalTs: 'iframeIntervalTs',
|
||
workerEnd: 'workerEnd',
|
||
networkDelay: 'networkDelay',
|
||
playbackStreamVideoFps: 'playbackStreamVideoFps',
|
||
wasmDecodeVideoNoResponseError: 'wasmDecodeVideoNoResponseError',
|
||
simdH264DecodeVideoWidthIsTooLarge: 'simdH264DecodeVideoWidthIsTooLarge',
|
||
websocketOpen: 'websocketOpen',
|
||
closeEnd: 'closeEnd'
|
||
};
|
||
|
||
const MEDIA_TYPE = {
|
||
audio: 1,
|
||
video: 2
|
||
};
|
||
const FLV_MEDIA_TYPE = {
|
||
audio: 8,
|
||
video: 9,
|
||
scriptData: 18
|
||
};
|
||
const WORKER_SEND_TYPE = {
|
||
init: 'init',
|
||
decode: 'decode',
|
||
audioDecode: 'audioDecode',
|
||
videoDecode: 'videoDecode',
|
||
initAudioCodec: 'initAudioCodec',
|
||
initVideoCodec: 'initVideoCodec',
|
||
close: 'close',
|
||
updateConfig: 'updateConfig',
|
||
resetDecode: 'resetDecode',
|
||
clearBuffer: 'clearBuffer',
|
||
resetAudioDecode: 'resetAudioDecode',
|
||
resetVideoDecode: 'resetVideoDecode',
|
||
fetchStream: 'fetchStream',
|
||
sendWsMessage: 'sendWsMessage'
|
||
};
|
||
|
||
// inner events
|
||
const EVENTS = {
|
||
fullscreen: 'fullscreen$2',
|
||
webFullscreen: 'webFullscreen',
|
||
decoderWorkerInit: 'decoderWorkerInit',
|
||
play: 'play',
|
||
playing: 'playing',
|
||
pause: 'pause',
|
||
mute: 'mute',
|
||
load: 'load',
|
||
loading: 'loading',
|
||
zooming: 'zooming',
|
||
videoInfo: 'videoInfo',
|
||
timeUpdate: 'timeUpdate',
|
||
audioInfo: "audioInfo",
|
||
log: 'log',
|
||
error: "error",
|
||
kBps: 'kBps',
|
||
timeout: 'timeout',
|
||
delayTimeout: 'delayTimeout',
|
||
delayTimeoutRetryEnd: 'delayTimeoutRetryEnd',
|
||
loadingTimeout: 'loadingTimeout',
|
||
loadingTimeoutRetryEnd: 'loadingTimeoutRetryEnd',
|
||
stats: 'stats',
|
||
performance: "performance",
|
||
faceDetectActive: 'faceDetectActive',
|
||
objectDetectActive: 'objectDetectActive',
|
||
// record
|
||
record: 'record',
|
||
recording: 'recording',
|
||
recordingTimestamp: 'recordingTimestamp',
|
||
recordStart: 'recordStart',
|
||
recordEnd: 'recordEnd',
|
||
recordCreateError: 'recordCreateError',
|
||
recordBlob: 'recordBlob',
|
||
buffer: 'buffer',
|
||
videoFrame: 'videoFrame',
|
||
start: 'start',
|
||
metadata: 'metadata',
|
||
resize: 'resize',
|
||
volumechange: 'volumechange',
|
||
destroy: 'destroy',
|
||
beforeDestroy: 'beforeDestroy',
|
||
// stream
|
||
streamEnd: 'streamEnd',
|
||
streamRate: 'streamRate',
|
||
streamAbps: 'streamAbps',
|
||
streamVbps: 'streamVbps',
|
||
streamDts: 'streamDts',
|
||
streamSuccess: 'streamSuccess',
|
||
streamMessage: 'streamMessage',
|
||
streamError: 'streamError',
|
||
streamStats: 'streamStats',
|
||
// MSE
|
||
mseSourceOpen: 'mseSourceOpen',
|
||
mseSourceClose: 'mseSourceClose',
|
||
mseSourceended: 'mseSourceended',
|
||
mseSourceBufferError: 'mseSourceBufferError',
|
||
mseSourceBufferBusy: 'mseSourceBufferBusy',
|
||
mseSourceBufferFull: 'mseSourceBufferFull',
|
||
// VIDEO
|
||
videoWaiting: 'videoWaiting',
|
||
videoTimeUpdate: 'videoTimeUpdate',
|
||
videoSyncAudio: 'videoSyncAudio',
|
||
//
|
||
playToRenderTimes: 'playToRenderTimes',
|
||
playbackTime: 'playbackTime',
|
||
playbackTimestamp: 'playbackTimestamp',
|
||
playbackPrecision: 'playbackPrecision',
|
||
playbackJustTime: 'playbackJustTime',
|
||
playbackStats: 'playbackStats',
|
||
playbackSeek: 'playbackSeek',
|
||
playbackPause: 'playbackPause',
|
||
playbackPauseOrResume: 'playbackPauseOrResume',
|
||
playbackRateChange: 'playbackRateChange',
|
||
playbackPreRateChange: 'playbackPreRateChange',
|
||
ptz: 'ptz',
|
||
streamQualityChange: 'streamQualityChange',
|
||
visibilityChange: "visibilityChange",
|
||
netBuf: 'netBuf',
|
||
close: 'close',
|
||
networkDelayTimeout: 'networkDelayTimeout',
|
||
togglePerformancePanel: 'togglePerformancePanel',
|
||
viewResizeChange: 'viewResizeChange',
|
||
flvDemuxBufferSizeTooLarge: 'flvDemuxBufferSizeTooLarge',
|
||
// talk
|
||
talkGetUserMediaSuccess: 'talkGetUserMediaSuccess',
|
||
talkGetUserMediaFail: 'talkGetUserMediaFail',
|
||
talkGetUserMediaTimeout: 'talkGetUserMediaTimeout',
|
||
talkStreamStart: 'talkStreamStart',
|
||
talkStreamOpen: 'talkStreamOpen',
|
||
talkStreamClose: 'talkStreamClose',
|
||
talkStreamError: 'talkStreamError',
|
||
talkStreamInactive: 'talkStreamInactive',
|
||
webrtcDisconnect: 'webrtcDisconnect',
|
||
webrtcFailed: 'webrtcFailed',
|
||
webrtcClosed: 'webrtcClosed',
|
||
// crash
|
||
crashLog: 'crashLog',
|
||
// dom
|
||
focus: 'focus',
|
||
blur: 'blur',
|
||
visibilityHiddenTimeout: 'visibilityHiddenTimeout',
|
||
// websocket
|
||
websocketOpen: 'websocketOpen',
|
||
websocketClose: 'websocketClose',
|
||
websocketError: 'websocketError',
|
||
websocketMessage: 'websocketMessage',
|
||
// ai
|
||
|
||
aiObjectDetectorInfo: 'aiObjectDetectorInfo',
|
||
aiFaceDetector: 'aiFaceDetector',
|
||
//
|
||
playFailedAndPaused: 'playFailedAndPaused'
|
||
};
|
||
const EVENTS_ERROR = {
|
||
playError: 'playIsNotPauseOrUrlIsNull',
|
||
fetchError: "fetchError",
|
||
websocketError: 'websocketError',
|
||
webcodecsH265NotSupport: 'webcodecsH265NotSupport',
|
||
webcodecsDecodeError: 'webcodecsDecodeError',
|
||
mediaSourceH265NotSupport: 'mediaSourceH265NotSupport',
|
||
mediaSourceDecoderConfigurationError: 'mediaSourceDecoderConfigurationError',
|
||
mediaSourceFull: EVENTS.mseSourceBufferFull,
|
||
mseSourceBufferError: EVENTS.mseSourceBufferError,
|
||
mediaSourceAppendBufferError: 'mediaSourceAppendBufferError',
|
||
mediaSourceBufferListLarge: 'mediaSourceBufferListLarge',
|
||
mediaSourceAppendBufferEndTimeout: 'mediaSourceAppendBufferEndTimeout',
|
||
mediaSourceTsIsMaxDiff: 'mediaSourceTsIsMaxDiff',
|
||
wasmDecodeError: 'wasmDecodeError',
|
||
hlsError: 'hlsError',
|
||
webrtcError: 'webrtcError',
|
||
webglAlignmentError: 'webglAlignmentError',
|
||
webcodecsWidthOrHeightChange: 'webcodecsWidthOrHeightChange',
|
||
tallWebsocketClosedByError: 'tallWebsocketClosedByError',
|
||
flvDemuxBufferSizeTooLarge: EVENTS.flvDemuxBufferSizeTooLarge,
|
||
wasmDecodeVideoNoResponseError: 'wasmDecodeVideoNoResponseError',
|
||
audioChannelError: 'audioChannelError',
|
||
simdH264DecodeVideoWidthIsTooLarge: 'simdH264DecodeVideoWidthIsTooLarge'
|
||
};
|
||
const WEBSOCKET_STATUS_CODE = {
|
||
connecting: 0,
|
||
open: 1,
|
||
closing: 2,
|
||
closed: 3
|
||
};
|
||
|
||
const VIDEO_ENC_CODE = {
|
||
h264: 7,
|
||
h265: 12
|
||
};
|
||
const VIDEO_ENC_TYPE_SHOW = {
|
||
h264: 'H264(AVC)',
|
||
h265: 'H265(HEVC)'
|
||
};
|
||
const AUDIO_ENC_CODE = {
|
||
AAC: 10,
|
||
ALAW: 7,
|
||
MULAW: 8,
|
||
MP3: 2
|
||
};
|
||
const H264_NAL_TYPE = {
|
||
sps: 7,
|
||
pps: 8,
|
||
iFrame: 5,
|
||
kUnspecified: 0,
|
||
kSliceNonIDR: 1,
|
||
kSliceDPA: 2,
|
||
kSliceDPB: 3,
|
||
kSliceDPC: 4,
|
||
kSliceIDR: 5,
|
||
kSliceSEI: 6,
|
||
kSliceSPS: 7,
|
||
kSlicePPS: 8,
|
||
kSliceAUD: 9,
|
||
kEndOfSequence: 10,
|
||
kEndOfStream: 11,
|
||
kFiller: 12,
|
||
kSPSExt: 13,
|
||
kReserved0: 14
|
||
};
|
||
const H265_NAL_TYPE = {
|
||
vps: 32,
|
||
// 语义为视频参数集
|
||
sps: 33,
|
||
// 语义为序列参数集
|
||
pps: 34,
|
||
// 语义为图像参数集
|
||
sei: 39 //SEI 语义为补充增强信息
|
||
//iFrame: 19, // 语义为可能有RADL图像的IDR图像的SS编码数据 IDR
|
||
//pFrame: 1, // 语义为被参考的后置图像,且非TSA、非STSA的SS编码数据
|
||
//nLp: 20, // kSliceIDR_N_LP
|
||
};
|
||
const ENCODED_VIDEO_TYPE = {
|
||
key: 'key',
|
||
delta: 'delta'
|
||
};
|
||
const FETCH_ERROR = {
|
||
abortError: 'The user aborted a request',
|
||
abortError2: 'AbortError',
|
||
abort: 'AbortError'
|
||
};
|
||
const AVC_PACKET_TYPE = {
|
||
sequenceHeader: 0,
|
||
nalu: 1
|
||
};
|
||
const FRAME_TYPE = {
|
||
keyFrame: 1,
|
||
interFrame: 2
|
||
};
|
||
|
||
var screenfull = createCommonjsModule(function (module) {
|
||
/*!
|
||
* screenfull
|
||
* v5.1.0 - 2020-12-24
|
||
* (c) Sindre Sorhus; MIT License
|
||
*/
|
||
(function () {
|
||
|
||
var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {};
|
||
var isCommonjs = module.exports;
|
||
|
||
var fn = (function () {
|
||
var val;
|
||
|
||
var fnMap = [
|
||
[
|
||
'requestFullscreen',
|
||
'exitFullscreen',
|
||
'fullscreenElement',
|
||
'fullscreenEnabled',
|
||
'fullscreenchange',
|
||
'fullscreenerror'
|
||
],
|
||
// New WebKit
|
||
[
|
||
'webkitRequestFullscreen',
|
||
'webkitExitFullscreen',
|
||
'webkitFullscreenElement',
|
||
'webkitFullscreenEnabled',
|
||
'webkitfullscreenchange',
|
||
'webkitfullscreenerror'
|
||
|
||
],
|
||
// Old WebKit
|
||
[
|
||
'webkitRequestFullScreen',
|
||
'webkitCancelFullScreen',
|
||
'webkitCurrentFullScreenElement',
|
||
'webkitCancelFullScreen',
|
||
'webkitfullscreenchange',
|
||
'webkitfullscreenerror'
|
||
|
||
],
|
||
[
|
||
'mozRequestFullScreen',
|
||
'mozCancelFullScreen',
|
||
'mozFullScreenElement',
|
||
'mozFullScreenEnabled',
|
||
'mozfullscreenchange',
|
||
'mozfullscreenerror'
|
||
],
|
||
[
|
||
'msRequestFullscreen',
|
||
'msExitFullscreen',
|
||
'msFullscreenElement',
|
||
'msFullscreenEnabled',
|
||
'MSFullscreenChange',
|
||
'MSFullscreenError'
|
||
]
|
||
];
|
||
|
||
var i = 0;
|
||
var l = fnMap.length;
|
||
var ret = {};
|
||
|
||
for (; i < l; i++) {
|
||
val = fnMap[i];
|
||
if (val && val[1] in document) {
|
||
for (i = 0; i < val.length; i++) {
|
||
ret[fnMap[0][i]] = val[i];
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
})();
|
||
|
||
var eventNameMap = {
|
||
change: fn.fullscreenchange,
|
||
error: fn.fullscreenerror
|
||
};
|
||
|
||
var screenfull = {
|
||
request: function (element, options) {
|
||
return new Promise(function (resolve, reject) {
|
||
var onFullScreenEntered = function () {
|
||
this.off('change', onFullScreenEntered);
|
||
resolve();
|
||
}.bind(this);
|
||
|
||
this.on('change', onFullScreenEntered);
|
||
|
||
element = element || document.documentElement;
|
||
|
||
var returnPromise = element[fn.requestFullscreen](options);
|
||
|
||
if (returnPromise instanceof Promise) {
|
||
returnPromise.then(onFullScreenEntered).catch(reject);
|
||
}
|
||
}.bind(this));
|
||
},
|
||
exit: function () {
|
||
return new Promise(function (resolve, reject) {
|
||
if (!this.isFullscreen) {
|
||
resolve();
|
||
return;
|
||
}
|
||
|
||
var onFullScreenExit = function () {
|
||
this.off('change', onFullScreenExit);
|
||
resolve();
|
||
}.bind(this);
|
||
|
||
this.on('change', onFullScreenExit);
|
||
|
||
var returnPromise = document[fn.exitFullscreen]();
|
||
|
||
if (returnPromise instanceof Promise) {
|
||
returnPromise.then(onFullScreenExit).catch(reject);
|
||
}
|
||
}.bind(this));
|
||
},
|
||
toggle: function (element, options) {
|
||
return this.isFullscreen ? this.exit() : this.request(element, options);
|
||
},
|
||
onchange: function (callback) {
|
||
this.on('change', callback);
|
||
},
|
||
onerror: function (callback) {
|
||
this.on('error', callback);
|
||
},
|
||
on: function (event, callback) {
|
||
var eventName = eventNameMap[event];
|
||
if (eventName) {
|
||
document.addEventListener(eventName, callback, false);
|
||
}
|
||
},
|
||
off: function (event, callback) {
|
||
var eventName = eventNameMap[event];
|
||
if (eventName) {
|
||
document.removeEventListener(eventName, callback, false);
|
||
}
|
||
},
|
||
raw: fn
|
||
};
|
||
|
||
if (!fn) {
|
||
if (isCommonjs) {
|
||
module.exports = {isEnabled: false};
|
||
} else {
|
||
window.screenfull = {isEnabled: false};
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
Object.defineProperties(screenfull, {
|
||
isFullscreen: {
|
||
get: function () {
|
||
return Boolean(document[fn.fullscreenElement]);
|
||
}
|
||
},
|
||
element: {
|
||
enumerable: true,
|
||
get: function () {
|
||
return document[fn.fullscreenElement];
|
||
}
|
||
},
|
||
isEnabled: {
|
||
enumerable: true,
|
||
get: function () {
|
||
// Coerce to boolean in case of old WebKit
|
||
return Boolean(document[fn.fullscreenEnabled]);
|
||
}
|
||
}
|
||
});
|
||
|
||
if (isCommonjs) {
|
||
module.exports = screenfull;
|
||
} else {
|
||
window.screenfull = screenfull;
|
||
}
|
||
})();
|
||
});
|
||
screenfull.isEnabled;
|
||
|
||
/**
|
||
*
|
||
* @param payload
|
||
* @returns {boolean}
|
||
*/
|
||
function isAacCodecPacket(payload) {
|
||
return payload[0] >> 4 === AUDIO_ENC_CODE.AAC && payload[1] === AVC_PACKET_TYPE.sequenceHeader;
|
||
}
|
||
|
||
function now() {
|
||
return new Date().getTime();
|
||
}
|
||
(() => {
|
||
try {
|
||
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
|
||
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
|
||
if (module instanceof WebAssembly.Module) return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
|
||
}
|
||
} catch (e) {}
|
||
return false;
|
||
})();
|
||
function getNowTime() {
|
||
if (performance && typeof performance.now === 'function') {
|
||
return performance.now();
|
||
}
|
||
return Date.now();
|
||
}
|
||
function calculationRate(callback) {
|
||
let totalSize = 0;
|
||
let lastTime = getNowTime();
|
||
return size => {
|
||
if (!isNumber(size)) {
|
||
return;
|
||
}
|
||
totalSize += size;
|
||
const thisTime = getNowTime();
|
||
const diffTime = thisTime - lastTime;
|
||
if (diffTime >= 1000) {
|
||
callback(totalSize / diffTime * 1000);
|
||
lastTime = thisTime;
|
||
totalSize = 0;
|
||
}
|
||
};
|
||
}
|
||
function formatVideoDecoderConfigure(avcC) {
|
||
let codecArray = avcC.subarray(1, 4);
|
||
let codecString = "avc1.";
|
||
for (let j = 0; j < 3; j++) {
|
||
let h = codecArray[j].toString(16);
|
||
if (h.length < 2) {
|
||
h = "0" + h;
|
||
}
|
||
codecString += h;
|
||
}
|
||
return {
|
||
codec: codecString,
|
||
description: avcC
|
||
};
|
||
}
|
||
function isNumber(value) {
|
||
const toString = Object.prototype.toString;
|
||
return toString.call(value) === "[object Number]";
|
||
}
|
||
function isEmpty(value) {
|
||
return value === null || value === undefined;
|
||
}
|
||
function isFunction(fn) {
|
||
return typeof fn === "function";
|
||
}
|
||
function checkNaluType(naluBuffer) {
|
||
let result = null;
|
||
let type = naluBuffer[0] & 0b0001_1111;
|
||
if (type === H264_NAL_TYPE.sps || type === H264_NAL_TYPE.pps) {
|
||
result = VIDEO_ENC_TYPE_SHOW.h264;
|
||
}
|
||
if (!result) {
|
||
type = (naluBuffer[0] & 0x7E) >> 1;
|
||
if (type === H265_NAL_TYPE.vps || type === H265_NAL_TYPE.sps || type === H265_NAL_TYPE.pps) {
|
||
result = VIDEO_ENC_TYPE_SHOW.h265;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
function supportWritableStream() {
|
||
return typeof WritableStream !== 'undefined';
|
||
}
|
||
function calcStreamFpsByBufferList(bufferList, type) {
|
||
if (type) {
|
||
bufferList = bufferList.filter(item => item.type === type);
|
||
}
|
||
let firstItem = bufferList[0];
|
||
let oneSecondLength = null;
|
||
let nextIndex = 1;
|
||
if (bufferList.length > 0) {
|
||
let nextItem = bufferList[1];
|
||
if (nextItem && nextItem.ts - firstItem.ts > 100000) {
|
||
firstItem = nextItem;
|
||
nextIndex = 2;
|
||
}
|
||
}
|
||
if (firstItem) {
|
||
// next start
|
||
for (let i = nextIndex; i < bufferList.length; i++) {
|
||
let tempItem = bufferList[i];
|
||
if (type && tempItem.type !== type) {
|
||
tempItem = null;
|
||
}
|
||
if (tempItem) {
|
||
const diff = tempItem.ts - firstItem.ts;
|
||
if (diff >= 1000) {
|
||
const prevTempItem = bufferList[i - 1];
|
||
const diff2 = prevTempItem.ts - firstItem.ts;
|
||
if (diff2 < 1000) {
|
||
oneSecondLength = i + 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return oneSecondLength;
|
||
}
|
||
function isFetchSuccess(res) {
|
||
return res.ok && res.status >= 200 && res.status <= 299;
|
||
}
|
||
function clone(obj) {
|
||
let result = '';
|
||
//
|
||
if (typeof obj === 'object') {
|
||
try {
|
||
result = JSON.stringify(obj);
|
||
result = JSON.parse(result);
|
||
} catch (e) {
|
||
result = obj;
|
||
}
|
||
} else {
|
||
result = obj;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @returns {object:DEFAULT_PLAYER_OPTIONS}
|
||
*/
|
||
function getDefaultPlayerOptions() {
|
||
return clone(DEFAULT_PLAYER_OPTIONS);
|
||
}
|
||
|
||
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||
return new (P || (P = Promise))(function (resolve, reject) {
|
||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||
});
|
||
};
|
||
const Types = [[Uint8Array, Int8Array], [Uint16Array, Int16Array], [Uint32Array, Int32Array, Float32Array], [Float64Array]];
|
||
const U32 = Symbol(32);
|
||
const U16 = Symbol(16);
|
||
const U8 = Symbol(8);
|
||
const OPutMap = new Map();
|
||
Types.forEach((t, i) => t.forEach((t) => OPutMap.set(t, i)));
|
||
class OPut {
|
||
constructor(g) {
|
||
this.g = g;
|
||
this.consumed = 0;
|
||
if (g)
|
||
this.need = g.next().value;
|
||
}
|
||
fillFromReader(source) {
|
||
return __awaiter(this, void 0, void 0, function* () {
|
||
const { done, value } = yield source.read();
|
||
if (done) {
|
||
this.close();
|
||
return;
|
||
}
|
||
else {
|
||
this.write(value);
|
||
return this.fillFromReader(source);
|
||
}
|
||
});
|
||
}
|
||
;
|
||
consume() {
|
||
if (this.buffer && this.consumed) {
|
||
this.buffer.copyWithin(0, this.consumed);
|
||
this.buffer = this.buffer.subarray(0, this.buffer.length - this.consumed);
|
||
this.consumed = 0;
|
||
}
|
||
}
|
||
demand(n, consume) {
|
||
if (consume)
|
||
this.consume();
|
||
this.need = n;
|
||
return this.flush();
|
||
}
|
||
read(need) {
|
||
return new Promise((resolve, reject) => {
|
||
if (this.resolve)
|
||
return reject("last read not complete yet");
|
||
this.resolve = (data) => {
|
||
delete this.resolve;
|
||
delete this.need;
|
||
resolve(data);
|
||
};
|
||
this.demand(need, true);
|
||
});
|
||
}
|
||
readU32() {
|
||
return this.read(U32);
|
||
}
|
||
readU16() {
|
||
return this.read(U16);
|
||
}
|
||
readU8() {
|
||
return this.read(U8);
|
||
}
|
||
close() {
|
||
if (this.g)
|
||
this.g.return();
|
||
}
|
||
flush() {
|
||
if (!this.buffer || !this.need)
|
||
return;
|
||
let returnValue = null;
|
||
const unread = this.buffer.subarray(this.consumed);
|
||
let n = 0;
|
||
const notEnough = (x) => unread.length < (n = x);
|
||
if (typeof this.need === 'number') {
|
||
if (notEnough(this.need))
|
||
return;
|
||
returnValue = unread.subarray(0, n);
|
||
}
|
||
else if (this.need instanceof ArrayBuffer) {
|
||
if (notEnough(this.need.byteLength))
|
||
return;
|
||
new Uint8Array(this.need).set(unread.subarray(0, n));
|
||
returnValue = this.need;
|
||
}
|
||
else if (this.need === U32) {
|
||
if (notEnough(4))
|
||
return;
|
||
returnValue = (unread[0] << 24) | (unread[1] << 16) | (unread[2] << 8) | unread[3];
|
||
}
|
||
else if (this.need === U16) {
|
||
if (notEnough(2))
|
||
return;
|
||
returnValue = (unread[0] << 8) | unread[1];
|
||
}
|
||
else if (this.need === U8) {
|
||
if (notEnough(1))
|
||
return;
|
||
returnValue = unread[0];
|
||
}
|
||
else if (OPutMap.has(this.need.constructor)) {
|
||
if (notEnough(this.need.length << OPutMap.get(this.need.constructor)))
|
||
return;
|
||
new Uint8Array(this.need.buffer, this.need.byteOffset).set(unread.subarray(0, n));
|
||
returnValue = this.need;
|
||
}
|
||
else if (this.g) {
|
||
this.g.throw(new Error('Unsupported type'));
|
||
return;
|
||
}
|
||
this.consumed += n;
|
||
if (this.g)
|
||
this.demand(this.g.next(returnValue).value, true);
|
||
else if (this.resolve)
|
||
this.resolve(returnValue);
|
||
return returnValue;
|
||
}
|
||
write(value) {
|
||
if (value instanceof ArrayBuffer) {
|
||
this.malloc(value.byteLength).set(new Uint8Array(value));
|
||
}
|
||
else {
|
||
this.malloc(value.byteLength).set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
||
}
|
||
if (this.g || this.resolve)
|
||
this.flush();
|
||
}
|
||
writeU32(value) {
|
||
this.malloc(4).set([(value >> 24) & 0xff, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff]);
|
||
this.flush();
|
||
}
|
||
writeU16(value) {
|
||
this.malloc(2).set([(value >> 8) & 0xff, value & 0xff]);
|
||
this.flush();
|
||
}
|
||
writeU8(value) {
|
||
this.malloc(1)[0] = value;
|
||
this.flush();
|
||
}
|
||
malloc(size) {
|
||
if (this.buffer) {
|
||
const l = this.buffer.length;
|
||
const nl = l + size;
|
||
if (nl <= this.buffer.buffer.byteLength - this.buffer.byteOffset) {
|
||
this.buffer = new Uint8Array(this.buffer.buffer, this.buffer.byteOffset, nl);
|
||
}
|
||
else {
|
||
const n = new Uint8Array(nl);
|
||
n.set(this.buffer);
|
||
this.buffer = n;
|
||
}
|
||
return this.buffer.subarray(l, nl);
|
||
}
|
||
else {
|
||
this.buffer = new Uint8Array(size);
|
||
return this.buffer;
|
||
}
|
||
}
|
||
}
|
||
OPut.U32 = U32;
|
||
OPut.U16 = U16;
|
||
OPut.U8 = U8;
|
||
|
||
class Debug {
|
||
constructor(master) {
|
||
this.log = function (name) {
|
||
if (master._opt.debug && master._opt.debugLevel == DEBUG_LEVEL.debug) {
|
||
const prefix = master._opt.debugUuid ? `[${master._opt.debugUuid}]` : '';
|
||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||
args[_key - 1] = arguments[_key];
|
||
}
|
||
console.log(`JessibucaPro${prefix}:[\u2705\u2705\u2705][${name}]`, ...args);
|
||
}
|
||
};
|
||
this.warn = function (name) {
|
||
if (master._opt.debug && (master._opt.debugLevel == DEBUG_LEVEL.debug || master._opt.debugLevel == DEBUG_LEVEL.warn)) {
|
||
const prefix = master._opt.debugUuid ? `[${master._opt.debugUuid}]` : '';
|
||
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
||
args[_key2 - 1] = arguments[_key2];
|
||
}
|
||
console.log(`JessibucaPro${prefix}:[\u2757\u2757\u2757][${name}]`, ...args);
|
||
}
|
||
};
|
||
this.error = function (name) {
|
||
const prefix = master._opt.debugUuid ? `[${master._opt.debugUuid}]` : '';
|
||
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
|
||
args[_key3 - 1] = arguments[_key3];
|
||
}
|
||
console.error(`JessibucaPro${prefix}:[\u274C\u274C\u274C][${name}]`, ...args);
|
||
};
|
||
}
|
||
}
|
||
|
||
// Exponential-Golomb buffer decoder
|
||
class ExpGolomb {
|
||
constructor(uint8array) {
|
||
this._buffer = uint8array;
|
||
this._buffer_index = 0;
|
||
this._total_bytes = uint8array.byteLength;
|
||
this._total_bits = uint8array.byteLength * 8;
|
||
this._current_word = 0;
|
||
this._current_word_bits_left = 0;
|
||
}
|
||
destroy() {
|
||
this._buffer = null;
|
||
}
|
||
_fillCurrentWord() {
|
||
let buffer_bytes_left = this._total_bytes - this._buffer_index;
|
||
if (buffer_bytes_left <= 0) {
|
||
// throw new IllegalStateException('ExpGolomb: _fillCurrentWord() but no bytes available');
|
||
console.error('ExpGolomb: _fillCurrentWord() but no bytes available', this._total_bytes, this._buffer_index);
|
||
return;
|
||
}
|
||
let bytes_read = Math.min(4, buffer_bytes_left);
|
||
let word = new Uint8Array(4);
|
||
word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read));
|
||
this._current_word = new DataView(word.buffer).getUint32(0, false);
|
||
this._buffer_index += bytes_read;
|
||
this._current_word_bits_left = bytes_read * 8;
|
||
}
|
||
readBits(bits) {
|
||
if (bits > 32) {
|
||
// throw new InvalidArgumentException('ExpGolomb: readBits() bits exceeded max 32bits!');
|
||
console.error('ExpGolomb: readBits() bits exceeded max 32bits!');
|
||
}
|
||
if (bits <= this._current_word_bits_left) {
|
||
let result = this._current_word >>> 32 - bits;
|
||
this._current_word <<= bits;
|
||
this._current_word_bits_left -= bits;
|
||
return result;
|
||
}
|
||
let result = this._current_word_bits_left ? this._current_word : 0;
|
||
result = result >>> 32 - this._current_word_bits_left;
|
||
let bits_need_left = bits - this._current_word_bits_left;
|
||
this._fillCurrentWord();
|
||
let bits_read_next = Math.min(bits_need_left, this._current_word_bits_left);
|
||
let result2 = this._current_word >>> 32 - bits_read_next;
|
||
this._current_word <<= bits_read_next;
|
||
this._current_word_bits_left -= bits_read_next;
|
||
result = result << bits_read_next | result2;
|
||
return result;
|
||
}
|
||
readBool() {
|
||
return this.readBits(1) === 1;
|
||
}
|
||
readByte() {
|
||
return this.readBits(8);
|
||
}
|
||
_skipLeadingZero() {
|
||
let zero_count;
|
||
for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) {
|
||
if (0 !== (this._current_word & 0x80000000 >>> zero_count)) {
|
||
this._current_word <<= zero_count;
|
||
this._current_word_bits_left -= zero_count;
|
||
return zero_count;
|
||
}
|
||
}
|
||
this._fillCurrentWord();
|
||
return zero_count + this._skipLeadingZero();
|
||
}
|
||
readUEG() {
|
||
// unsigned exponential golomb
|
||
let leading_zeros = this._skipLeadingZero();
|
||
return this.readBits(leading_zeros + 1) - 1;
|
||
}
|
||
readSEG() {
|
||
// signed exponential golomb
|
||
let value = this.readUEG();
|
||
if (value & 0x01) {
|
||
return value + 1 >>> 1;
|
||
} else {
|
||
return -1 * (value >>> 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Copyright (C) 2016 Bilibili. All Rights Reserved.
|
||
*
|
||
* @author zheng qian <xqq@xqq.im>
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
class SPSParser {
|
||
static _ebsp2rbsp(uint8array) {
|
||
let src = uint8array;
|
||
let src_length = src.byteLength;
|
||
let dst = new Uint8Array(src_length);
|
||
let dst_idx = 0;
|
||
for (let i = 0; i < src_length; i++) {
|
||
if (i >= 2) {
|
||
// Unescape: Skip 0x03 after 00 00
|
||
if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) {
|
||
continue;
|
||
}
|
||
}
|
||
dst[dst_idx] = src[i];
|
||
dst_idx++;
|
||
}
|
||
return new Uint8Array(dst.buffer, 0, dst_idx);
|
||
}
|
||
|
||
// 解析 SPS
|
||
// https://zhuanlan.zhihu.com/p/27896239
|
||
static parseSPS(uint8array) {
|
||
let rbsp = SPSParser._ebsp2rbsp(uint8array);
|
||
let gb = new ExpGolomb(rbsp);
|
||
gb.readByte();
|
||
// 标识当前H.264码流的profile。
|
||
// 我们知道,H.264中定义了三种常用的档次profile: 基准档次:baseline profile;主要档次:main profile; 扩展档次:extended profile;
|
||
|
||
let profile_idc = gb.readByte(); // profile_idc
|
||
gb.readByte(); // constraint_set_flags[5] + reserved_zero[3]
|
||
// 标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。
|
||
let level_idc = gb.readByte(); // level_idc
|
||
// 表示当前的序列参数集的id。通过该id值,图像参数集pps可以引用其代表的sps中的参数。
|
||
gb.readUEG(); // seq_parameter_set_id
|
||
|
||
let profile_string = SPSParser.getProfileString(profile_idc);
|
||
let level_string = SPSParser.getLevelString(level_idc);
|
||
let chroma_format_idc = 1;
|
||
let chroma_format = 420;
|
||
let chroma_format_table = [0, 420, 422, 444];
|
||
let bit_depth = 8;
|
||
|
||
//
|
||
if (profile_idc === 100 || profile_idc === 110 || profile_idc === 122 || profile_idc === 244 || profile_idc === 44 || profile_idc === 83 || profile_idc === 86 || profile_idc === 118 || profile_idc === 128 || profile_idc === 138 || profile_idc === 144) {
|
||
//
|
||
chroma_format_idc = gb.readUEG();
|
||
if (chroma_format_idc === 3) {
|
||
gb.readBits(1); // separate_colour_plane_flag
|
||
}
|
||
|
||
if (chroma_format_idc <= 3) {
|
||
chroma_format = chroma_format_table[chroma_format_idc];
|
||
}
|
||
bit_depth = gb.readUEG() + 8; // bit_depth_luma_minus8
|
||
gb.readUEG(); // bit_depth_chroma_minus8
|
||
gb.readBits(1); // qpprime_y_zero_transform_bypass_flag
|
||
if (gb.readBool()) {
|
||
// seq_scaling_matrix_present_flag
|
||
let scaling_list_count = chroma_format_idc !== 3 ? 8 : 12;
|
||
for (let i = 0; i < scaling_list_count; i++) {
|
||
if (gb.readBool()) {
|
||
// seq_scaling_list_present_flag
|
||
if (i < 6) {
|
||
SPSParser._skipScalingList(gb, 16);
|
||
} else {
|
||
SPSParser._skipScalingList(gb, 64);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 用于计算MaxFrameNum的值。计算公式为MaxFrameNum = 2^(log2_max_frame_num_minus4 +
|
||
gb.readUEG(); // log2_max_frame_num_minus4
|
||
// 表示解码picture order count(POC)的方法。POC是另一种计量图像序号的方式,与frame_num有着不同的计算方法。该语法元素的取值为0、1或2。
|
||
let pic_order_cnt_type = gb.readUEG();
|
||
if (pic_order_cnt_type === 0) {
|
||
gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4
|
||
} else if (pic_order_cnt_type === 1) {
|
||
gb.readBits(1); // delta_pic_order_always_zero_flag
|
||
gb.readSEG(); // offset_for_non_ref_pic
|
||
gb.readSEG(); // offset_for_top_to_bottom_field
|
||
let num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG();
|
||
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
||
gb.readSEG(); // offset_for_ref_frame
|
||
}
|
||
}
|
||
// 用于表示参考帧的最大数目。
|
||
let ref_frames = gb.readUEG(); // max_num_ref_frames
|
||
// 标识位,说明frame_num中是否允许不连续的值。
|
||
gb.readBits(1); // gaps_in_frame_num_value_allowed_flag
|
||
// 用于计算图像的宽度。单位为宏块个数,因此图像的实际宽度为:
|
||
let pic_width_in_mbs_minus1 = gb.readUEG();
|
||
// 使用PicHeightInMapUnits来度量视频中一帧图像的高度。
|
||
// PicHeightInMapUnits并非图像明确的以像素或宏块为单位的高度,而需要考虑该宏块是帧编码或场编码。PicHeightInMapUnits的计算方式为:
|
||
let pic_height_in_map_units_minus1 = gb.readUEG();
|
||
// 标识位,说明宏块的编码方式。当该标识位为0时,宏块可能为帧编码或场编码;
|
||
// 该标识位为1时,所有宏块都采用帧编码。根据该标识位取值不同,PicHeightInMapUnits的含义也不同,
|
||
// 为0时表示一场数据按宏块计算的高度,为1时表示一帧数据按宏块计算的高度。
|
||
let frame_mbs_only_flag = gb.readBits(1);
|
||
if (frame_mbs_only_flag === 0) {
|
||
// 标识位,说明是否采用了宏块级的帧场自适应编码。当该标识位为0时,不存在帧编码和场编码之间的切换;当标识位为1时,宏块可能在帧编码和场编码模式之间进行选择。
|
||
gb.readBits(1); // mb_adaptive_frame_field_flag
|
||
}
|
||
// 标识位,用于B_Skip、B_Direct模式运动矢量的推导计算。
|
||
gb.readBits(1); // direct_8x8_inference_flag
|
||
|
||
let frame_crop_left_offset = 0;
|
||
let frame_crop_right_offset = 0;
|
||
let frame_crop_top_offset = 0;
|
||
let frame_crop_bottom_offset = 0;
|
||
let frame_cropping_flag = gb.readBool();
|
||
if (frame_cropping_flag) {
|
||
frame_crop_left_offset = gb.readUEG();
|
||
frame_crop_right_offset = gb.readUEG();
|
||
frame_crop_top_offset = gb.readUEG();
|
||
frame_crop_bottom_offset = gb.readUEG();
|
||
}
|
||
let sar_width = 1,
|
||
sar_height = 1;
|
||
let fps = 0,
|
||
fps_fixed = true,
|
||
fps_num = 0,
|
||
fps_den = 0;
|
||
// 标识位,说明SPS中是否存在VUI信息。
|
||
let vui_parameters_present_flag = gb.readBool();
|
||
if (vui_parameters_present_flag) {
|
||
if (gb.readBool()) {
|
||
// aspect_ratio_info_present_flag
|
||
let aspect_ratio_idc = gb.readByte();
|
||
let sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
|
||
let sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
|
||
if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
|
||
sar_width = sar_w_table[aspect_ratio_idc - 1];
|
||
sar_height = sar_h_table[aspect_ratio_idc - 1];
|
||
} else if (aspect_ratio_idc === 255) {
|
||
sar_width = gb.readByte() << 8 | gb.readByte();
|
||
sar_height = gb.readByte() << 8 | gb.readByte();
|
||
}
|
||
}
|
||
if (gb.readBool()) {
|
||
// overscan_info_present_flag
|
||
gb.readBool(); // overscan_appropriate_flag
|
||
}
|
||
|
||
if (gb.readBool()) {
|
||
// video_signal_type_present_flag
|
||
gb.readBits(4); // video_format & video_full_range_flag
|
||
if (gb.readBool()) {
|
||
// colour_description_present_flag
|
||
gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients
|
||
}
|
||
}
|
||
|
||
if (gb.readBool()) {
|
||
// chroma_loc_info_present_flag
|
||
gb.readUEG(); // chroma_sample_loc_type_top_field
|
||
gb.readUEG(); // chroma_sample_loc_type_bottom_field
|
||
}
|
||
|
||
if (gb.readBool()) {
|
||
// timing_info_present_flag
|
||
let num_units_in_tick = gb.readBits(32);
|
||
let time_scale = gb.readBits(32);
|
||
fps_fixed = gb.readBool(); // fixed_frame_rate_flag
|
||
|
||
fps_num = time_scale;
|
||
fps_den = num_units_in_tick * 2;
|
||
fps = fps_num / fps_den;
|
||
}
|
||
}
|
||
let sarScale = 1;
|
||
if (sar_width !== 1 || sar_height !== 1) {
|
||
sarScale = sar_width / sar_height;
|
||
}
|
||
let crop_unit_x = 0,
|
||
crop_unit_y = 0;
|
||
if (chroma_format_idc === 0) {
|
||
crop_unit_x = 1;
|
||
crop_unit_y = 2 - frame_mbs_only_flag;
|
||
} else {
|
||
let sub_wc = chroma_format_idc === 3 ? 1 : 2;
|
||
let sub_hc = chroma_format_idc === 1 ? 2 : 1;
|
||
crop_unit_x = sub_wc;
|
||
crop_unit_y = sub_hc * (2 - frame_mbs_only_flag);
|
||
}
|
||
let codec_width = (pic_width_in_mbs_minus1 + 1) * 16;
|
||
let codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16);
|
||
codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x;
|
||
codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y;
|
||
let present_width = Math.ceil(codec_width * sarScale);
|
||
gb.destroy();
|
||
gb = null;
|
||
|
||
// 解析出来的SPS 内容。
|
||
return {
|
||
profile_string: profile_string,
|
||
// baseline, high, high10, ...
|
||
level_string: level_string,
|
||
// 3, 3.1, 4, 4.1, 5, 5.1, ...
|
||
bit_depth: bit_depth,
|
||
// 8bit, 10bit, ...
|
||
ref_frames: ref_frames,
|
||
chroma_format: chroma_format,
|
||
// 4:2:0, 4:2:2, ...
|
||
chroma_format_string: SPSParser.getChromaFormatString(chroma_format),
|
||
frame_rate: {
|
||
fixed: fps_fixed,
|
||
fps: fps,
|
||
fps_den: fps_den,
|
||
fps_num: fps_num
|
||
},
|
||
sar_ratio: {
|
||
width: sar_width,
|
||
height: sar_height
|
||
},
|
||
codec_size: {
|
||
width: codec_width,
|
||
height: codec_height
|
||
},
|
||
present_size: {
|
||
width: present_width,
|
||
height: codec_height
|
||
}
|
||
};
|
||
}
|
||
static parseSPS$2(uint8array) {
|
||
let codec_array = uint8array.subarray(1, 4);
|
||
let codec_mimetype = 'avc1.';
|
||
for (let j = 0; j < 3; j++) {
|
||
let h = codec_array[j].toString(16);
|
||
if (h.length < 2) {
|
||
h = '0' + h;
|
||
}
|
||
codec_mimetype += h;
|
||
}
|
||
let rbsp = SPSParser._ebsp2rbsp(uint8array);
|
||
let gb = new ExpGolomb(rbsp);
|
||
gb.readByte();
|
||
let profile_idc = gb.readByte(); // profile_idc
|
||
gb.readByte(); // constraint_set_flags[5] + reserved_zero[3]
|
||
let level_idc = gb.readByte(); // level_idc
|
||
gb.readUEG(); // seq_parameter_set_id
|
||
|
||
let profile_string = SPSParser.getProfileString(profile_idc);
|
||
let level_string = SPSParser.getLevelString(level_idc);
|
||
let chroma_format_idc = 1;
|
||
let chroma_format = 420;
|
||
let chroma_format_table = [0, 420, 422, 444];
|
||
let bit_depth_luma = 8;
|
||
let bit_depth_chroma = 8;
|
||
if (profile_idc === 100 || profile_idc === 110 || profile_idc === 122 || profile_idc === 244 || profile_idc === 44 || profile_idc === 83 || profile_idc === 86 || profile_idc === 118 || profile_idc === 128 || profile_idc === 138 || profile_idc === 144) {
|
||
chroma_format_idc = gb.readUEG();
|
||
if (chroma_format_idc === 3) {
|
||
gb.readBits(1); // separate_colour_plane_flag
|
||
}
|
||
|
||
if (chroma_format_idc <= 3) {
|
||
chroma_format = chroma_format_table[chroma_format_idc];
|
||
}
|
||
bit_depth_luma = gb.readUEG() + 8; // bit_depth_luma_minus8
|
||
bit_depth_chroma = gb.readUEG() + 8; // bit_depth_chroma_minus8
|
||
gb.readBits(1); // qpprime_y_zero_transform_bypass_flag
|
||
if (gb.readBool()) {
|
||
// seq_scaling_matrix_present_flag
|
||
let scaling_list_count = chroma_format_idc !== 3 ? 8 : 12;
|
||
for (let i = 0; i < scaling_list_count; i++) {
|
||
if (gb.readBool()) {
|
||
// seq_scaling_list_present_flag
|
||
if (i < 6) {
|
||
SPSParser._skipScalingList(gb, 16);
|
||
} else {
|
||
SPSParser._skipScalingList(gb, 64);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
gb.readUEG(); // log2_max_frame_num_minus4
|
||
let pic_order_cnt_type = gb.readUEG();
|
||
if (pic_order_cnt_type === 0) {
|
||
gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4
|
||
} else if (pic_order_cnt_type === 1) {
|
||
gb.readBits(1); // delta_pic_order_always_zero_flag
|
||
gb.readSEG(); // offset_for_non_ref_pic
|
||
gb.readSEG(); // offset_for_top_to_bottom_field
|
||
let num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG();
|
||
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
||
gb.readSEG(); // offset_for_ref_frame
|
||
}
|
||
}
|
||
|
||
let ref_frames = gb.readUEG(); // max_num_ref_frames
|
||
gb.readBits(1); // gaps_in_frame_num_value_allowed_flag
|
||
|
||
let pic_width_in_mbs_minus1 = gb.readUEG();
|
||
let pic_height_in_map_units_minus1 = gb.readUEG();
|
||
let frame_mbs_only_flag = gb.readBits(1);
|
||
if (frame_mbs_only_flag === 0) {
|
||
gb.readBits(1); // mb_adaptive_frame_field_flag
|
||
}
|
||
|
||
gb.readBits(1); // direct_8x8_inference_flag
|
||
|
||
let frame_crop_left_offset = 0;
|
||
let frame_crop_right_offset = 0;
|
||
let frame_crop_top_offset = 0;
|
||
let frame_crop_bottom_offset = 0;
|
||
let frame_cropping_flag = gb.readBool();
|
||
if (frame_cropping_flag) {
|
||
frame_crop_left_offset = gb.readUEG();
|
||
frame_crop_right_offset = gb.readUEG();
|
||
frame_crop_top_offset = gb.readUEG();
|
||
frame_crop_bottom_offset = gb.readUEG();
|
||
}
|
||
let sar_width = 1,
|
||
sar_height = 1;
|
||
let fps = 0,
|
||
fps_fixed = true,
|
||
fps_num = 0,
|
||
fps_den = 0;
|
||
let vui_parameters_present_flag = gb.readBool();
|
||
if (vui_parameters_present_flag) {
|
||
if (gb.readBool()) {
|
||
// aspect_ratio_info_present_flag
|
||
let aspect_ratio_idc = gb.readByte();
|
||
let sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
|
||
let sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
|
||
if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
|
||
sar_width = sar_w_table[aspect_ratio_idc - 1];
|
||
sar_height = sar_h_table[aspect_ratio_idc - 1];
|
||
} else if (aspect_ratio_idc === 255) {
|
||
sar_width = gb.readByte() << 8 | gb.readByte();
|
||
sar_height = gb.readByte() << 8 | gb.readByte();
|
||
}
|
||
}
|
||
if (gb.readBool()) {
|
||
// overscan_info_present_flag
|
||
gb.readBool(); // overscan_appropriate_flag
|
||
}
|
||
|
||
if (gb.readBool()) {
|
||
// video_signal_type_present_flag
|
||
gb.readBits(4); // video_format & video_full_range_flag
|
||
if (gb.readBool()) {
|
||
// colour_description_present_flag
|
||
gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients
|
||
}
|
||
}
|
||
|
||
if (gb.readBool()) {
|
||
// chroma_loc_info_present_flag
|
||
gb.readUEG(); // chroma_sample_loc_type_top_field
|
||
gb.readUEG(); // chroma_sample_loc_type_bottom_field
|
||
}
|
||
|
||
if (gb.readBool()) {
|
||
// timing_info_present_flag
|
||
let num_units_in_tick = gb.readBits(32);
|
||
let time_scale = gb.readBits(32);
|
||
fps_fixed = gb.readBool(); // fixed_frame_rate_flag
|
||
|
||
fps_num = time_scale;
|
||
fps_den = num_units_in_tick * 2;
|
||
fps = fps_num / fps_den;
|
||
}
|
||
}
|
||
let sarScale = 1;
|
||
if (sar_width !== 1 || sar_height !== 1) {
|
||
sarScale = sar_width / sar_height;
|
||
}
|
||
let crop_unit_x = 0,
|
||
crop_unit_y = 0;
|
||
if (chroma_format_idc === 0) {
|
||
crop_unit_x = 1;
|
||
crop_unit_y = 2 - frame_mbs_only_flag;
|
||
} else {
|
||
let sub_wc = chroma_format_idc === 3 ? 1 : 2;
|
||
let sub_hc = chroma_format_idc === 1 ? 2 : 1;
|
||
crop_unit_x = sub_wc;
|
||
crop_unit_y = sub_hc * (2 - frame_mbs_only_flag);
|
||
}
|
||
let codec_width = (pic_width_in_mbs_minus1 + 1) * 16;
|
||
let codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16);
|
||
codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x;
|
||
codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y;
|
||
let present_width = Math.ceil(codec_width * sarScale);
|
||
gb.destroy();
|
||
gb = null;
|
||
return {
|
||
codec_mimetype,
|
||
profile_idc,
|
||
level_idc,
|
||
profile_string,
|
||
// baseline, high, high10, ...
|
||
level_string,
|
||
// 3, 3.1, 4, 4.1, 5, 5.1, ...
|
||
chroma_format_idc,
|
||
bit_depth: bit_depth_luma,
|
||
// 8bit, 10bit, ...
|
||
bit_depth_luma,
|
||
bit_depth_chroma,
|
||
ref_frames,
|
||
chroma_format,
|
||
// 4:2:0, 4:2:2, ...
|
||
chroma_format_string: SPSParser.getChromaFormatString(chroma_format),
|
||
frame_rate: {
|
||
fixed: fps_fixed,
|
||
fps: fps,
|
||
fps_den: fps_den,
|
||
fps_num: fps_num
|
||
},
|
||
sar_ratio: {
|
||
width: sar_width,
|
||
height: sar_height
|
||
},
|
||
codec_size: {
|
||
width: codec_width,
|
||
height: codec_height
|
||
},
|
||
present_size: {
|
||
width: present_width,
|
||
height: codec_height
|
||
}
|
||
};
|
||
}
|
||
static _skipScalingList(gb, count) {
|
||
let last_scale = 8,
|
||
next_scale = 8;
|
||
let delta_scale = 0;
|
||
for (let i = 0; i < count; i++) {
|
||
if (next_scale !== 0) {
|
||
delta_scale = gb.readSEG();
|
||
next_scale = (last_scale + delta_scale + 256) % 256;
|
||
}
|
||
last_scale = next_scale === 0 ? last_scale : next_scale;
|
||
}
|
||
}
|
||
|
||
// profile_idc = 66 → baseline profile;
|
||
// profile_idc = 77 → main profile;
|
||
// profile_idc = 88 → extended profile;
|
||
// 在新版的标准中,还包括了High、High 10、High 4:2:2、High 4:4:4、High 10 Intra、High
|
||
// 4:2:2 Intra、High 4:4:4 Intra、CAVLC 4:4:4 Intra
|
||
static getProfileString(profile_idc) {
|
||
switch (profile_idc) {
|
||
case 66:
|
||
return 'Baseline';
|
||
case 77:
|
||
return 'Main';
|
||
case 88:
|
||
return 'Extended';
|
||
case 100:
|
||
return 'High';
|
||
case 110:
|
||
return 'High10';
|
||
case 122:
|
||
return 'High422';
|
||
case 244:
|
||
return 'High444';
|
||
default:
|
||
return 'Unknown';
|
||
}
|
||
}
|
||
static getLevelString(level_idc) {
|
||
return (level_idc / 10).toFixed(1);
|
||
}
|
||
static getChromaFormatString(chroma) {
|
||
switch (chroma) {
|
||
case 420:
|
||
return '4:2:0';
|
||
case 422:
|
||
return '4:2:2';
|
||
case 444:
|
||
return '4:4:4';
|
||
default:
|
||
return 'Unknown';
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
function parseAVCDecoderConfigurationRecord(arrayBuffer) {
|
||
const meta = {};
|
||
const v = new DataView(arrayBuffer.buffer);
|
||
let version = v.getUint8(0); // configurationVersion
|
||
let avcProfile = v.getUint8(1); // avcProfileIndication
|
||
v.getUint8(2); // profile_compatibil
|
||
v.getUint8(3); // AVCLevelIndication
|
||
|
||
if (version !== 1 || avcProfile === 0) {
|
||
// this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord');
|
||
|
||
return {};
|
||
}
|
||
const _naluLengthSize = (v.getUint8(4) & 3) + 1; // lengthSizeMinusOne
|
||
|
||
if (_naluLengthSize !== 3 && _naluLengthSize !== 4) {
|
||
// holy shit!!!
|
||
// this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Strange NaluLengthSizeMinusOne: ${_naluLengthSize - 1}`);
|
||
return {};
|
||
}
|
||
let spsCount = v.getUint8(5) & 31; // numOfSequenceParameterSets
|
||
|
||
if (spsCount === 0) {
|
||
// this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No SPS');
|
||
return {};
|
||
}
|
||
let offset = 6;
|
||
for (let i = 0; i < spsCount; i++) {
|
||
let len = v.getUint16(offset, false); // sequenceParameterSetLength
|
||
offset += 2;
|
||
if (len === 0) {
|
||
continue;
|
||
}
|
||
|
||
// Notice: Nalu without startcode header (00 00 00 01)
|
||
let sps = new Uint8Array(arrayBuffer.buffer, offset, len);
|
||
offset += len;
|
||
// flv.js作者选择了自己来解析这个数据结构,也是迫不得已,因为JS环境下没有ffmpeg,解析这个结构主要是为了提取 sps和pps。虽然理论上sps允许有多个,但其实一般就一个。
|
||
// packetTtype 为 1 表示 NALU,NALU= network abstract layer unit,这是H.264的概念,网络抽象层数据单元,其实简单理解就是一帧视频数据。
|
||
// pps的信息没什么用,所以作者只实现了sps的分析器,说明作者下了很大功夫去学习264的标准,其中的Golomb解码还是挺复杂的,能解对不容易,我在PC和手机平台都是用ffmpeg去解析的。
|
||
// SPS里面包括了视频分辨率,帧率,profile level等视频重要信息。
|
||
let config = SPSParser.parseSPS(sps);
|
||
// console.log('h264 sps config',config)
|
||
if (i !== 0) {
|
||
// ignore other sps's config
|
||
continue;
|
||
}
|
||
meta.sps = sps;
|
||
meta.timescale = 1000;
|
||
meta.codecWidth = config.codec_size.width;
|
||
meta.codecHeight = config.codec_size.height;
|
||
meta.presentWidth = config.present_size.width;
|
||
meta.presentHeight = config.present_size.height;
|
||
meta.profile = config.profile_string;
|
||
meta.level = config.level_string;
|
||
meta.bitDepth = config.bit_depth;
|
||
meta.chromaFormat = config.chroma_format;
|
||
meta.sarRatio = config.sar_ratio;
|
||
meta.frameRate = config.frame_rate;
|
||
if (config.frame_rate.fixed === false || config.frame_rate.fps_num === 0 || config.frame_rate.fps_den === 0) {
|
||
meta.frameRate = {
|
||
fixed: true,
|
||
fps: 23.976,
|
||
fps_num: 23976,
|
||
fps_den: 1000
|
||
};
|
||
}
|
||
let fps_den = meta.frameRate.fps_den;
|
||
let fps_num = meta.frameRate.fps_num;
|
||
meta.refSampleDuration = meta.timescale * (fps_den / fps_num);
|
||
let codecArray = sps.subarray(1, 4);
|
||
let codecString = 'avc1.';
|
||
for (let j = 0; j < 3; j++) {
|
||
let h = codecArray[j].toString(16);
|
||
if (h.length < 2) {
|
||
h = '0' + h;
|
||
}
|
||
codecString += h;
|
||
}
|
||
// codec
|
||
meta.codec = codecString;
|
||
}
|
||
let ppsCount = v.getUint8(offset); // numOfPictureParameterSets
|
||
if (ppsCount === 0) {
|
||
// this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No PPS');
|
||
return {};
|
||
}
|
||
offset++;
|
||
for (let i = 0; i < ppsCount; i++) {
|
||
let len = v.getUint16(offset, false); // pictureParameterSetLength
|
||
offset += 2;
|
||
if (len === 0) {
|
||
continue;
|
||
}
|
||
let pps = new Uint8Array(arrayBuffer.buffer, offset, len);
|
||
|
||
// pps is useless for extracting video information
|
||
offset += len;
|
||
meta.pps = pps;
|
||
}
|
||
meta.videoType = 'avc';
|
||
if (meta.sps) {
|
||
const spsLength = meta.sps.byteLength;
|
||
const spsFlag = new Uint8Array([spsLength >>> 24 & 0xFF, spsLength >>> 16 & 0xFF, spsLength >>> 8 & 0xFF, spsLength & 0xFF]);
|
||
const sps = new Uint8Array(spsLength + 4);
|
||
sps.set(spsFlag, 0);
|
||
sps.set(meta.sps, 4);
|
||
meta.sps = sps;
|
||
}
|
||
if (meta.pps) {
|
||
const ppsLength = meta.pps.byteLength;
|
||
const ppsFlag = new Uint8Array([ppsLength >>> 24 & 0xFF, ppsLength >>> 16 & 0xFF, ppsLength >>> 8 & 0xFF, ppsLength & 0xFF]);
|
||
const pps = new Uint8Array(ppsLength + 4);
|
||
pps.set(ppsFlag, 0);
|
||
pps.set(meta.pps, 4);
|
||
meta.pps = pps;
|
||
}
|
||
|
||
// meta.avcc = arrayBuffer;
|
||
return meta;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param sps
|
||
* @param pps
|
||
* @returns {Uint8Array}
|
||
*/
|
||
function avcEncoderConfigurationRecord(_ref) {
|
||
let {
|
||
sps,
|
||
pps
|
||
} = _ref;
|
||
// todo: 1 是 序列帧 2-4cts帧
|
||
// 从 0x01 开始 表示version。
|
||
// RTMP_AVC_HEAD
|
||
// 0x17 keyframe 7:AVC
|
||
// 0x00 AVC sequence header
|
||
// 0x00 0x00 0x00
|
||
// 0x01 configurationVersion
|
||
// 0x42 AVCProfileIndication
|
||
// 0x00 profile_compatibility
|
||
// 0x1E AVCLevelIndication
|
||
// 0xFF lengthSizeMinusOne
|
||
const tmp = [0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x00, 0x1E, 0xFF];
|
||
// 是个是私有协议标识,h264的。
|
||
// 0x17 :23
|
||
tmp[0] = 0x17;
|
||
|
||
// 标准的 nale 格式。。。
|
||
tmp[6] = sps[1]; // 0x42 avc profile ( sps[0][1] )
|
||
tmp[7] = sps[2]; // 0x00 avc compatibility ( sps[0][2] )
|
||
tmp[8] = sps[3]; // 0x1E avc level ( sps[0][3] )
|
||
//tmp[9] 0xFF 6 reserved ( all bits on )
|
||
// temp 的length 是10 最后的下标就是 9
|
||
|
||
// 0xE1 : 225
|
||
tmp[10] = 0xE1; //
|
||
// number of SPS NALUs (usually 1) repeated once per SPS:
|
||
//
|
||
tmp[11] = sps.byteLength >> 8 & 0xff; //
|
||
// SPS size
|
||
tmp[12] = sps.byteLength & 0xff; //
|
||
// variable SPS NALU data
|
||
// number of PPS NALUs (usually 1) repeated once per PPS
|
||
// PPS size
|
||
// variable PPS NALU data
|
||
tmp.push(...sps, 0x01, pps.byteLength >> 8 & 0xff, pps.byteLength & 0xff, ...pps);
|
||
//
|
||
|
||
const arrayBuffer = new Uint8Array(tmp);
|
||
return arrayBuffer;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param oneNALBuffer
|
||
* @param isIframe
|
||
* @returns {Uint8Array}
|
||
*/
|
||
function avcEncoderNalePacket(oneNALBuffer, isIframe) {
|
||
// 正常发送nal
|
||
const idrBit = 0x10 | 7;
|
||
const nIdrBit = 0x20 | 7;
|
||
let tmp = [];
|
||
if (isIframe) {
|
||
tmp[0] = idrBit;
|
||
} else {
|
||
tmp[0] = nIdrBit;
|
||
}
|
||
// compositionTime
|
||
tmp[1] = 1;
|
||
tmp[2] = 0;
|
||
tmp[3] = 0;
|
||
tmp[4] = 0;
|
||
|
||
//
|
||
tmp[5] = oneNALBuffer.byteLength >> 24 & 0xff;
|
||
tmp[6] = oneNALBuffer.byteLength >> 16 & 0xff;
|
||
tmp[7] = oneNALBuffer.byteLength >> 8 & 0xff;
|
||
tmp[8] = oneNALBuffer.byteLength & 0xff;
|
||
const arrayBuffer = new Uint8Array(tmp.length + oneNALBuffer.byteLength);
|
||
arrayBuffer.set(tmp, 0);
|
||
arrayBuffer.set(oneNALBuffer, tmp.length);
|
||
return arrayBuffer;
|
||
}
|
||
function avcEncoderNalePacketNotLength(oneNALBuffer, isIframe) {
|
||
// 正常发送nal
|
||
const idrBit = 0x10 | 7;
|
||
const nIdrBit = 0x20 | 7;
|
||
let tmp = [];
|
||
if (isIframe) {
|
||
tmp[0] = idrBit;
|
||
} else {
|
||
tmp[0] = nIdrBit;
|
||
}
|
||
// compositionTime
|
||
tmp[1] = 1;
|
||
tmp[2] = 0;
|
||
tmp[3] = 0;
|
||
tmp[4] = 0;
|
||
const arrayBuffer = new Uint8Array(tmp.length + oneNALBuffer.byteLength);
|
||
arrayBuffer.set(tmp, 0);
|
||
arrayBuffer.set(oneNALBuffer, tmp.length);
|
||
return arrayBuffer;
|
||
}
|
||
function addNaleHeaderLength(nalUnit) {
|
||
const nalUnitLength = nalUnit.byteLength;
|
||
const header = new Uint8Array(4);
|
||
header[0] = nalUnitLength >>> 24 & 0xff;
|
||
header[1] = nalUnitLength >>> 16 & 0xff;
|
||
header[2] = nalUnitLength >>> 8 & 0xff;
|
||
header[3] = nalUnitLength & 0xff;
|
||
const result = new Uint8Array(nalUnitLength + 4);
|
||
result.set(header, 0);
|
||
result.set(nalUnit, 4);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* (NALU类型 & 0001 1111)
|
||
* @param nalu
|
||
* @returns {number}
|
||
*/
|
||
function getAvcSeqHeadType(nalu) {
|
||
return nalu[0] & 0b0001_1111;
|
||
}
|
||
function isAvcSeqHead(type) {
|
||
return type === H264_NAL_TYPE.sps || type === H264_NAL_TYPE.pps;
|
||
}
|
||
function isHvcSEIType(type) {
|
||
return type === H264_NAL_TYPE.kSliceSEI;
|
||
}
|
||
function isNotAvcSeqHead(type) {
|
||
return !isAvcSeqHead(type) && !isHvcSEIType(type);
|
||
}
|
||
function isAvcNaluIFrame(type) {
|
||
return type === H264_NAL_TYPE.iFrame;
|
||
}
|
||
function isSameAvcNaluType(naluList) {
|
||
if (naluList.length === 0) {
|
||
return false;
|
||
}
|
||
const type = getAvcSeqHeadType(naluList[0]);
|
||
for (let i = 1; i < naluList.length; i++) {
|
||
if (type !== getAvcSeqHeadType(naluList[i])) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
const _ebsp2rbsp = uint8array => {
|
||
let src = uint8array;
|
||
let src_length = src.byteLength;
|
||
let dst = new Uint8Array(src_length);
|
||
let dst_idx = 0;
|
||
for (let i = 0; i < src_length; i++) {
|
||
if (i >= 2) {
|
||
// Unescape: Skip 0x03 after 00 00
|
||
if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) {
|
||
continue;
|
||
}
|
||
}
|
||
dst[dst_idx] = src[i];
|
||
dst_idx++;
|
||
}
|
||
return new Uint8Array(dst.buffer, 0, dst_idx);
|
||
};
|
||
const getLevelString = level_idc => {
|
||
return (level_idc / 30).toFixed(1);
|
||
};
|
||
const getChromaFormatString = chroma_format_idc => {
|
||
switch (chroma_format_idc) {
|
||
case 0:
|
||
return '4:0:0';
|
||
case 1:
|
||
return '4:2:0';
|
||
case 2:
|
||
return '4:2:2';
|
||
case 3:
|
||
return '4:4:4';
|
||
default:
|
||
return 'Unknown';
|
||
}
|
||
};
|
||
const parseHevcSPS = uint8array => {
|
||
let rbsp = _ebsp2rbsp(uint8array);
|
||
let gb = new ExpGolomb(rbsp);
|
||
|
||
/* remove NALu Header */
|
||
gb.readByte();
|
||
gb.readByte();
|
||
let left_offset = 0,
|
||
right_offset = 0,
|
||
top_offset = 0,
|
||
bottom_offset = 0;
|
||
|
||
// SPS
|
||
gb.readBits(4);
|
||
let max_sub_layers_minus1 = gb.readBits(3);
|
||
gb.readBool();
|
||
|
||
// profile_tier_level begin
|
||
let general_profile_space = gb.readBits(2);
|
||
let general_tier_flag = gb.readBool();
|
||
let general_profile_idc = gb.readBits(5);
|
||
let general_profile_compatibility_flags_1 = gb.readByte();
|
||
let general_profile_compatibility_flags_2 = gb.readByte();
|
||
let general_profile_compatibility_flags_3 = gb.readByte();
|
||
let general_profile_compatibility_flags_4 = gb.readByte();
|
||
let general_constraint_indicator_flags_1 = gb.readByte();
|
||
let general_constraint_indicator_flags_2 = gb.readByte();
|
||
let general_constraint_indicator_flags_3 = gb.readByte();
|
||
let general_constraint_indicator_flags_4 = gb.readByte();
|
||
let general_constraint_indicator_flags_5 = gb.readByte();
|
||
let general_constraint_indicator_flags_6 = gb.readByte();
|
||
let general_level_idc = gb.readByte();
|
||
let sub_layer_profile_present_flag = [];
|
||
let sub_layer_level_present_flag = [];
|
||
for (let i = 0; i < max_sub_layers_minus1; i++) {
|
||
sub_layer_profile_present_flag.push(gb.readBool());
|
||
sub_layer_level_present_flag.push(gb.readBool());
|
||
}
|
||
if (max_sub_layers_minus1 > 0) {
|
||
for (let i = max_sub_layers_minus1; i < 8; i++) {
|
||
gb.readBits(2);
|
||
}
|
||
}
|
||
for (let i = 0; i < max_sub_layers_minus1; i++) {
|
||
if (sub_layer_profile_present_flag[i]) {
|
||
gb.readByte(); // sub_layer_profile_space, sub_layer_tier_flag, sub_layer_profile_idc
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte(); // sub_layer_profile_compatibility_flag
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte();
|
||
}
|
||
if (sub_layer_profile_present_flag[i]) {
|
||
gb.readByte();
|
||
}
|
||
}
|
||
// profile_tier_level end
|
||
|
||
gb.readUEG();
|
||
let chroma_format_idc = gb.readUEG();
|
||
if (chroma_format_idc == 3) {
|
||
gb.readBits(1); // separate_colour_plane_flag
|
||
}
|
||
|
||
let pic_width_in_luma_samples = gb.readUEG();
|
||
let pic_height_in_luma_samples = gb.readUEG();
|
||
let conformance_window_flag = gb.readBool();
|
||
if (conformance_window_flag) {
|
||
left_offset += gb.readUEG();
|
||
right_offset += gb.readUEG();
|
||
top_offset += gb.readUEG();
|
||
bottom_offset += gb.readUEG();
|
||
}
|
||
let bit_depth_luma_minus8 = gb.readUEG();
|
||
let bit_depth_chroma_minus8 = gb.readUEG();
|
||
let log2_max_pic_order_cnt_lsb_minus4 = gb.readUEG();
|
||
let sub_layer_ordering_info_present_flag = gb.readBool();
|
||
for (let i = sub_layer_ordering_info_present_flag ? 0 : max_sub_layers_minus1; i <= max_sub_layers_minus1; i++) {
|
||
gb.readUEG(); // max_dec_pic_buffering_minus1[i]
|
||
gb.readUEG(); // max_num_reorder_pics[i]
|
||
gb.readUEG(); // max_latency_increase_plus1[i]
|
||
}
|
||
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
let scaling_list_enabled_flag = gb.readBool();
|
||
if (scaling_list_enabled_flag) {
|
||
let sps_scaling_list_data_present_flag = gb.readBool();
|
||
if (sps_scaling_list_data_present_flag) {
|
||
for (let sizeId = 0; sizeId < 4; sizeId++) {
|
||
for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {
|
||
let scaling_list_pred_mode_flag = gb.readBool();
|
||
if (!scaling_list_pred_mode_flag) {
|
||
gb.readUEG(); // scaling_list_pred_matrix_id_delta
|
||
} else {
|
||
let coefNum = Math.min(64, 1 << 4 + (sizeId << 1));
|
||
if (sizeId > 1) {
|
||
gb.readSEG();
|
||
}
|
||
for (let i = 0; i < coefNum; i++) {
|
||
gb.readSEG();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
gb.readBool();
|
||
gb.readBool();
|
||
let pcm_enabled_flag = gb.readBool();
|
||
if (pcm_enabled_flag) {
|
||
gb.readByte();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readBool();
|
||
}
|
||
let num_short_term_ref_pic_sets = gb.readUEG();
|
||
let num_delta_pocs = 0;
|
||
for (let i = 0; i < num_short_term_ref_pic_sets; i++) {
|
||
let inter_ref_pic_set_prediction_flag = false;
|
||
if (i !== 0) {
|
||
inter_ref_pic_set_prediction_flag = gb.readBool();
|
||
}
|
||
if (inter_ref_pic_set_prediction_flag) {
|
||
if (i === num_short_term_ref_pic_sets) {
|
||
gb.readUEG();
|
||
}
|
||
gb.readBool();
|
||
gb.readUEG();
|
||
let next_num_delta_pocs = 0;
|
||
for (let j = 0; j <= num_delta_pocs; j++) {
|
||
let used_by_curr_pic_flag = gb.readBool();
|
||
let use_delta_flag = false;
|
||
if (!used_by_curr_pic_flag) {
|
||
use_delta_flag = gb.readBool();
|
||
}
|
||
if (used_by_curr_pic_flag || use_delta_flag) {
|
||
next_num_delta_pocs++;
|
||
}
|
||
}
|
||
num_delta_pocs = next_num_delta_pocs;
|
||
} else {
|
||
let num_negative_pics = gb.readUEG();
|
||
let num_positive_pics = gb.readUEG();
|
||
num_delta_pocs = num_negative_pics + num_positive_pics;
|
||
for (let j = 0; j < num_negative_pics; j++) {
|
||
gb.readUEG();
|
||
gb.readBool();
|
||
}
|
||
for (let j = 0; j < num_positive_pics; j++) {
|
||
gb.readUEG();
|
||
gb.readBool();
|
||
}
|
||
}
|
||
}
|
||
let long_term_ref_pics_present_flag = gb.readBool();
|
||
if (long_term_ref_pics_present_flag) {
|
||
let num_long_term_ref_pics_sps = gb.readUEG();
|
||
for (let i = 0; i < num_long_term_ref_pics_sps; i++) {
|
||
for (let j = 0; j < log2_max_pic_order_cnt_lsb_minus4 + 4; j++) {
|
||
gb.readBits(1);
|
||
}
|
||
gb.readBits(1);
|
||
}
|
||
}
|
||
//*
|
||
let default_display_window_flag = false; // for calc offset
|
||
let min_spatial_segmentation_idc = 0; // for hvcC
|
||
let sar_width = 1,
|
||
sar_height = 1;
|
||
let fps_fixed = false,
|
||
fps_den = 1,
|
||
fps_num = 1;
|
||
//*/
|
||
gb.readBool();
|
||
gb.readBool();
|
||
let vui_parameters_present_flag = gb.readBool();
|
||
if (vui_parameters_present_flag) {
|
||
let aspect_ratio_info_present_flag = gb.readBool();
|
||
if (aspect_ratio_info_present_flag) {
|
||
let aspect_ratio_idc = gb.readByte();
|
||
let sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
|
||
let sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
|
||
if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
|
||
sar_width = sar_w_table[aspect_ratio_idc - 1];
|
||
sar_height = sar_h_table[aspect_ratio_idc - 1];
|
||
} else if (aspect_ratio_idc === 255) {
|
||
sar_width = gb.readBits(16);
|
||
sar_height = gb.readBits(16);
|
||
}
|
||
}
|
||
let overscan_info_present_flag = gb.readBool();
|
||
if (overscan_info_present_flag) {
|
||
gb.readBool();
|
||
}
|
||
let video_signal_type_present_flag = gb.readBool();
|
||
if (video_signal_type_present_flag) {
|
||
gb.readBits(3);
|
||
gb.readBool();
|
||
let colour_description_present_flag = gb.readBool();
|
||
if (colour_description_present_flag) {
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readByte();
|
||
}
|
||
}
|
||
let chroma_loc_info_present_flag = gb.readBool();
|
||
if (chroma_loc_info_present_flag) {
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
}
|
||
gb.readBool();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
default_display_window_flag = gb.readBool();
|
||
if (default_display_window_flag) {
|
||
left_offset += gb.readUEG();
|
||
right_offset += gb.readUEG();
|
||
top_offset += gb.readUEG();
|
||
bottom_offset += gb.readUEG();
|
||
}
|
||
let vui_timing_info_present_flag = gb.readBool();
|
||
if (vui_timing_info_present_flag) {
|
||
fps_den = gb.readBits(32);
|
||
fps_num = gb.readBits(32);
|
||
let vui_poc_proportional_to_timing_flag = gb.readBool();
|
||
if (vui_poc_proportional_to_timing_flag) {
|
||
gb.readUEG();
|
||
let vui_hrd_parameters_present_flag = gb.readBool();
|
||
if (vui_hrd_parameters_present_flag) {
|
||
let nal_hrd_parameters_present_flag = false;
|
||
let vcl_hrd_parameters_present_flag = false;
|
||
let sub_pic_hrd_params_present_flag = false;
|
||
{
|
||
nal_hrd_parameters_present_flag = gb.readBool();
|
||
vcl_hrd_parameters_present_flag = gb.readBool();
|
||
if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
|
||
sub_pic_hrd_params_present_flag = gb.readBool();
|
||
if (sub_pic_hrd_params_present_flag) {
|
||
gb.readByte();
|
||
gb.readBits(5);
|
||
gb.readBool();
|
||
gb.readBits(5);
|
||
}
|
||
gb.readBits(4);
|
||
gb.readBits(4);
|
||
if (sub_pic_hrd_params_present_flag) {
|
||
gb.readBits(4);
|
||
}
|
||
gb.readBits(5);
|
||
gb.readBits(5);
|
||
gb.readBits(5);
|
||
}
|
||
}
|
||
for (let i = 0; i <= max_sub_layers_minus1; i++) {
|
||
let fixed_pic_rate_general_flag = gb.readBool();
|
||
fps_fixed = fixed_pic_rate_general_flag;
|
||
let fixed_pic_rate_within_cvs_flag = false;
|
||
let cpbCnt = 1;
|
||
if (!fixed_pic_rate_general_flag) {
|
||
fixed_pic_rate_within_cvs_flag = gb.readBool();
|
||
}
|
||
let low_delay_hrd_flag = false;
|
||
if (fixed_pic_rate_within_cvs_flag) {
|
||
gb.readSEG();
|
||
} else {
|
||
low_delay_hrd_flag = gb.readBool();
|
||
}
|
||
if (!low_delay_hrd_flag) {
|
||
cpbcnt = gb.readUEG() + 1;
|
||
}
|
||
if (nal_hrd_parameters_present_flag) {
|
||
for (let j = 0; j < cpbCnt; j++) {
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
if (sub_pic_hrd_params_present_flag) {
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
}
|
||
}
|
||
}
|
||
if (vcl_hrd_parameters_present_flag) {
|
||
for (let j = 0; j < cpbCnt; j++) {
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
if (sub_pic_hrd_params_present_flag) {
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
let bitstream_restriction_flag = gb.readBool();
|
||
if (bitstream_restriction_flag) {
|
||
gb.readBool();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
min_spatial_segmentation_idc = gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
}
|
||
}
|
||
gb.readBool(); // ignore...
|
||
|
||
// for meta data
|
||
let codec_mimetype = `hvc1.${general_profile_idc}.1.L${general_level_idc}.B0`;
|
||
let codec_width = pic_width_in_luma_samples;
|
||
let codec_height = pic_height_in_luma_samples;
|
||
let sar_scale = 1;
|
||
if (sar_width !== 1 && sar_height !== 1) {
|
||
sar_scale = sar_width / sar_height;
|
||
}
|
||
gb.destroy();
|
||
gb = null;
|
||
return {
|
||
codec_mimetype,
|
||
level_string: getLevelString(general_level_idc),
|
||
profile_idc: general_profile_idc,
|
||
bit_depth: bit_depth_luma_minus8 + 8,
|
||
ref_frames: 1,
|
||
// FIXME!!!
|
||
chroma_format: chroma_format_idc,
|
||
chroma_format_string: getChromaFormatString(chroma_format_idc),
|
||
general_level_idc,
|
||
general_profile_space,
|
||
general_tier_flag,
|
||
general_profile_idc,
|
||
general_profile_compatibility_flags_1,
|
||
general_profile_compatibility_flags_2,
|
||
general_profile_compatibility_flags_3,
|
||
general_profile_compatibility_flags_4,
|
||
general_constraint_indicator_flags_1,
|
||
general_constraint_indicator_flags_2,
|
||
general_constraint_indicator_flags_3,
|
||
general_constraint_indicator_flags_4,
|
||
general_constraint_indicator_flags_5,
|
||
general_constraint_indicator_flags_6,
|
||
min_spatial_segmentation_idc,
|
||
constant_frame_rate: 0 /* FIXME!! fps_fixed ? 1 : 0? */,
|
||
chroma_format_idc,
|
||
bit_depth_luma_minus8,
|
||
bit_depth_chroma_minus8,
|
||
frame_rate: {
|
||
fixed: fps_fixed,
|
||
fps: fps_num / fps_den,
|
||
fps_den: fps_den,
|
||
fps_num: fps_num
|
||
},
|
||
sar_ratio: {
|
||
width: sar_width,
|
||
height: sar_height
|
||
},
|
||
codec_size: {
|
||
width: codec_width,
|
||
height: codec_height
|
||
},
|
||
present_size: {
|
||
width: codec_width * sar_scale,
|
||
height: codec_height
|
||
}
|
||
};
|
||
};
|
||
const parseHevcVPS = uint8array => {
|
||
let rbsp = _ebsp2rbsp(uint8array);
|
||
let gb = new ExpGolomb(rbsp);
|
||
|
||
/* remove NALu Header */
|
||
gb.readByte();
|
||
gb.readByte();
|
||
|
||
// VPS
|
||
gb.readBits(4);
|
||
gb.readBits(2);
|
||
gb.readBits(6);
|
||
let max_sub_layers_minus1 = gb.readBits(3);
|
||
let temporal_id_nesting_flag = gb.readBool();
|
||
// and more ...
|
||
|
||
return {
|
||
num_temporal_layers: max_sub_layers_minus1 + 1,
|
||
temporal_id_nested: temporal_id_nesting_flag
|
||
};
|
||
};
|
||
const parseHevcPPS = uint8array => {
|
||
let rbsp = _ebsp2rbsp(uint8array);
|
||
let gb = new ExpGolomb(rbsp);
|
||
|
||
/* remove NALu Header */
|
||
gb.readByte();
|
||
gb.readByte();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
gb.readBits(3);
|
||
gb.readBool();
|
||
gb.readBool();
|
||
gb.readUEG();
|
||
gb.readUEG();
|
||
gb.readSEG();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
let cu_qp_delta_enabled_flag = gb.readBool();
|
||
if (cu_qp_delta_enabled_flag) {
|
||
gb.readUEG();
|
||
}
|
||
gb.readSEG();
|
||
gb.readSEG();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
gb.readBool();
|
||
let tiles_enabled_flag = gb.readBool();
|
||
let entropy_coding_sync_enabled_flag = gb.readBool();
|
||
// and more ...
|
||
|
||
// needs hvcC
|
||
let parallelismType = 1; // slice-based parallel decoding
|
||
if (entropy_coding_sync_enabled_flag && tiles_enabled_flag) {
|
||
parallelismType = 0; // mixed-type parallel decoding
|
||
} else if (entropy_coding_sync_enabled_flag) {
|
||
parallelismType = 3; // wavefront-based parallel decoding
|
||
} else if (tiles_enabled_flag) {
|
||
parallelismType = 2; // tile-based parallel decoding
|
||
}
|
||
|
||
return {
|
||
parallelismType
|
||
};
|
||
};
|
||
|
||
function hevcEncoderConfigurationRecord$2(_ref2) {
|
||
let {
|
||
vps,
|
||
pps,
|
||
sps
|
||
} = _ref2;
|
||
let detail = {
|
||
configurationVersion: 1
|
||
};
|
||
const vpsDetail = parseHevcVPS(vps);
|
||
const spsDetail = parseHevcSPS(sps);
|
||
const ppsDetail = parseHevcPPS(pps);
|
||
detail = Object.assign(detail, vpsDetail, spsDetail, ppsDetail);
|
||
let length = 23 + (3 + 2 + vps.byteLength) + (3 + 2 + sps.byteLength) + (3 + 2 + pps.byteLength);
|
||
let data = new Uint8Array(length);
|
||
data[0] = 0x01; // configurationVersion
|
||
data[1] = (detail.general_profile_space & 0x03) << 6 | (detail.general_tier_flag ? 1 : 0) << 5 | detail.general_profile_idc & 0x1F;
|
||
data[2] = detail.general_profile_compatibility_flags_1 || 0;
|
||
data[3] = detail.general_profile_compatibility_flags_2 || 0;
|
||
data[4] = detail.general_profile_compatibility_flags_3 || 0;
|
||
data[5] = detail.general_profile_compatibility_flags_4 || 0;
|
||
data[6] = detail.general_constraint_indicator_flags_1 || 0;
|
||
data[7] = detail.general_constraint_indicator_flags_2 || 0;
|
||
data[8] = detail.general_constraint_indicator_flags_3 || 0;
|
||
data[9] = detail.general_constraint_indicator_flags_4 || 0;
|
||
data[10] = detail.general_constraint_indicator_flags_5 || 0;
|
||
data[11] = detail.general_constraint_indicator_flags_6 || 0;
|
||
data[12] = 0x3C;
|
||
data[13] = 0xF0 | (detail.min_spatial_segmentation_idc & 0x0F00) >> 8;
|
||
data[14] = detail.min_spatial_segmentation_idc & 0xFF;
|
||
data[15] = 0xFC | detail.parallelismType & 0x03;
|
||
data[16] = 0xFC | detail.chroma_format_idc & 0x03;
|
||
data[17] = 0xF8 | detail.bit_depth_luma_minus8 & 0x07;
|
||
data[18] = 0xF8 | detail.bit_depth_chroma_minus8 & 0x07;
|
||
data[19] = 0;
|
||
data[20] = 0;
|
||
data[21] = (detail.constant_frame_rate & 0x03) << 6 | (detail.num_temporal_layers & 0x07) << 3 | (detail.temporal_id_nested ? 1 : 0) << 2 | 3;
|
||
data[22] = 3;
|
||
data[23 + 0 + 0] = 0x80 | H265_NAL_TYPE.vps;
|
||
data[23 + 0 + 1] = 0;
|
||
data[23 + 0 + 2] = 1;
|
||
data[23 + 0 + 3] = (vps.byteLength & 0xFF00) >> 8;
|
||
data[23 + 0 + 4] = (vps.byteLength & 0x00FF) >> 0;
|
||
data.set(vps, 23 + 0 + 5);
|
||
data[23 + (5 + vps.byteLength) + 0] = 0x80 | H265_NAL_TYPE.sps;
|
||
data[23 + (5 + vps.byteLength) + 1] = 0;
|
||
data[23 + (5 + vps.byteLength) + 2] = 1;
|
||
data[23 + (5 + vps.byteLength) + 3] = (sps.byteLength & 0xFF00) >> 8;
|
||
data[23 + (5 + vps.byteLength) + 4] = (sps.byteLength & 0x00FF) >> 0;
|
||
data.set(sps, 23 + (5 + vps.byteLength) + 5);
|
||
data[23 + (5 + vps.byteLength + 5 + sps.byteLength) + 0] = 0x80 | H265_NAL_TYPE.pps;
|
||
data[23 + (5 + vps.byteLength + 5 + sps.byteLength) + 1] = 0;
|
||
data[23 + (5 + vps.byteLength + 5 + sps.byteLength) + 2] = 1;
|
||
data[23 + (5 + vps.byteLength + 5 + sps.byteLength) + 3] = (pps.byteLength & 0xFF00) >> 8;
|
||
data[23 + (5 + vps.byteLength + 5 + sps.byteLength) + 4] = (pps.byteLength & 0x00FF) >> 0;
|
||
data.set(pps, 23 + (5 + vps.byteLength + 5 + sps.byteLength) + 5);
|
||
const prevData = [0x1c, 0, 0, 0, 0];
|
||
const newData = new Uint8Array(prevData.length + data.byteLength);
|
||
newData.set(prevData, 0);
|
||
newData.set(data, prevData.length);
|
||
return newData;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param oneNALBuffer
|
||
* @param isIframe
|
||
* @returns {Uint8Array}
|
||
*/
|
||
function hevcEncoderNalePacket(oneNALBuffer, isIframe) {
|
||
// 正常发送nal
|
||
// 这边增加 是否i帧, 然后前面封装了1 + 8 个字节的数据。
|
||
const idrBit = 0x10 | 12;
|
||
const nIdrBit = 0x20 | 12;
|
||
let tmp = [];
|
||
if (isIframe) {
|
||
tmp[0] = idrBit;
|
||
} else {
|
||
tmp[0] = nIdrBit;
|
||
}
|
||
tmp[1] = 1;
|
||
//
|
||
tmp[2] = 0;
|
||
tmp[3] = 0;
|
||
tmp[4] = 0;
|
||
// 真正开始的地方。。。
|
||
tmp[5] = oneNALBuffer.byteLength >> 24 & 0xff;
|
||
tmp[6] = oneNALBuffer.byteLength >> 16 & 0xff;
|
||
tmp[7] = oneNALBuffer.byteLength >> 8 & 0xff;
|
||
tmp[8] = oneNALBuffer.byteLength & 0xff;
|
||
const arrayBuffer = new Uint8Array(tmp.length + oneNALBuffer.byteLength);
|
||
arrayBuffer.set(tmp, 0);
|
||
arrayBuffer.set(oneNALBuffer, tmp.length);
|
||
return arrayBuffer;
|
||
}
|
||
function getHevcSeqHeadType(nalu) {
|
||
return (nalu[0] & 0x7E) >> 1;
|
||
}
|
||
function isHevcSEIType(type) {
|
||
return type === H265_NAL_TYPE.sei;
|
||
}
|
||
|
||
// 32-40是VPS SPS PPS SUFFIX_SEI_NUT等
|
||
function isHevcSeqHead(type) {
|
||
return type >= 32 && type <= 40;
|
||
}
|
||
function isNotHevcSeqHead(type) {
|
||
return !isHevcSeqHead(type);
|
||
}
|
||
|
||
// 16-21是关键(I)帧
|
||
function isHevcNalIFrame(type) {
|
||
return type >= 16 && type <= 21;
|
||
}
|
||
|
||
function checkInt(value) {
|
||
return parseInt(value) === value;
|
||
}
|
||
function checkInts(arrayish) {
|
||
if (!checkInt(arrayish.length)) {
|
||
return false;
|
||
}
|
||
for (var i = 0; i < arrayish.length; i++) {
|
||
if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
function coerceArray(arg, copy) {
|
||
// ArrayBuffer view
|
||
if (arg.buffer && arg.name === 'Uint8Array') {
|
||
if (copy) {
|
||
if (arg.slice) {
|
||
arg = arg.slice();
|
||
} else {
|
||
arg = Array.prototype.slice.call(arg);
|
||
}
|
||
}
|
||
return arg;
|
||
}
|
||
|
||
// It's an array; check it is a valid representation of a byte
|
||
if (Array.isArray(arg)) {
|
||
if (!checkInts(arg)) {
|
||
throw new Error('Array contains invalid value: ' + arg);
|
||
}
|
||
return new Uint8Array(arg);
|
||
}
|
||
|
||
// Something else, but behaves like an array (maybe a Buffer? Arguments?)
|
||
if (checkInt(arg.length) && checkInts(arg)) {
|
||
return new Uint8Array(arg);
|
||
}
|
||
throw new Error('unsupported array-like object');
|
||
}
|
||
function createArray(length) {
|
||
return new Uint8Array(length);
|
||
}
|
||
function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
|
||
if (sourceStart != null || sourceEnd != null) {
|
||
if (sourceArray.slice) {
|
||
sourceArray = sourceArray.slice(sourceStart, sourceEnd);
|
||
} else {
|
||
sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
|
||
}
|
||
}
|
||
targetArray.set(sourceArray, targetStart);
|
||
}
|
||
var convertUtf8 = function () {
|
||
function toBytes(text) {
|
||
var result = [],
|
||
i = 0;
|
||
text = encodeURI(text);
|
||
while (i < text.length) {
|
||
var c = text.charCodeAt(i++);
|
||
|
||
// if it is a % sign, encode the following 2 bytes as a hex value
|
||
if (c === 37) {
|
||
result.push(parseInt(text.substr(i, 2), 16));
|
||
i += 2;
|
||
|
||
// otherwise, just the actual byte
|
||
} else {
|
||
result.push(c);
|
||
}
|
||
}
|
||
return coerceArray(result);
|
||
}
|
||
function fromBytes(bytes) {
|
||
var result = [],
|
||
i = 0;
|
||
while (i < bytes.length) {
|
||
var c = bytes[i];
|
||
if (c < 128) {
|
||
result.push(String.fromCharCode(c));
|
||
i++;
|
||
} else if (c > 191 && c < 224) {
|
||
result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
|
||
i += 2;
|
||
} else {
|
||
result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
|
||
i += 3;
|
||
}
|
||
}
|
||
return result.join('');
|
||
}
|
||
return {
|
||
toBytes: toBytes,
|
||
fromBytes: fromBytes
|
||
};
|
||
}();
|
||
var convertHex = function () {
|
||
function toBytes(text) {
|
||
var result = [];
|
||
for (var i = 0; i < text.length; i += 2) {
|
||
result.push(parseInt(text.substr(i, 2), 16));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
|
||
var Hex = '0123456789abcdef';
|
||
function fromBytes(bytes) {
|
||
var result = [];
|
||
for (var i = 0; i < bytes.length; i++) {
|
||
var v = bytes[i];
|
||
result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
|
||
}
|
||
return result.join('');
|
||
}
|
||
return {
|
||
toBytes: toBytes,
|
||
fromBytes: fromBytes
|
||
};
|
||
}();
|
||
|
||
// Number of rounds by keysize
|
||
var numberOfRounds = {
|
||
16: 10,
|
||
24: 12,
|
||
32: 14
|
||
};
|
||
|
||
// Round constant words
|
||
var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91];
|
||
|
||
// S-box and Inverse S-box (S is for Substitution)
|
||
var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
|
||
var Si = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d];
|
||
|
||
// Transformations for encryption
|
||
var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a];
|
||
var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
|
||
var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
|
||
var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c];
|
||
|
||
// Transformations for decryption
|
||
var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742];
|
||
var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
|
||
var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
|
||
var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0];
|
||
|
||
// Transformations for decryption key expansion
|
||
var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
|
||
var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
|
||
var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
|
||
var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];
|
||
function convertToInt32(bytes) {
|
||
var result = [];
|
||
for (var i = 0; i < bytes.length; i += 4) {
|
||
result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
|
||
}
|
||
return result;
|
||
}
|
||
var AesJs = function (key) {
|
||
if (!(this instanceof AesJs)) {
|
||
throw Error('AES must be instanitated with `new`');
|
||
}
|
||
Object.defineProperty(this, 'key', {
|
||
value: coerceArray(key, true)
|
||
});
|
||
this._prepare();
|
||
};
|
||
AesJs.prototype._prepare = function () {
|
||
var rounds = numberOfRounds[this.key.length];
|
||
if (rounds == null) {
|
||
throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
|
||
}
|
||
|
||
// encryption round keys
|
||
this._Ke = [];
|
||
|
||
// decryption round keys
|
||
this._Kd = [];
|
||
for (var i = 0; i <= rounds; i++) {
|
||
this._Ke.push([0, 0, 0, 0]);
|
||
this._Kd.push([0, 0, 0, 0]);
|
||
}
|
||
var roundKeyCount = (rounds + 1) * 4;
|
||
var KC = this.key.length / 4;
|
||
|
||
// convert the key into ints
|
||
var tk = convertToInt32(this.key);
|
||
|
||
// copy values into round key arrays
|
||
var index;
|
||
for (var i = 0; i < KC; i++) {
|
||
index = i >> 2;
|
||
this._Ke[index][i % 4] = tk[i];
|
||
this._Kd[rounds - index][i % 4] = tk[i];
|
||
}
|
||
|
||
// key expansion (fips-197 section 5.2)
|
||
var rconpointer = 0;
|
||
var t = KC,
|
||
tt;
|
||
while (t < roundKeyCount) {
|
||
tt = tk[KC - 1];
|
||
tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
|
||
rconpointer += 1;
|
||
|
||
// key expansion (for non-256 bit)
|
||
if (KC != 8) {
|
||
for (var i = 1; i < KC; i++) {
|
||
tk[i] ^= tk[i - 1];
|
||
}
|
||
|
||
// key expansion for 256-bit keys is "slightly different" (fips-197)
|
||
} else {
|
||
for (var i = 1; i < KC / 2; i++) {
|
||
tk[i] ^= tk[i - 1];
|
||
}
|
||
tt = tk[KC / 2 - 1];
|
||
tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
|
||
for (var i = KC / 2 + 1; i < KC; i++) {
|
||
tk[i] ^= tk[i - 1];
|
||
}
|
||
}
|
||
|
||
// copy values into round key arrays
|
||
var i = 0,
|
||
r,
|
||
c;
|
||
while (i < KC && t < roundKeyCount) {
|
||
r = t >> 2;
|
||
c = t % 4;
|
||
this._Ke[r][c] = tk[i];
|
||
this._Kd[rounds - r][c] = tk[i++];
|
||
t++;
|
||
}
|
||
}
|
||
|
||
// inverse-cipher-ify the decryption round key (fips-197 section 5.3)
|
||
for (var r = 1; r < rounds; r++) {
|
||
for (var c = 0; c < 4; c++) {
|
||
tt = this._Kd[r][c];
|
||
this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
|
||
}
|
||
}
|
||
};
|
||
AesJs.prototype.encrypt = function (plaintext) {
|
||
if (plaintext.length != 16) {
|
||
throw new Error('invalid plaintext size (must be 16 bytes)');
|
||
}
|
||
var rounds = this._Ke.length - 1;
|
||
var a = [0, 0, 0, 0];
|
||
|
||
// convert plaintext to (ints ^ key)
|
||
var t = convertToInt32(plaintext);
|
||
for (var i = 0; i < 4; i++) {
|
||
t[i] ^= this._Ke[0][i];
|
||
}
|
||
|
||
// apply round transforms
|
||
for (var r = 1; r < rounds; r++) {
|
||
for (var i = 0; i < 4; i++) {
|
||
a[i] = T1[t[i] >> 24 & 0xff] ^ T2[t[(i + 1) % 4] >> 16 & 0xff] ^ T3[t[(i + 2) % 4] >> 8 & 0xff] ^ T4[t[(i + 3) % 4] & 0xff] ^ this._Ke[r][i];
|
||
}
|
||
t = a.slice();
|
||
}
|
||
|
||
// the last round is special
|
||
var result = createArray(16),
|
||
tt;
|
||
for (var i = 0; i < 4; i++) {
|
||
tt = this._Ke[rounds][i];
|
||
result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
|
||
result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
|
||
result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
|
||
result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
|
||
}
|
||
return result;
|
||
};
|
||
AesJs.prototype.decrypt = function (ciphertext) {
|
||
if (ciphertext.length != 16) {
|
||
throw new Error('invalid ciphertext size (must be 16 bytes)');
|
||
}
|
||
var rounds = this._Kd.length - 1;
|
||
var a = [0, 0, 0, 0];
|
||
|
||
// convert plaintext to (ints ^ key)
|
||
var t = convertToInt32(ciphertext);
|
||
for (var i = 0; i < 4; i++) {
|
||
t[i] ^= this._Kd[0][i];
|
||
}
|
||
|
||
// apply round transforms
|
||
for (var r = 1; r < rounds; r++) {
|
||
for (var i = 0; i < 4; i++) {
|
||
a[i] = T5[t[i] >> 24 & 0xff] ^ T6[t[(i + 3) % 4] >> 16 & 0xff] ^ T7[t[(i + 2) % 4] >> 8 & 0xff] ^ T8[t[(i + 1) % 4] & 0xff] ^ this._Kd[r][i];
|
||
}
|
||
t = a.slice();
|
||
}
|
||
|
||
// the last round is special
|
||
var result = createArray(16),
|
||
tt;
|
||
for (var i = 0; i < 4; i++) {
|
||
tt = this._Kd[rounds][i];
|
||
result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
|
||
result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
|
||
result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
|
||
result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
|
||
}
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* Mode Of Operation - Electonic Codebook (ECB)
|
||
*/
|
||
var ModeOfOperationECB = function (key) {
|
||
if (!(this instanceof ModeOfOperationECB)) {
|
||
throw Error('AES must be instanitated with `new`');
|
||
}
|
||
this.description = "Electronic Code Block";
|
||
this.name = "ecb";
|
||
this._aes = new AesJs(key);
|
||
};
|
||
ModeOfOperationECB.prototype.encrypt = function (plaintext) {
|
||
plaintext = coerceArray(plaintext);
|
||
if (plaintext.length % 16 !== 0) {
|
||
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
|
||
}
|
||
var ciphertext = createArray(plaintext.length);
|
||
var block = createArray(16);
|
||
for (var i = 0; i < plaintext.length; i += 16) {
|
||
copyArray(plaintext, block, 0, i, i + 16);
|
||
block = this._aes.encrypt(block);
|
||
copyArray(block, ciphertext, i);
|
||
}
|
||
return ciphertext;
|
||
};
|
||
ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
|
||
ciphertext = coerceArray(ciphertext);
|
||
if (ciphertext.length % 16 !== 0) {
|
||
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
|
||
}
|
||
var plaintext = createArray(ciphertext.length);
|
||
var block = createArray(16);
|
||
for (var i = 0; i < ciphertext.length; i += 16) {
|
||
copyArray(ciphertext, block, 0, i, i + 16);
|
||
block = this._aes.decrypt(block);
|
||
copyArray(block, plaintext, i);
|
||
}
|
||
return plaintext;
|
||
};
|
||
|
||
/**
|
||
* Mode Of Operation - Cipher Block Chaining (CBC)
|
||
*/
|
||
var ModeOfOperationCBC = function (key, iv) {
|
||
if (!(this instanceof ModeOfOperationCBC)) {
|
||
throw Error('AES must be instanitated with `new`');
|
||
}
|
||
this.description = "Cipher Block Chaining";
|
||
this.name = "cbc";
|
||
if (!iv) {
|
||
iv = createArray(16);
|
||
} else if (iv.length != 16) {
|
||
throw new Error('invalid initialation vector size (must be 16 bytes)');
|
||
}
|
||
this._lastCipherblock = coerceArray(iv, true);
|
||
this._aes = new AesJs(key);
|
||
};
|
||
ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
|
||
plaintext = coerceArray(plaintext);
|
||
if (plaintext.length % 16 !== 0) {
|
||
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
|
||
}
|
||
var ciphertext = createArray(plaintext.length);
|
||
var block = createArray(16);
|
||
for (var i = 0; i < plaintext.length; i += 16) {
|
||
copyArray(plaintext, block, 0, i, i + 16);
|
||
for (var j = 0; j < 16; j++) {
|
||
block[j] ^= this._lastCipherblock[j];
|
||
}
|
||
this._lastCipherblock = this._aes.encrypt(block);
|
||
copyArray(this._lastCipherblock, ciphertext, i);
|
||
}
|
||
return ciphertext;
|
||
};
|
||
ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
|
||
ciphertext = coerceArray(ciphertext);
|
||
if (ciphertext.length % 16 !== 0) {
|
||
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
|
||
}
|
||
var plaintext = createArray(ciphertext.length);
|
||
var block = createArray(16);
|
||
for (var i = 0; i < ciphertext.length; i += 16) {
|
||
copyArray(ciphertext, block, 0, i, i + 16);
|
||
block = this._aes.decrypt(block);
|
||
for (var j = 0; j < 16; j++) {
|
||
plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
|
||
}
|
||
copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
|
||
}
|
||
return plaintext;
|
||
};
|
||
|
||
/**
|
||
* Mode Of Operation - Cipher Feedback (CFB)
|
||
*/
|
||
var ModeOfOperationCFB = function (key, iv, segmentSize) {
|
||
if (!(this instanceof ModeOfOperationCFB)) {
|
||
throw Error('AES must be instanitated with `new`');
|
||
}
|
||
this.description = "Cipher Feedback";
|
||
this.name = "cfb";
|
||
if (!iv) {
|
||
iv = createArray(16);
|
||
} else if (iv.length != 16) {
|
||
throw new Error('invalid initialation vector size (must be 16 size)');
|
||
}
|
||
if (!segmentSize) {
|
||
segmentSize = 1;
|
||
}
|
||
this.segmentSize = segmentSize;
|
||
this._shiftRegister = coerceArray(iv, true);
|
||
this._aes = new AesJs(key);
|
||
};
|
||
ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
|
||
if (plaintext.length % this.segmentSize != 0) {
|
||
throw new Error('invalid plaintext size (must be segmentSize bytes)');
|
||
}
|
||
var encrypted = coerceArray(plaintext, true);
|
||
var xorSegment;
|
||
for (var i = 0; i < encrypted.length; i += this.segmentSize) {
|
||
xorSegment = this._aes.encrypt(this._shiftRegister);
|
||
for (var j = 0; j < this.segmentSize; j++) {
|
||
encrypted[i + j] ^= xorSegment[j];
|
||
}
|
||
|
||
// Shift the register
|
||
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
|
||
copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
|
||
}
|
||
return encrypted;
|
||
};
|
||
ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
|
||
if (ciphertext.length % this.segmentSize != 0) {
|
||
throw new Error('invalid ciphertext size (must be segmentSize bytes)');
|
||
}
|
||
var plaintext = coerceArray(ciphertext, true);
|
||
var xorSegment;
|
||
for (var i = 0; i < plaintext.length; i += this.segmentSize) {
|
||
xorSegment = this._aes.encrypt(this._shiftRegister);
|
||
for (var j = 0; j < this.segmentSize; j++) {
|
||
plaintext[i + j] ^= xorSegment[j];
|
||
}
|
||
|
||
// Shift the register
|
||
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
|
||
copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
|
||
}
|
||
return plaintext;
|
||
};
|
||
|
||
/**
|
||
* Mode Of Operation - Output Feedback (OFB)
|
||
*/
|
||
var ModeOfOperationOFB = function (key, iv) {
|
||
if (!(this instanceof ModeOfOperationOFB)) {
|
||
throw Error('AES must be instanitated with `new`');
|
||
}
|
||
this.description = "Output Feedback";
|
||
this.name = "ofb";
|
||
if (!iv) {
|
||
iv = createArray(16);
|
||
} else if (iv.length != 16) {
|
||
throw new Error('invalid initialation vector size (must be 16 bytes)');
|
||
}
|
||
this._lastPrecipher = coerceArray(iv, true);
|
||
this._lastPrecipherIndex = 16;
|
||
this._aes = new AesJs(key);
|
||
};
|
||
ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
|
||
var encrypted = coerceArray(plaintext, true);
|
||
for (var i = 0; i < encrypted.length; i++) {
|
||
if (this._lastPrecipherIndex === 16) {
|
||
this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
|
||
this._lastPrecipherIndex = 0;
|
||
}
|
||
encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
|
||
}
|
||
return encrypted;
|
||
};
|
||
|
||
// Decryption is symetric
|
||
ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
|
||
|
||
/**
|
||
* Counter object for CTR common mode of operation
|
||
*/
|
||
var Counter = function (initialValue) {
|
||
if (!(this instanceof Counter)) {
|
||
throw Error('Counter must be instanitated with `new`');
|
||
}
|
||
|
||
// We allow 0, but anything false-ish uses the default 1
|
||
if (initialValue !== 0 && !initialValue) {
|
||
initialValue = 1;
|
||
}
|
||
if (typeof initialValue === 'number') {
|
||
this._counter = createArray(16);
|
||
this.setValue(initialValue);
|
||
} else {
|
||
this.setBytes(initialValue);
|
||
}
|
||
};
|
||
Counter.prototype.setValue = function (value) {
|
||
if (typeof value !== 'number' || parseInt(value) != value) {
|
||
throw new Error('invalid counter value (must be an integer)');
|
||
}
|
||
|
||
// We cannot safely handle numbers beyond the safe range for integers
|
||
if (value > Number.MAX_SAFE_INTEGER) {
|
||
throw new Error('integer value out of safe range');
|
||
}
|
||
for (var index = 15; index >= 0; --index) {
|
||
this._counter[index] = value % 256;
|
||
value = parseInt(value / 256);
|
||
}
|
||
};
|
||
Counter.prototype.setBytes = function (bytes) {
|
||
bytes = coerceArray(bytes, true);
|
||
if (bytes.length != 16) {
|
||
throw new Error('invalid counter bytes size (must be 16 bytes)');
|
||
}
|
||
this._counter = bytes;
|
||
};
|
||
Counter.prototype.increment = function () {
|
||
for (var i = 15; i >= 0; i--) {
|
||
if (this._counter[i] === 255) {
|
||
this._counter[i] = 0;
|
||
} else {
|
||
this._counter[i]++;
|
||
break;
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Mode Of Operation - Counter (CTR)
|
||
*/
|
||
var ModeOfOperationCTR = function (key, counter) {
|
||
if (!(this instanceof ModeOfOperationCTR)) {
|
||
throw Error('AES must be instanitated with `new`');
|
||
}
|
||
this.description = "Counter";
|
||
this.name = "ctr";
|
||
if (!(counter instanceof Counter)) {
|
||
counter = new Counter(counter);
|
||
}
|
||
this._counter = counter;
|
||
this._remainingCounter = null;
|
||
this._remainingCounterIndex = 16;
|
||
this._aes = new AesJs(key);
|
||
};
|
||
ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
|
||
var encrypted = coerceArray(plaintext, true);
|
||
for (var i = 0; i < encrypted.length; i++) {
|
||
if (this._remainingCounterIndex === 16) {
|
||
this._remainingCounter = this._aes.encrypt(this._counter._counter);
|
||
this._remainingCounterIndex = 0;
|
||
this._counter.increment();
|
||
}
|
||
encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
|
||
}
|
||
return encrypted;
|
||
};
|
||
|
||
// Decryption is symetric
|
||
ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
|
||
|
||
///////////////////////
|
||
// Padding
|
||
|
||
// See:https://tools.ietf.org/html/rfc2315
|
||
function pkcs7pad(data) {
|
||
data = coerceArray(data, true);
|
||
var padder = 16 - data.length % 16;
|
||
var result = createArray(data.length + padder);
|
||
copyArray(data, result);
|
||
for (var i = data.length; i < result.length; i++) {
|
||
result[i] = padder;
|
||
}
|
||
return result;
|
||
}
|
||
function pkcs7strip(data) {
|
||
data = coerceArray(data, true);
|
||
if (data.length < 16) {
|
||
throw new Error('PKCS#7 invalid length');
|
||
}
|
||
var padder = data[data.length - 1];
|
||
if (padder > 16) {
|
||
throw new Error('PKCS#7 padding byte out of range');
|
||
}
|
||
var length = data.length - padder;
|
||
for (var i = 0; i < padder; i++) {
|
||
if (data[length + i] !== padder) {
|
||
throw new Error('PKCS#7 invalid padding byte');
|
||
}
|
||
}
|
||
var result = createArray(length);
|
||
copyArray(data, result, 0, 0, length);
|
||
return result;
|
||
}
|
||
|
||
///////////////////////
|
||
// Exporting
|
||
|
||
// The block cipher
|
||
const aesjs = {
|
||
AES: AesJs,
|
||
Counter: Counter,
|
||
ModeOfOperation: {
|
||
ecb: ModeOfOperationECB,
|
||
cbc: ModeOfOperationCBC,
|
||
cfb: ModeOfOperationCFB,
|
||
ofb: ModeOfOperationOFB,
|
||
ctr: ModeOfOperationCTR
|
||
},
|
||
utils: {
|
||
hex: convertHex,
|
||
utf8: convertUtf8
|
||
},
|
||
padding: {
|
||
pkcs7: {
|
||
pad: pkcs7pad,
|
||
strip: pkcs7strip
|
||
}
|
||
},
|
||
_arrayTest: {
|
||
coerceArray: coerceArray,
|
||
createArray: createArray,
|
||
copyArray: copyArray
|
||
}
|
||
};
|
||
|
||
function getNaluLength(data) {
|
||
let length = data[3] | data[2] << 8 | data[1] << 16 | data[0] << 24;
|
||
return length;
|
||
}
|
||
|
||
// aes-256-ctr 解密
|
||
function aes256ctrDecrypt(arrayBuffer, key, iv) {
|
||
key = new Uint8Array(key);
|
||
iv = new Uint8Array(iv);
|
||
const totalLength = arrayBuffer.byteLength;
|
||
let startIndex = 5;
|
||
while (startIndex < totalLength) {
|
||
let tempNaluLength = getNaluLength(arrayBuffer.slice(startIndex, startIndex + 4));
|
||
if (tempNaluLength > totalLength) {
|
||
break;
|
||
}
|
||
let naluType = arrayBuffer[startIndex + 4];
|
||
naluType = naluType & 0x1f;
|
||
if (naluType === 1 || naluType === 5) {
|
||
const tempNalu = arrayBuffer.slice(startIndex + 4 + 2, startIndex + 4 + tempNaluLength);
|
||
let aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
||
const decryptMsg = aesCtr.decrypt(tempNalu);
|
||
// release memory
|
||
aesCtr = null;
|
||
arrayBuffer.set(decryptMsg, startIndex + 4 + 2);
|
||
}
|
||
startIndex = startIndex + 4 + tempNaluLength;
|
||
}
|
||
return arrayBuffer;
|
||
}
|
||
|
||
if (!Date.now) Date.now = function () {
|
||
return new Date().getTime();
|
||
};
|
||
function workerPostRun (Module) {
|
||
let bufferList = [];
|
||
let tempAudioBuffer = [];
|
||
let wcsVideoDecoder = {};
|
||
let abortController = new AbortController();
|
||
let socket = null;
|
||
let streamRate = null;
|
||
let streamRateAndStatsInterval = null;
|
||
let input = null;
|
||
let videoWidth = null;
|
||
let videoHeight = null;
|
||
let hasInitVideoCodec = false;
|
||
let hasInitAudioCodec = false;
|
||
let isVideoFirstIFrame = false;
|
||
// let lastDecodeVideoFrameTimestamp = 0;
|
||
// let lastDecodeVideoFrameLocalTimestamp = 0;
|
||
// let lastDecodeAudioFrameTimestamp = 0;
|
||
// let newDecodedVideoFrameTimestamp = 0;
|
||
// let newDecodedVideoFrameLocalTimestamp = 0;
|
||
// let newDecodedAudioFrameTimestamp = 0;
|
||
let isStreamTimeDiffMoreThanLocalTimeDiff = false;
|
||
let preLoopTimestamp = null;
|
||
let bufferStartDts = null;
|
||
let bufferStartLocalTs = null;
|
||
let audioOutputArray = [];
|
||
let audioRemain = 0;
|
||
let audioChannels = 0;
|
||
let preIframeTs = null;
|
||
let preTimestamp = null;
|
||
let getDefaultOpt = () => {
|
||
const defaultOptions = getDefaultPlayerOptions();
|
||
return {
|
||
debug: defaultOptions.debug,
|
||
debugLevel: defaultOptions.debugLevel,
|
||
debugUuid: defaultOptions.debugUuid,
|
||
useOffscreen: defaultOptions.useOffscreen,
|
||
useWCS: defaultOptions.useWCS,
|
||
videoBuffer: defaultOptions.videoBuffer,
|
||
videoBufferDelay: defaultOptions.videoBufferDelay,
|
||
openWebglAlignment: defaultOptions.openWebglAlignment,
|
||
playType: defaultOptions.playType,
|
||
hasAudio: defaultOptions.hasAudio,
|
||
hasVideo: defaultOptions.hasVideo,
|
||
playbackRate: 1,
|
||
playbackForwardMaxRateDecodeIFrame: defaultOptions.playbackForwardMaxRateDecodeIFrame,
|
||
playbackIsCacheBeforeDecodeForFpsRender: defaultOptions.playbackConfig.isCacheBeforeDecodeForFpsRender,
|
||
sampleRate: 0,
|
||
networkDelay: defaultOptions.networkDelay,
|
||
visibility: true,
|
||
useSIMD: defaultOptions.useSIMD,
|
||
isRecording: false,
|
||
recordType: defaultOptions.recordType,
|
||
isNakedFlow: defaultOptions.isNakedFlow,
|
||
checkFirstIFrame: defaultOptions.checkFirstIFrame,
|
||
audioBufferSize: 1024,
|
||
isCrypto: defaultOptions.isCrypto,
|
||
cryptoKey: defaultOptions.cryptoKey,
|
||
cryptoIV: defaultOptions.cryptoIV
|
||
};
|
||
};
|
||
if ("VideoEncoder" in self) {
|
||
wcsVideoDecoder = {
|
||
hasInit: false,
|
||
isEmitInfo: false,
|
||
offscreenCanvas: null,
|
||
offscreenCanvasCtx: null,
|
||
decoder: new VideoDecoder({
|
||
output: function (videoFrame) {
|
||
if (!wcsVideoDecoder.isEmitInfo) {
|
||
decoder.debug.log('worker', 'Webcodecs Video Decoder initSize');
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.initVideo,
|
||
w: videoFrame.codedWidth,
|
||
h: videoFrame.codedHeight
|
||
});
|
||
wcsVideoDecoder.isEmitInfo = true;
|
||
wcsVideoDecoder.offscreenCanvas = new OffscreenCanvas(videoFrame.codedWidth, videoFrame.codedHeight);
|
||
wcsVideoDecoder.offscreenCanvasCtx = wcsVideoDecoder.offscreenCanvas.getContext("2d");
|
||
}
|
||
if (isFunction(videoFrame.createImageBitmap)) {
|
||
videoFrame.createImageBitmap().then(image => {
|
||
wcsVideoDecoder.offscreenCanvasCtx.drawImage(image, 0, 0, videoFrame.codedWidth, videoFrame.codedHeight);
|
||
let image_bitmap = wcsVideoDecoder.offscreenCanvas.transferToImageBitmap();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.render,
|
||
buffer: image_bitmap,
|
||
delay: decoder.delay,
|
||
ts: 0
|
||
}, [image_bitmap]);
|
||
setTimeout(function () {
|
||
if (videoFrame.close) {
|
||
videoFrame.close();
|
||
} else {
|
||
videoFrame.destroy();
|
||
}
|
||
}, 100);
|
||
});
|
||
} else {
|
||
wcsVideoDecoder.offscreenCanvasCtx.drawImage(videoFrame, 0, 0, videoFrame.codedWidth, videoFrame.codedHeight);
|
||
let image_bitmap = wcsVideoDecoder.offscreenCanvas.transferToImageBitmap();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.render,
|
||
buffer: image_bitmap,
|
||
delay: decoder.delay,
|
||
ts: 0
|
||
}, [image_bitmap]);
|
||
setTimeout(function () {
|
||
if (videoFrame.close) {
|
||
videoFrame.close();
|
||
} else {
|
||
videoFrame.destroy();
|
||
}
|
||
}, 100);
|
||
}
|
||
},
|
||
error: function (error) {
|
||
decoder.debug.error('worker', 'VideoDecoder error', error);
|
||
}
|
||
}),
|
||
decode: function (payload, ts, cts) {
|
||
const isIFrame = payload[0] >> 4 === 1;
|
||
if (!wcsVideoDecoder.hasInit) {
|
||
if (isIFrame && payload[1] === 0) {
|
||
const videoCodec = payload[0] & 0x0F;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.videoCode,
|
||
code: videoCodec
|
||
});
|
||
const extraData = payload.slice(5);
|
||
const config = formatVideoDecoderConfigure(extraData);
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.videoCodec,
|
||
buffer: payload,
|
||
codecId: videoCodec
|
||
}, [payload.buffer]);
|
||
wcsVideoDecoder.decoder.configure(config);
|
||
wcsVideoDecoder.hasInit = true;
|
||
}
|
||
} else {
|
||
const chunk = new EncodedVideoChunk({
|
||
data: payload.slice(5),
|
||
timestamp: ts,
|
||
type: isIFrame ? ENCODED_VIDEO_TYPE.key : ENCODED_VIDEO_TYPE.delta
|
||
});
|
||
wcsVideoDecoder.decoder.decode(chunk);
|
||
}
|
||
},
|
||
reset() {
|
||
wcsVideoDecoder.hasInit = false;
|
||
wcsVideoDecoder.isEmitInfo = false;
|
||
wcsVideoDecoder.offscreenCanvas = null;
|
||
wcsVideoDecoder.offscreenCanvasCtx = null;
|
||
}
|
||
};
|
||
}
|
||
let abort = function () {
|
||
if (abortController) {
|
||
abortController.abort();
|
||
abortController = null;
|
||
}
|
||
};
|
||
let nakedFlowDemuxer = {
|
||
init() {
|
||
nakedFlowDemuxer.lastBuf = null;
|
||
nakedFlowDemuxer.vps = null;
|
||
nakedFlowDemuxer.sps = null;
|
||
nakedFlowDemuxer.pps = null;
|
||
nakedFlowDemuxer.streamType = null;
|
||
nakedFlowDemuxer.localDts = 0;
|
||
nakedFlowDemuxer.isSendSeqHeader = false;
|
||
},
|
||
destroy() {
|
||
nakedFlowDemuxer.lastBuf = null;
|
||
nakedFlowDemuxer.vps = null;
|
||
nakedFlowDemuxer.sps = null;
|
||
nakedFlowDemuxer.pps = null;
|
||
nakedFlowDemuxer.streamType = null;
|
||
nakedFlowDemuxer.localDts = 0;
|
||
nakedFlowDemuxer.isSendSeqHeader = false;
|
||
},
|
||
//
|
||
dispatch(data) {
|
||
const uint8Array = new Uint8Array(data);
|
||
// decoder.debug.log('worker', 'nakedFlowDemuxer dispatch', uint8Array.byteLength);
|
||
nakedFlowDemuxer.extractNALu$2(uint8Array);
|
||
// nakedFlowDemuxer.handleNALu(uint8Array);
|
||
},
|
||
|
||
getNaluDts() {
|
||
let resul = nakedFlowDemuxer.localDts;
|
||
nakedFlowDemuxer.localDts = nakedFlowDemuxer.localDts + 1000 / 25;
|
||
return resul;
|
||
},
|
||
getNaluAudioDts() {
|
||
const audioContextSampleRate = decoder._opt.sampleRate;
|
||
const audioBufferSize = decoder._opt.audioBufferSize;
|
||
return nakedFlowDemuxer.localDts + parseInt(audioBufferSize / audioContextSampleRate * 1000);
|
||
},
|
||
extractNALu(buffer) {
|
||
let i = 0,
|
||
length = buffer.byteLength,
|
||
value,
|
||
state = 0,
|
||
result = [],
|
||
lastIndex;
|
||
while (i < length) {
|
||
value = buffer[i++];
|
||
// Annex-B格式使用start code进行分割,start code为0x000001或0x00000001,SPS/PPS作为一般NALU单元以start code作为分隔符的方式放在文件或者直播流的头部。
|
||
// finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
|
||
switch (state) {
|
||
case 0:
|
||
if (value === 0) {
|
||
state = 1;
|
||
}
|
||
break;
|
||
case 1:
|
||
if (value === 0) {
|
||
state = 2;
|
||
} else {
|
||
state = 0;
|
||
}
|
||
break;
|
||
case 2:
|
||
case 3:
|
||
if (value === 0) {
|
||
state = 3;
|
||
} else if (value === 1 && i < length) {
|
||
if (lastIndex) {
|
||
result.push(buffer.subarray(lastIndex, i - state - 1));
|
||
}
|
||
lastIndex = i;
|
||
state = 0;
|
||
} else {
|
||
state = 0;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (lastIndex) {
|
||
result.push(buffer.subarray(lastIndex, length));
|
||
}
|
||
return result;
|
||
},
|
||
extractNALu$2(buffer) {
|
||
let typedArray = null;
|
||
if (!buffer || buffer.byteLength < 1) return;
|
||
if (nakedFlowDemuxer.lastBuf) {
|
||
typedArray = new Uint8Array(buffer.byteLength + nakedFlowDemuxer.lastBuf.length);
|
||
typedArray.set(nakedFlowDemuxer.lastBuf);
|
||
typedArray.set(new Uint8Array(buffer), nakedFlowDemuxer.lastBuf.length);
|
||
} else {
|
||
typedArray = new Uint8Array(buffer);
|
||
}
|
||
let lastNalEndPos = 0;
|
||
let b1 = -1; // byte before one
|
||
let b2 = -2; // byte before two
|
||
const nalStartPos = new Array();
|
||
for (let i = 0; i < typedArray.length; i += 2) {
|
||
const b_0 = typedArray[i];
|
||
const b_1 = typedArray[i + 1];
|
||
if (b1 == 0 && b_0 == 0 && b_1 == 0) {
|
||
nalStartPos.push(i - 1);
|
||
} else if (b_1 == 1 && b_0 == 0 && b1 == 0 && b2 == 0) {
|
||
nalStartPos.push(i - 2);
|
||
}
|
||
b2 = b_0;
|
||
b1 = b_1;
|
||
}
|
||
if (nalStartPos.length > 1) {
|
||
for (let i = 0; i < nalStartPos.length - 1; ++i) {
|
||
const naluItem = typedArray.subarray(nalStartPos[i], nalStartPos[i + 1] + 1);
|
||
nakedFlowDemuxer.handleNALu(naluItem);
|
||
//console.log('nakedFlowDemuxer.lastBuf nalType', nakedFlowDemuxer.lastBuf.byteLength);
|
||
lastNalEndPos = nalStartPos[i + 1];
|
||
}
|
||
} else {
|
||
lastNalEndPos = nalStartPos[0];
|
||
}
|
||
if (lastNalEndPos != 0 && lastNalEndPos < typedArray.length) {
|
||
nakedFlowDemuxer.lastBuf = typedArray.subarray(lastNalEndPos);
|
||
} else {
|
||
if (!!!nakedFlowDemuxer.lastBuf) {
|
||
nakedFlowDemuxer.lastBuf = typedArray;
|
||
}
|
||
const _newBuf = new Uint8Array(nakedFlowDemuxer.lastBuf.length + buffer.byteLength);
|
||
_newBuf.set(nakedFlowDemuxer.lastBuf);
|
||
_newBuf.set(new Uint8Array(buffer), nakedFlowDemuxer.lastBuf.length);
|
||
nakedFlowDemuxer.lastBuf = _newBuf;
|
||
}
|
||
},
|
||
handleNALu(nalu) {
|
||
if (nalu.length <= 4) {
|
||
decoder.debug.error('worker', 'handleNALu', 'nalu.length <= 4');
|
||
return;
|
||
}
|
||
// 0001 去掉前4个字节(start code)
|
||
nalu = nalu.slice(4);
|
||
nakedFlowDemuxer.handleVideoNalu(nalu);
|
||
},
|
||
handleVideoNalu(nalu) {
|
||
// decoder.debug.log('worker', 'handleVideoNalu', nalu);
|
||
if (!nakedFlowDemuxer.streamType) {
|
||
nakedFlowDemuxer.streamType = checkNaluType(nalu);
|
||
}
|
||
if (nakedFlowDemuxer.streamType === VIDEO_ENC_TYPE_SHOW.h264) {
|
||
// const nalType = getAvcSeqHeadType(nalu);
|
||
// if (nalType === H264_NAL_TYPE.pps) {
|
||
// nakedFlowDemuxer.extractH264PPS(nalu);
|
||
// } else {
|
||
// nakedFlowDemuxer.handleVideoH264Nalu(nalu);
|
||
// }
|
||
const tempNalu = nakedFlowDemuxer.handleAddNaluStartCode(nalu);
|
||
const naluList = nakedFlowDemuxer.extractNALu(tempNalu);
|
||
if (naluList.length === 0) {
|
||
decoder.debug.warn('worker', 'handleVideoNalu', 'naluList.length === 0');
|
||
return;
|
||
}
|
||
const newNaluList = [];
|
||
naluList.forEach(naluItem => {
|
||
const nalType = getAvcSeqHeadType(naluItem);
|
||
if (nalType === H264_NAL_TYPE.pps || nalType === H264_NAL_TYPE.sps) {
|
||
nakedFlowDemuxer.handleVideoH264Nalu(naluItem);
|
||
} else {
|
||
if (isNotAvcSeqHead(nalType)) {
|
||
newNaluList.push(naluItem);
|
||
}
|
||
}
|
||
});
|
||
if (newNaluList.length === 1) {
|
||
nakedFlowDemuxer.handleVideoH264Nalu(newNaluList[0]);
|
||
} else {
|
||
const isSameNaluType = isSameAvcNaluType(newNaluList);
|
||
if (isSameNaluType) {
|
||
const naluType = getAvcSeqHeadType(newNaluList[0]);
|
||
const isIFrame = isAvcNaluIFrame(naluType);
|
||
nakedFlowDemuxer.handleVideoH264NaluList(newNaluList, isIFrame, naluType);
|
||
} else {
|
||
newNaluList.forEach(naluItem => {
|
||
nakedFlowDemuxer.handleVideoH264Nalu(naluItem);
|
||
});
|
||
}
|
||
}
|
||
} else if (nakedFlowDemuxer.streamType === VIDEO_ENC_TYPE_SHOW.h265) {
|
||
const naluType = getHevcSeqHeadType(nalu);
|
||
if (naluType === H265_NAL_TYPE.pps) {
|
||
nakedFlowDemuxer.extractH265PPS(nalu);
|
||
} else {
|
||
nakedFlowDemuxer.handleVideoH265Nalu(nalu);
|
||
}
|
||
}
|
||
},
|
||
extractH264PPS(nalu) {
|
||
const tempNalu = nakedFlowDemuxer.handleAddNaluStartCode(nalu);
|
||
const naluList = nakedFlowDemuxer.extractNALu(tempNalu);
|
||
naluList.forEach(naluItem => {
|
||
const nalType = getAvcSeqHeadType(naluItem);
|
||
if (isHvcSEIType(nalType)) {
|
||
nakedFlowDemuxer.extractH264SEI(naluItem);
|
||
} else {
|
||
nakedFlowDemuxer.handleVideoH264Nalu(naluItem);
|
||
}
|
||
});
|
||
},
|
||
extractH265PPS(nalu) {
|
||
const tempNalu = nakedFlowDemuxer.handleAddNaluStartCode(nalu);
|
||
const naluList = nakedFlowDemuxer.extractNALu(tempNalu);
|
||
naluList.forEach(naluItem => {
|
||
const nalType = getHevcSeqHeadType(naluItem);
|
||
if (isHevcSEIType(nalType)) {
|
||
nakedFlowDemuxer.extractH265SEI(naluItem);
|
||
} else {
|
||
nakedFlowDemuxer.handleVideoH265Nalu(naluItem);
|
||
}
|
||
});
|
||
},
|
||
extractH264SEI(nalu) {
|
||
const tempNalu = nakedFlowDemuxer.handleAddNaluStartCode(nalu);
|
||
const naluList = nakedFlowDemuxer.extractNALu(tempNalu);
|
||
naluList.forEach(naluItem => {
|
||
nakedFlowDemuxer.handleVideoH264Nalu(naluItem);
|
||
});
|
||
},
|
||
extractH265SEI(nalu) {
|
||
const tempNalu = nakedFlowDemuxer.handleAddNaluStartCode(nalu);
|
||
const naluList = nakedFlowDemuxer.extractNALu(tempNalu);
|
||
//console.log('extractH265SEI', naluList);
|
||
naluList.forEach(naluItem => {
|
||
nakedFlowDemuxer.handleVideoH265Nalu(naluItem);
|
||
});
|
||
},
|
||
handleAddNaluStartCode(nalu) {
|
||
const prefix = [0, 0, 0, 1];
|
||
const newNalu = new Uint8Array(nalu.length + prefix.length);
|
||
newNalu.set(prefix);
|
||
newNalu.set(nalu, prefix.length);
|
||
return newNalu;
|
||
},
|
||
handleVideoH264Nalu(nalu) {
|
||
const nalType = getAvcSeqHeadType(nalu);
|
||
// decoder.debug.log('worker', `handleVideoH264Nalu nalType is ${nalu[0]} , nalu[0] is ${nalType}`);
|
||
switch (nalType) {
|
||
case H264_NAL_TYPE.sps:
|
||
nakedFlowDemuxer.sps = nalu;
|
||
// console.log('handleVideoH264Nalu sps', nalu);
|
||
break;
|
||
case H264_NAL_TYPE.pps:
|
||
nakedFlowDemuxer.pps = nalu;
|
||
// console.log('handleVideoH264Nalu pps', nalu);
|
||
break;
|
||
}
|
||
if (!nakedFlowDemuxer.isSendSeqHeader) {
|
||
if (nakedFlowDemuxer.sps && nakedFlowDemuxer.pps) {
|
||
nakedFlowDemuxer.isSendSeqHeader = true;
|
||
const seqHeader = avcEncoderConfigurationRecord({
|
||
sps: nakedFlowDemuxer.sps,
|
||
pps: nakedFlowDemuxer.pps
|
||
});
|
||
// console.log('handleVideoH264Nalu seqHeader', seqHeader);
|
||
// decoder.decodeVideo(seqHeader, 0, true, 0);
|
||
decoder.decode(seqHeader, {
|
||
type: MEDIA_TYPE.video,
|
||
ts: 0,
|
||
isIFrame: true,
|
||
cts: 0
|
||
});
|
||
}
|
||
} else {
|
||
if (isNotAvcSeqHead(nalType)) {
|
||
const isIFrame = isAvcNaluIFrame(nalType);
|
||
const dts = nakedFlowDemuxer.getNaluDts();
|
||
if (isIFrame) {
|
||
decoder.calcIframeIntervalTimestamp(dts);
|
||
}
|
||
decoder.calcNetworkDelay(dts);
|
||
const packet = avcEncoderNalePacket(nalu, isIFrame);
|
||
decoder.decode(packet, {
|
||
type: MEDIA_TYPE.video,
|
||
ts: dts,
|
||
isIFrame: isIFrame,
|
||
cts: 0
|
||
});
|
||
// decoder.decodeVideo(packet, dts, isIFrame, 0);
|
||
} else {
|
||
decoder.debug.warn('work', `handleVideoH264Nalu Avc Seq Head is ${nalType}`);
|
||
}
|
||
// console.log('handleVideoH264Nalu packet', packet);
|
||
}
|
||
},
|
||
|
||
handleVideoH264NaluList(naluList, isIFrame, naluType) {
|
||
if (nakedFlowDemuxer.isSendSeqHeader) {
|
||
const dts = nakedFlowDemuxer.getNaluDts();
|
||
decoder.calcNetworkDelay(dts);
|
||
const newNalu = naluList.reduce((pre, cur) => {
|
||
const nalu2 = addNaleHeaderLength(pre);
|
||
const nalu3 = addNaleHeaderLength(cur);
|
||
const nalu4 = new Uint8Array(nalu2.byteLength + nalu3.byteLength);
|
||
nalu4.set(nalu2, 0);
|
||
nalu4.set(nalu3, nalu2.byteLength);
|
||
return nalu4;
|
||
});
|
||
const packet = avcEncoderNalePacketNotLength(newNalu, isIFrame);
|
||
decoder.decode(packet, {
|
||
type: MEDIA_TYPE.video,
|
||
ts: dts,
|
||
isIFrame: isIFrame,
|
||
cts: 0
|
||
});
|
||
decoder.debug.log('worker', `handleVideoH264NaluList list size is ${naluList.length} package length is ${packet.byteLength} isIFrame is ${isIFrame},nalu type is ${naluType}, dts is ${dts}`);
|
||
} else {
|
||
decoder.debug.warn('worker', 'handleVideoH264NaluList isSendSeqHeader is false');
|
||
}
|
||
},
|
||
handleVideoH265Nalu(nalu) {
|
||
const nalType = getHevcSeqHeadType(nalu);
|
||
// decoder.debug.log('worker', `handleVideoH265Nalu nalType is ${nalType} , isHevcNalIFrame ${isHevcNalIFrame(nalType)} ,nalu[0] is ${nalu[0]} ,nalu.bytelength is ${nalu.byteLength}`);
|
||
switch (nalType) {
|
||
case H265_NAL_TYPE.vps:
|
||
nakedFlowDemuxer.vps = nalu;
|
||
// console.log('handleVideoH265Nalu vps', nalu);
|
||
break;
|
||
case H265_NAL_TYPE.sps:
|
||
nakedFlowDemuxer.sps = nalu;
|
||
// console.log('handleVideoH265Nalu sps', nalu);
|
||
break;
|
||
case H265_NAL_TYPE.pps:
|
||
nakedFlowDemuxer.pps = nalu;
|
||
// console.log('handleVideoH265Nalu pps', nalu);
|
||
break;
|
||
}
|
||
if (!nakedFlowDemuxer.isSendSeqHeader) {
|
||
if (nakedFlowDemuxer.vps && nakedFlowDemuxer.sps && nakedFlowDemuxer.pps) {
|
||
nakedFlowDemuxer.isSendSeqHeader = true;
|
||
const seqHeader = hevcEncoderConfigurationRecord$2({
|
||
vps: nakedFlowDemuxer.vps,
|
||
sps: nakedFlowDemuxer.sps,
|
||
pps: nakedFlowDemuxer.pps
|
||
});
|
||
// console.log('handleVideoH265Nalu seqHeader', seqHeader, seqHeader.byteLength);
|
||
// decoder.decodeVideo(seqHeader, 0, true, 0);
|
||
decoder.decode(seqHeader, {
|
||
type: MEDIA_TYPE.video,
|
||
ts: 0,
|
||
isIFrame: true,
|
||
cts: 0
|
||
});
|
||
}
|
||
} else {
|
||
if (isNotHevcSeqHead(nalType)) {
|
||
const isIFrame = isHevcNalIFrame(nalType);
|
||
const dts = nakedFlowDemuxer.getNaluDts();
|
||
decoder.calcNetworkDelay(dts);
|
||
const packet = hevcEncoderNalePacket(nalu, isIFrame);
|
||
if (isIFrame) {
|
||
decoder.calcIframeIntervalTimestamp(dts);
|
||
}
|
||
// console.log('handleVideoH265Nalu packet', nalu, packet.byteLength, dts, isIFrame);
|
||
// decoder.decodeVideo(packet, dts, isIFrame, 0);
|
||
decoder.decode(packet, {
|
||
type: MEDIA_TYPE.video,
|
||
ts: dts,
|
||
isIFrame: isIFrame,
|
||
cts: 0
|
||
});
|
||
} else {
|
||
decoder.debug.warn('work', `handleVideoH265Nalu HevcSeqHead is ${nalType}`);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
let decoder = {
|
||
isPlayer: true,
|
||
_opt: getDefaultOpt(),
|
||
startStreamRateAndStatsInterval: function () {
|
||
// decoder.debug.log('worker', 'startStreamRateAndStatsInterval');
|
||
decoder.stopStreamRateAndStatsInterval();
|
||
streamRateAndStatsInterval = setInterval(() => {
|
||
streamRate && streamRate(0);
|
||
const streamStats = JSON.stringify({
|
||
demuxBufferDelay: decoder.getVideoBufferLength(),
|
||
audioDemuxBufferDelay: decoder.getAudioBufferLength(),
|
||
flvBufferByteLength: decoder.getFlvBufferLength(),
|
||
netBuf: decoder.networkDelay || 0,
|
||
pushLatestDelay: decoder.pushLatestDelay || 0,
|
||
isDropping: decoder.dropping === true || decoder.isPushDropping === true,
|
||
isStreamTsMoreThanLocal: isStreamTimeDiffMoreThanLocalTimeDiff
|
||
});
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamStats,
|
||
value: streamStats
|
||
});
|
||
}, 1000);
|
||
},
|
||
stopStreamRateAndStatsInterval: function () {
|
||
// decoder.debug.log('worker', `stopStreamRateAndStatsInterval`);
|
||
if (streamRateAndStatsInterval) {
|
||
clearInterval(streamRateAndStatsInterval);
|
||
streamRateAndStatsInterval = null;
|
||
}
|
||
},
|
||
useOffscreen: function () {
|
||
return decoder._opt.useOffscreen && typeof OffscreenCanvas != 'undefined';
|
||
},
|
||
getDelay: function (timestamp) {
|
||
if (!timestamp || decoder._opt.hasVideo && !isVideoFirstIFrame) {
|
||
return -1;
|
||
}
|
||
if (decoder.preDelayTimestamp && decoder.preDelayTimestamp > timestamp) {
|
||
if (decoder.preDelayTimestamp - timestamp > 1000) {
|
||
decoder.debug.warn('worker', `getDelay() and preDelayTimestamp is ${decoder.preDelayTimestamp} > timestamp is ${timestamp} more than ${decoder.preDelayTimestamp - timestamp}ms`);
|
||
}
|
||
decoder.preDelayTimestamp = timestamp;
|
||
return decoder.delay;
|
||
}
|
||
if (!decoder.firstTimestamp) {
|
||
decoder.firstTimestamp = timestamp;
|
||
decoder.startTimestamp = Date.now();
|
||
decoder.delay = -1;
|
||
} else {
|
||
if (timestamp) {
|
||
const localTimestamp = Date.now() - decoder.startTimestamp;
|
||
const timeTimestamp = timestamp - decoder.firstTimestamp;
|
||
if (localTimestamp >= timeTimestamp) {
|
||
isStreamTimeDiffMoreThanLocalTimeDiff = false;
|
||
decoder.delay = localTimestamp - timeTimestamp;
|
||
} else {
|
||
// if stream ts more than local ts, support stream rate is more than 1 rate
|
||
isStreamTimeDiffMoreThanLocalTimeDiff = true;
|
||
decoder.delay = timeTimestamp - localTimestamp;
|
||
}
|
||
}
|
||
}
|
||
decoder.preDelayTimestamp = timestamp;
|
||
return decoder.delay;
|
||
},
|
||
getDelayNotUpdateDelay: function (timestamp) {
|
||
if (!timestamp || decoder._opt.hasVideo && !isVideoFirstIFrame) {
|
||
return -1;
|
||
}
|
||
if (decoder.preDelayTimestamp && decoder.preDelayTimestamp - timestamp > 1000) {
|
||
decoder.debug.warn('worker', `getDelayNotUpdateDelay and preDelayTimestamp is ${decoder.preDelayTimestamp} > timestamp is ${timestamp} more than ${decoder.preDelayTimestamp - timestamp}ms`);
|
||
return -1;
|
||
}
|
||
if (!decoder.firstTimestamp) {
|
||
return -1;
|
||
} else {
|
||
let delay = -1;
|
||
if (timestamp) {
|
||
const localTimestamp = Date.now() - decoder.startTimestamp;
|
||
const timeTimestamp = timestamp - decoder.firstTimestamp;
|
||
if (localTimestamp >= timeTimestamp) {
|
||
isStreamTimeDiffMoreThanLocalTimeDiff = false;
|
||
delay = localTimestamp - timeTimestamp;
|
||
} else {
|
||
isStreamTimeDiffMoreThanLocalTimeDiff = true;
|
||
delay = timeTimestamp - localTimestamp;
|
||
}
|
||
}
|
||
return delay;
|
||
}
|
||
},
|
||
resetDelay: function () {
|
||
decoder.firstTimestamp = null;
|
||
decoder.startTimestamp = null;
|
||
decoder.delay = -1;
|
||
},
|
||
resetAllDelay: function () {
|
||
decoder.resetDelay();
|
||
decoder.preDelayTimestamp = null;
|
||
},
|
||
doDecode: function (data) {
|
||
if (decoder._opt.useWCS && decoder.useOffscreen() && data.type === MEDIA_TYPE.video && wcsVideoDecoder.decode) {
|
||
wcsVideoDecoder.decode(data.payload, data.ts, data.cts);
|
||
} else {
|
||
data.decoder.decode(data.payload, data.ts, data.isIFrame, data.cts);
|
||
}
|
||
},
|
||
init: function () {
|
||
decoder.debug.log('worker', 'init and opt is', decoder._opt);
|
||
const isPlayer = decoder._opt.playType === PLAY_TYPE.player;
|
||
nakedFlowDemuxer.init();
|
||
decoder.isPlayer = isPlayer;
|
||
// player || (playback && not cache before decode for fps render)
|
||
if (!decoder.isPlaybackCacheBeforeDecodeForFpsRender()) {
|
||
// default loop
|
||
const loop = () => {
|
||
let data = null;
|
||
if (bufferList.length) {
|
||
if (decoder.isPushDropping) {
|
||
decoder.debug.warn('worker', `loop() isPushDropping is true and bufferList length is ${this.bufferList.length}`);
|
||
return;
|
||
}
|
||
if (decoder.dropping) {
|
||
// // dropping
|
||
data = bufferList.shift();
|
||
decoder.debug.warn('worker', `loop() dropBuffer is dropping and isIFrame ${data.isIFrame} and delay is ${decoder.delay} and bufferlist is ${bufferList.length}`);
|
||
//
|
||
while (!data.isIFrame && bufferList.length) {
|
||
// decoder.debug.log('worker', 'loop is dropping = true, isIFrame is', data.isIFrame);
|
||
// dropping
|
||
data = bufferList.shift();
|
||
}
|
||
const tempDelay = decoder.getDelayNotUpdateDelay(data.ts);
|
||
if (data.isIFrame && tempDelay <= decoder.getNotDroppingDelayTs()) {
|
||
decoder.debug.log('worker', 'loop() is dropping = false, is iFrame');
|
||
decoder.dropping = false;
|
||
decoder.doDecode(data);
|
||
}
|
||
} else {
|
||
data = bufferList[0];
|
||
if (decoder.getDelay(data.ts) === -1) {
|
||
decoder.debug.log('worker', 'loop() common dumex delay is -1 ,data.ts is', data.ts);
|
||
bufferList.shift();
|
||
decoder.doDecode(data);
|
||
} else if (decoder.delay > decoder._opt.videoBuffer + decoder._opt.videoBufferDelay && isPlayer) {
|
||
if (decoder.hasIframeInBufferList()) {
|
||
decoder.debug.log('worker', `delay is ${decoder.delay}, set dropping is true`);
|
||
decoder.resetAllDelay();
|
||
decoder.dropping = true;
|
||
} else {
|
||
bufferList.shift();
|
||
decoder.doDecode(data);
|
||
}
|
||
} else {
|
||
// 持续解码
|
||
while (bufferList.length) {
|
||
data = bufferList[0];
|
||
if (decoder.getDelay(data.ts) > decoder._opt.videoBuffer) {
|
||
bufferList.shift();
|
||
decoder.doDecode(data);
|
||
} else {
|
||
if (decoder.delay < 0) {
|
||
decoder.debug.warn('worker', `loop() do not decode and delay is ${decoder.delay}, bufferList is ${bufferList.length}`);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (decoder.delay !== -1) {
|
||
decoder.debug.log('worker', 'loop() bufferList is empty and reset delay');
|
||
}
|
||
decoder.resetAllDelay();
|
||
}
|
||
};
|
||
decoder.stopId = setInterval(() => {
|
||
let nowTime = new Date().getTime();
|
||
if (!preLoopTimestamp) {
|
||
preLoopTimestamp = nowTime;
|
||
}
|
||
const diffTime = nowTime - preLoopTimestamp;
|
||
if (diffTime > 100) {
|
||
decoder.debug.warn('worker', `loop demux diff time is ${diffTime}`);
|
||
}
|
||
loop();
|
||
preLoopTimestamp = new Date().getTime();
|
||
}, 10);
|
||
} else {
|
||
decoder.debug.log('worker', 'playback and playbackIsCacheBeforeDecodeForFpsRender is true');
|
||
}
|
||
// if not check first frame is iframe, will set isVideoFirstIFrame = true
|
||
if (!decoder._opt.checkFirstIFrame) {
|
||
isVideoFirstIFrame = true;
|
||
}
|
||
},
|
||
playbackCacheLoop: function () {
|
||
if (decoder.stopId) {
|
||
clearInterval(decoder.stopId);
|
||
decoder.stopId = null;
|
||
}
|
||
const loop = () => {
|
||
let data = null;
|
||
if (bufferList.length) {
|
||
data = bufferList.shift();
|
||
decoder.doDecode(data);
|
||
}
|
||
};
|
||
loop();
|
||
const fragDuration = Math.ceil(1000 / (decoder.streamFps * decoder._opt.playbackRate));
|
||
decoder.debug.log('worker', `playbackCacheLoop fragDuration is ${fragDuration}, streamFps is ${decoder.streamFps}, playbackRate is ${decoder._opt.playbackRate}`);
|
||
decoder.stopId = setInterval(loop, fragDuration);
|
||
},
|
||
close: function () {
|
||
decoder.debug.log('worker', 'close');
|
||
decoder.stopStreamRateAndStatsInterval();
|
||
clearInterval(decoder.stopId);
|
||
decoder.stopId = null;
|
||
audioDecoder.clear && audioDecoder.clear();
|
||
videoDecoder.clear && videoDecoder.clear();
|
||
audioDecoder = null;
|
||
videoDecoder = null;
|
||
streamRate = null;
|
||
preLoopTimestamp = null;
|
||
isStreamTimeDiffMoreThanLocalTimeDiff = false;
|
||
// lastDecodeVideoFrameTimestamp = 0;
|
||
// lastDecodeVideoFrameLocalTimestamp = 0;
|
||
// lastDecodeAudioFrameTimestamp = 0;
|
||
// newDecodedVideoFrameTimestamp = 0;
|
||
// newDecodedVideoFrameLocalTimestamp = 0;
|
||
// newDecodedAudioFrameTimestamp = 0;
|
||
wcsVideoDecoder.reset && wcsVideoDecoder.reset();
|
||
decoder.firstTimestamp = null;
|
||
decoder.startTimestamp = null;
|
||
decoder.networkDelay = 0;
|
||
decoder.streamFps = null; // audio + video all fps
|
||
decoder.streamAudioFps = null; //
|
||
decoder.streamVideoFps = null;
|
||
decoder.delay = -1;
|
||
decoder.pushLatestDelay = -1;
|
||
decoder.preDelayTimestamp = null;
|
||
decoder.dropping = false;
|
||
decoder.isPushDropping = false;
|
||
decoder.isPlayer = true;
|
||
decoder._opt = getDefaultOpt();
|
||
if (decoder.webglObj) {
|
||
decoder.webglObj.destroy();
|
||
decoder.offscreenCanvas = null;
|
||
decoder.offscreenCanvasGL = null;
|
||
decoder.offscreenCanvasCtx = null;
|
||
}
|
||
bufferList = [];
|
||
tempAudioBuffer = [];
|
||
abort();
|
||
input = null;
|
||
if (socket) {
|
||
socket.close();
|
||
socket = null;
|
||
}
|
||
videoWidth = null;
|
||
videoHeight = null;
|
||
hasInitVideoCodec = false;
|
||
hasInitAudioCodec = false;
|
||
isVideoFirstIFrame = false;
|
||
audioOutputArray = [];
|
||
audioRemain = 0;
|
||
audioChannels = 0;
|
||
bufferStartDts = null;
|
||
bufferStartLocalTs = null;
|
||
preIframeTs = null;
|
||
preTimestamp = null;
|
||
nakedFlowDemuxer.destroy();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.closeEnd
|
||
});
|
||
},
|
||
pushBuffer: function (bufferData, options) {
|
||
if (options.type === MEDIA_TYPE.audio && isAacCodecPacket(bufferData)) {
|
||
decoder.decodeAudio(bufferData, options.ts);
|
||
return;
|
||
}
|
||
if (decoder.isPlayer && preTimestamp > 0 && options.type === MEDIA_TYPE.video && options.ts < preTimestamp && preTimestamp - options.ts > FRAME_TS_MAX_DIFF) {
|
||
decoder.debug.warn('worker', `pushBuffer, preTimestamp is ${preTimestamp}, options.ts is ${options.ts}, diff is ${preTimestamp - options.ts} more than ${FRAME_TS_MAX_DIFF}, resetAllDelay`);
|
||
decoder.resetAllDelay();
|
||
}
|
||
if (decoder.isPlayer && options.ts <= preTimestamp && preTimestamp > 0 && options.type === MEDIA_TYPE.video) {
|
||
decoder.debug.warn('worker', `pushBuffer, options.ts is ${options.ts} less than (or equal) preTimestamp is ${preTimestamp} and type is ${options.type}`);
|
||
}
|
||
|
||
// decoder.debug.error('worker', `pushBuffer and type is ${options.type}, isIframe is ${options.isIFrame}, ts is ${options.ts}`);
|
||
|
||
if (decoder.isPlayer && isVideoFirstIFrame) {
|
||
const maxDelay = decoder._opt.videoBuffer + decoder._opt.videoBufferDelay;
|
||
const pushLatestDelay = decoder.getDelayNotUpdateDelay(options.ts);
|
||
// update push latest delay
|
||
decoder.pushLatestDelay = pushLatestDelay;
|
||
if (pushLatestDelay > maxDelay && decoder.delay < maxDelay && decoder.delay > 0) {
|
||
if (decoder.hasIframeInBufferList() && decoder.isPushDropping === false) {
|
||
decoder.debug.log('worker', `pushBuffer, pushLatestDelay is ${pushLatestDelay} more than ${maxDelay} and decoder.delay is ${decoder.delay} and has iIframe and next decoder.dropBuffer$2()`);
|
||
decoder.dropBuffer$2();
|
||
}
|
||
}
|
||
}
|
||
if (options.type === MEDIA_TYPE.video) {
|
||
preTimestamp = options.ts;
|
||
}
|
||
// 音频
|
||
if (options.type === MEDIA_TYPE.audio) {
|
||
bufferList.push({
|
||
ts: options.ts,
|
||
payload: bufferData,
|
||
decoder: {
|
||
decode: decoder.decodeAudio
|
||
},
|
||
type: MEDIA_TYPE.audio,
|
||
isIFrame: false
|
||
});
|
||
} else if (options.type === MEDIA_TYPE.video) {
|
||
bufferList.push({
|
||
ts: options.ts,
|
||
cts: options.cts,
|
||
payload: bufferData,
|
||
decoder: {
|
||
decode: decoder.decodeVideo
|
||
},
|
||
type: MEDIA_TYPE.video,
|
||
isIFrame: options.isIFrame
|
||
});
|
||
}
|
||
if (decoder.isPlaybackCacheBeforeDecodeForFpsRender()) {
|
||
if (isEmpty(decoder.streamVideoFps) || isEmpty(decoder.streamAudioFps)) {
|
||
let streamVideoFps = decoder.streamVideoFps;
|
||
let streamAudioFps = decoder.streamAudioFps;
|
||
if (isEmpty(decoder.streamVideoFps)) {
|
||
streamVideoFps = calcStreamFpsByBufferList(bufferList, MEDIA_TYPE.video);
|
||
if (streamVideoFps) {
|
||
decoder.streamVideoFps = streamVideoFps;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.playbackStreamVideoFps,
|
||
value: decoder.streamVideoFps
|
||
});
|
||
if (streamAudioFps) {
|
||
decoder.streamFps = streamVideoFps + streamAudioFps;
|
||
} else {
|
||
decoder.streamFps = streamVideoFps;
|
||
}
|
||
decoder.playbackCacheLoop();
|
||
}
|
||
}
|
||
if (isEmpty(decoder.streamAudioFps)) {
|
||
streamAudioFps = calcStreamFpsByBufferList(bufferList, MEDIA_TYPE.audio);
|
||
if (streamAudioFps) {
|
||
decoder.streamAudioFps = streamAudioFps;
|
||
if (streamVideoFps) {
|
||
decoder.streamFps = streamVideoFps + streamAudioFps;
|
||
} else {
|
||
decoder.streamFps = streamAudioFps;
|
||
}
|
||
decoder.playbackCacheLoop();
|
||
}
|
||
}
|
||
if (isEmpty(decoder.streamVideoFps) && isEmpty(decoder.streamAudioFps)) {
|
||
decoder.debug.log('worker', `playbackCacheBeforeDecodeForFpsRender, calc streamAudioFps is ${streamAudioFps}, streamVideoFps is ${streamVideoFps}, bufferListLength is ${bufferList.length}`);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
// video buffer length
|
||
getVideoBufferLength() {
|
||
let result = 0;
|
||
bufferList.forEach(item => {
|
||
if (item.type === MEDIA_TYPE.video) {
|
||
result += 1;
|
||
}
|
||
});
|
||
return result;
|
||
},
|
||
hasIframeInBufferList() {
|
||
return bufferList.some(item => {
|
||
return item.type === MEDIA_TYPE.video && item.isIFrame;
|
||
});
|
||
},
|
||
getNotDroppingDelayTs() {
|
||
return decoder._opt.videoBuffer + decoder._opt.videoBufferDelay / 2;
|
||
},
|
||
getAudioBufferLength() {
|
||
let result = 0;
|
||
bufferList.forEach(item => {
|
||
if (item.type === MEDIA_TYPE.audio) {
|
||
result += 1;
|
||
}
|
||
});
|
||
return result;
|
||
},
|
||
getFlvBufferLength() {
|
||
let result = 0;
|
||
if (input && input.buffer) {
|
||
result = input.buffer.byteLength;
|
||
}
|
||
if (decoder._opt.isNakedFlow) {
|
||
if (nakedFlowDemuxer.lastBuf) {
|
||
result = nakedFlowDemuxer.lastBuf.byteLength;
|
||
}
|
||
}
|
||
return result;
|
||
},
|
||
fetchStream: function (url, options) {
|
||
decoder.debug.log('worker', 'fetchStream, url is ' + url, 'options:', JSON.stringify(options));
|
||
streamRate = calculationRate(rate => {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamRate,
|
||
value: rate
|
||
});
|
||
});
|
||
decoder.startStreamRateAndStatsInterval();
|
||
if (options.protocol === PLAYER_PLAY_PROTOCOL.fetch) {
|
||
input = new OPut(decoder.demuxFlv());
|
||
fetch(url, {
|
||
signal: abortController.signal
|
||
}).then(res => {
|
||
if (!isFetchSuccess(res)) {
|
||
decoder.debug.error('worker', `fetch response status is ${res.status} and ok is ${res.ok} and emit error and next abort()`);
|
||
abort();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS_ERROR.fetchError,
|
||
value: `fetch response status is ${res.status} and ok is ${res.ok}`
|
||
});
|
||
return;
|
||
}
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamSuccess
|
||
});
|
||
if (supportWritableStream()) {
|
||
res.body.pipeTo(new WritableStream({
|
||
write: value => {
|
||
streamRate(value.byteLength);
|
||
input.write(value);
|
||
},
|
||
close: () => {
|
||
input = null;
|
||
},
|
||
abort: e => {
|
||
input = null;
|
||
if (e.name === FETCH_ERROR.abort) {
|
||
return;
|
||
}
|
||
abort();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS_ERROR.fetchError,
|
||
value: e.toString()
|
||
});
|
||
}
|
||
}));
|
||
} else {
|
||
const reader = res.body.getReader();
|
||
const fetchNext = () => {
|
||
reader.read().then(_ref => {
|
||
let {
|
||
done,
|
||
value
|
||
} = _ref;
|
||
if (done) {
|
||
input = null;
|
||
return;
|
||
}
|
||
streamRate(value.byteLength);
|
||
input.write(value);
|
||
fetchNext();
|
||
}).catch(e => {
|
||
input = null;
|
||
if (e.name === FETCH_ERROR.abort) {
|
||
return;
|
||
}
|
||
abort();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS_ERROR.fetchError,
|
||
value: e.toString()
|
||
});
|
||
});
|
||
};
|
||
fetchNext();
|
||
}
|
||
}).catch(e => {
|
||
if (e.name === FETCH_ERROR.abort) {
|
||
return;
|
||
}
|
||
abort();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS_ERROR.fetchError,
|
||
value: e.toString()
|
||
});
|
||
input = null;
|
||
});
|
||
} else if (options.protocol === PLAYER_PLAY_PROTOCOL.websocket) {
|
||
if (options.isFlv) {
|
||
input = new OPut(decoder.demuxFlv());
|
||
}
|
||
socket = new WebSocket(url);
|
||
socket.binaryType = 'arraybuffer';
|
||
socket.onopen = () => {
|
||
decoder.debug.log('worker', 'fetchStream, WebsocketStream socket open');
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamSuccess
|
||
});
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.websocketOpen
|
||
});
|
||
};
|
||
socket.onclose = () => {
|
||
decoder.debug.log('worker', 'fetchStream, WebsocketStream socket close');
|
||
input = null;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamEnd
|
||
});
|
||
};
|
||
socket.onerror = error => {
|
||
decoder.debug.error('worker', 'fetchStream, WebsocketStream socket error', error.toString());
|
||
input = null;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS_ERROR.websocketError,
|
||
value: error.toString()
|
||
});
|
||
};
|
||
socket.onmessage = event => {
|
||
streamRate(event.data.byteLength);
|
||
// decoder.debug.log('worker',` socket on message isFlv is ${options.isFlv}`);
|
||
|
||
if (options.isFlv) {
|
||
input.write(event.data);
|
||
// decoder.debug.log('worker',` input.bufferList.length is ${input.buffer.length},byteLength is ${input.buffer.byteLength}`);
|
||
} else if (decoder._opt.isNakedFlow) {
|
||
decoder.demuxNakedFlow(event.data);
|
||
} else {
|
||
decoder.demuxM7s(event.data);
|
||
}
|
||
};
|
||
}
|
||
},
|
||
demuxFlv: function* () {
|
||
yield 9;
|
||
const tmp = new ArrayBuffer(4);
|
||
const tmp8 = new Uint8Array(tmp);
|
||
const tmp32 = new Uint32Array(tmp);
|
||
while (true) {
|
||
tmp8[3] = 0;
|
||
const t = yield 15;
|
||
const type = t[4];
|
||
tmp8[0] = t[7];
|
||
tmp8[1] = t[6];
|
||
tmp8[2] = t[5];
|
||
const length = tmp32[0];
|
||
tmp8[0] = t[10];
|
||
tmp8[1] = t[9];
|
||
tmp8[2] = t[8];
|
||
let ts = tmp32[0];
|
||
if (ts === 0xFFFFFF) {
|
||
tmp8[3] = t[11];
|
||
ts = tmp32[0];
|
||
}
|
||
const payload = (yield length).slice();
|
||
// console.log('demuxFlv type is', type);
|
||
switch (type) {
|
||
case FLV_MEDIA_TYPE.audio:
|
||
decoder.decode(payload, {
|
||
type: MEDIA_TYPE.audio,
|
||
ts
|
||
});
|
||
break;
|
||
case FLV_MEDIA_TYPE.video:
|
||
if (payload.byteLength > 0) {
|
||
const isIFrame = payload[0] >> 4 === 1;
|
||
// just for player
|
||
if (decoder.isPlayer) {
|
||
decoder.calcNetworkDelay(ts);
|
||
if (isIFrame) {
|
||
decoder.calcIframeIntervalTimestamp(ts);
|
||
}
|
||
}
|
||
tmp32[0] = payload[4];
|
||
tmp32[1] = payload[3];
|
||
tmp32[2] = payload[2];
|
||
tmp32[3] = 0;
|
||
let cts = tmp32[0];
|
||
let payloadBuffer = payload;
|
||
if (decoder._opt.isCrypto) {
|
||
if (decoder._opt.cryptoIV && decoder._opt.cryptoIV.byteLength > 0 && decoder._opt.cryptoKey && decoder._opt.cryptoKey.byteLength > 0) {
|
||
payloadBuffer = aes256ctrDecrypt(payload, decoder._opt.cryptoKey, decoder._opt.cryptoIV);
|
||
} else {
|
||
decoder.debug.error('worker', `cryptoKey.length is ${decoder._opt.cryptoKey && decoder._opt.cryptoKey.byteLength} or cryptoIV.length is ${decoder._opt.cryptoIV && decoder._opt.cryptoIV.byteLength} null`);
|
||
}
|
||
}
|
||
|
||
// decoder.debug.log('worker',`demuxFlv dts:${ts},isIFrame:${isIFrame}`);
|
||
decoder.decode(payloadBuffer, {
|
||
type: MEDIA_TYPE.video,
|
||
ts,
|
||
isIFrame,
|
||
cts
|
||
});
|
||
}
|
||
break;
|
||
default:
|
||
decoder.debug.log('worker', `demuxFlv() type is ${type}`);
|
||
break;
|
||
}
|
||
}
|
||
},
|
||
decode: function (payload, options) {
|
||
let playType = decoder._opt.playType;
|
||
if (options.type === MEDIA_TYPE.audio) {
|
||
if (decoder._opt.hasAudio) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamAbps,
|
||
value: payload.byteLength
|
||
});
|
||
if (playType === PLAY_TYPE.player) {
|
||
decoder.pushBuffer(payload, {
|
||
type: options.type,
|
||
ts: options.ts,
|
||
cts: options.cts
|
||
});
|
||
} else if (playType === PLAY_TYPE.playbackTF) {
|
||
if (decoder.isPlaybackCacheBeforeDecodeForFpsRender()) {
|
||
decoder.pushBuffer(payload, {
|
||
type: options.type,
|
||
ts: options.ts,
|
||
cts: options.cts
|
||
});
|
||
} else {
|
||
if (decoder._opt.playbackRate === 1) {
|
||
decoder.pushBuffer(payload, {
|
||
type: options.type,
|
||
ts: options.ts,
|
||
cts: options.cts
|
||
});
|
||
} else {
|
||
decoder.decodeAudio(payload, options.ts);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else if (options.type === MEDIA_TYPE.video) {
|
||
if (decoder._opt.hasVideo) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamVbps,
|
||
value: payload.byteLength
|
||
});
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.streamDts,
|
||
value: options.ts
|
||
});
|
||
if (playType === PLAY_TYPE.player) {
|
||
// decoder.debug.log('worker','fetchStream, decode video isIFrame is', options.isIFrame);
|
||
decoder.pushBuffer(payload, {
|
||
type: options.type,
|
||
ts: options.ts,
|
||
isIFrame: options.isIFrame,
|
||
cts: options.cts
|
||
});
|
||
} else if (playType === PLAY_TYPE.playbackTF) {
|
||
if (decoder._opt.playbackRate >= decoder._opt.playbackForwardMaxRateDecodeIFrame) {
|
||
if (options.isIFrame) {
|
||
decoder.decodeVideo(payload, options.ts, options.isIFrame, options.cts);
|
||
}
|
||
} else {
|
||
if (decoder.isPlaybackCacheBeforeDecodeForFpsRender()) {
|
||
decoder.pushBuffer(payload, {
|
||
type: options.type,
|
||
ts: options.ts,
|
||
cts: options.cts,
|
||
isIFrame: options.isIFrame
|
||
});
|
||
} else {
|
||
if (decoder._opt.playbackRate === 1) {
|
||
decoder.pushBuffer(payload, {
|
||
type: options.type,
|
||
ts: options.ts,
|
||
cts: options.cts,
|
||
isIFrame: options.isIFrame
|
||
});
|
||
} else {
|
||
decoder.decodeVideo(payload, options.ts, options.isIFrame, options.cts);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
setCodecAudio: function (payload) {
|
||
const codecId = payload[0] >> 4;
|
||
if (isAacCodecPacket(payload) || codecId === AUDIO_ENC_CODE.ALAW || codecId === AUDIO_ENC_CODE.MULAW) {
|
||
decoder.debug.log('worker', `setCodecAudio: init audio codec, codeId is ${codecId}`);
|
||
const extraData = codecId === AUDIO_ENC_CODE.AAC ? payload.slice(2) : payload.slice(1);
|
||
audioDecoder.setCodec(codecId, decoder._opt.sampleRate, extraData);
|
||
hasInitAudioCodec = true;
|
||
} else {
|
||
decoder.debug.warn('worker', 'setCodecAudio: hasInitAudioCodec is false, codecId is ', codecId);
|
||
}
|
||
},
|
||
decodeAudio: function (payload, ts) {
|
||
const codecId = payload[0] >> 4;
|
||
if (!hasInitAudioCodec) {
|
||
decoder.setCodecAudio(payload);
|
||
} else {
|
||
// lastDecodeAudioFrameTimestamp = ts;
|
||
audioDecoder.decode(codecId === AUDIO_ENC_CODE.AAC ? payload.slice(2) : payload.slice(1), ts);
|
||
}
|
||
},
|
||
setCodecVideo: function (payload) {
|
||
const codecId = payload[0] & 0x0F;
|
||
if (payload[0] >> 4 === FRAME_TYPE.keyFrame && payload[1] === AVC_PACKET_TYPE.sequenceHeader) {
|
||
if (codecId === VIDEO_ENC_CODE.h264 || codecId === VIDEO_ENC_CODE.h265) {
|
||
decoder.debug.log('worker', `setCodecVideo: init video codec , codecId is ${codecId}`);
|
||
const extraData = payload.slice(5);
|
||
if (codecId === VIDEO_ENC_CODE.h264 && decoder._opt.useSIMD) {
|
||
// need check width and height is more than 4080。
|
||
const avcConfig = parseAVCDecoderConfigurationRecord(extraData);
|
||
if (avcConfig.codecWidth > SIMD_H264_DECODE_MAX_WIDTH || avcConfig.codecHeight > SIMD_H264_DECODE_MAX_WIDTH) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.simdH264DecodeVideoWidthIsTooLarge
|
||
});
|
||
decoder.debug.warn('worker', `setCodecVideo: SIMD H264 decode video width is too large, width is ${avcConfig.codecWidth}, height is ${avcConfig.codecHeight}`);
|
||
return;
|
||
}
|
||
}
|
||
hasInitVideoCodec = true;
|
||
videoDecoder.setCodec(codecId, extraData);
|
||
if (decoder._opt.recordType === FILE_SUFFIX.mp4) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.videoCodec,
|
||
buffer: payload,
|
||
codecId
|
||
}, [payload.buffer]);
|
||
}
|
||
} else {
|
||
decoder.debug.warn('worker', `setCodecVideo: hasInitVideoCodec is false, codecId is ${codecId} is not H264 or H265`);
|
||
}
|
||
} else {
|
||
decoder.debug.warn('worker', `decodeVideo: hasInitVideoCodec is false, codecId is ${codecId} and frameType is ${payload[0] >> 4} and packetType is ${payload[1]}`);
|
||
}
|
||
},
|
||
decodeVideo: function (payload, ts, isIFrame, cts) {
|
||
if (!hasInitVideoCodec) {
|
||
decoder.setCodecVideo(payload);
|
||
} else {
|
||
if (!isVideoFirstIFrame && isIFrame) {
|
||
isVideoFirstIFrame = true;
|
||
}
|
||
if (isVideoFirstIFrame) {
|
||
const buffer = payload.slice(5);
|
||
// lastDecodeVideoFrameTimestamp = ts;
|
||
// lastDecodeVideoFrameLocalTimestamp = now();
|
||
videoDecoder.decode(buffer, isIFrame ? 1 : 0, ts);
|
||
if (decoder._opt.isRecording && decoder._opt.recordType === FILE_SUFFIX.mp4) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.videoNalu,
|
||
buffer: buffer,
|
||
isIFrame,
|
||
ts,
|
||
cts
|
||
}, [payload.buffer]);
|
||
}
|
||
} else {
|
||
decoder.debug.warn('worker', 'decodeVideo: first frame is not iframe');
|
||
}
|
||
}
|
||
},
|
||
clearBuffer: function () {
|
||
let needClear = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||
decoder.debug.log('worker', `clearBuffer,bufferList length is ${bufferList.length}, need clear is ${needClear}`);
|
||
if (needClear) {
|
||
bufferList = [];
|
||
}
|
||
decoder.resetAllDelay();
|
||
decoder.dropping = true;
|
||
},
|
||
dropBuffer$2: function () {
|
||
if (bufferList.length > 0) {
|
||
decoder.isPushDropping = true;
|
||
const iFrameIndex = bufferList.findIndex(bufferItem => {
|
||
return bufferItem.isIFrame === true && bufferItem.type === MEDIA_TYPE.video;
|
||
});
|
||
if (iFrameIndex >= 0) {
|
||
//
|
||
bufferList = bufferList.slice(iFrameIndex);
|
||
const iFrameItem = bufferList.shift();
|
||
const tempDelay = decoder.getDelayNotUpdateDelay(iFrameItem.ts);
|
||
decoder.doDecode(iFrameItem);
|
||
decoder.isPushDropping = false;
|
||
decoder.debug.log('worker', `dropBuffer$2() iFrameIndex is ${iFrameIndex},and bufferList length is ${bufferList.length} and tempDelay is ${tempDelay} ,delay is ${decoder.delay} `);
|
||
} else {
|
||
decoder.isPushDropping = false;
|
||
}
|
||
}
|
||
if (bufferList.length === 0) {
|
||
decoder.isPushDropping = false;
|
||
}
|
||
},
|
||
demuxM7s: function (arrayBuffer) {
|
||
const dv = new DataView(arrayBuffer);
|
||
const ts = dv.getUint32(1, false);
|
||
const type = dv.getUint8(0);
|
||
// decoder.debug.log('worker', `demuxM7s , type is ${type}`);
|
||
switch (type) {
|
||
case MEDIA_TYPE.audio:
|
||
decoder.decode(new Uint8Array(arrayBuffer, 5), {
|
||
type: MEDIA_TYPE.audio,
|
||
ts
|
||
});
|
||
break;
|
||
case MEDIA_TYPE.video:
|
||
if (dv.byteLength > 5) {
|
||
const isIFrame = dv.getUint8(5) >> 4 === 1;
|
||
if (decoder.isPlayer) {
|
||
decoder.calcNetworkDelay(ts);
|
||
if (isIFrame) {
|
||
decoder.calcIframeIntervalTimestamp(ts);
|
||
}
|
||
}
|
||
decoder.decode(new Uint8Array(arrayBuffer, 5), {
|
||
type: MEDIA_TYPE.video,
|
||
ts,
|
||
isIFrame
|
||
});
|
||
}
|
||
break;
|
||
}
|
||
},
|
||
demuxNakedFlow: function (arrayBuffer) {
|
||
// decoder.debug.log('worker', `demuxNakedFlow, arrayBuffer length is ${arrayBuffer.byteLength}`);
|
||
// console.log('demuxNakedFlow', arrayBuffer);
|
||
nakedFlowDemuxer.dispatch(arrayBuffer);
|
||
},
|
||
calcNetworkDelay: function (dts) {
|
||
if (!(isVideoFirstIFrame && dts > 0)) {
|
||
return;
|
||
}
|
||
if (bufferStartDts === null) {
|
||
bufferStartDts = dts;
|
||
bufferStartLocalTs = now();
|
||
} else {
|
||
if (dts < bufferStartDts) {
|
||
decoder.debug.warn('worker', `calcNetworkDelay, dts is ${dts} less than bufferStartDts is ${bufferStartDts}`);
|
||
bufferStartDts = dts;
|
||
bufferStartLocalTs = now();
|
||
}
|
||
}
|
||
const diff1 = dts - bufferStartDts;
|
||
const localDiff = now() - bufferStartLocalTs;
|
||
const delay = localDiff > diff1 ? localDiff - diff1 : 0;
|
||
decoder.networkDelay = delay;
|
||
if (delay > decoder._opt.networkDelay && decoder._opt.playType === PLAY_TYPE.player) {
|
||
decoder.debug.warn('worker', `calcNetworkDelay now dts:${dts}, start dts is ${bufferStartDts} vs start is ${diff1},local diff is ${localDiff} ,delay is ${delay}`);
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerFetch,
|
||
type: EVENTS.networkDelayTimeout,
|
||
value: delay
|
||
});
|
||
}
|
||
},
|
||
calcIframeIntervalTimestamp: function (ts) {
|
||
if (preIframeTs === null) {
|
||
preIframeTs = ts;
|
||
} else {
|
||
if (preIframeTs < ts) {
|
||
const intervalTimestamp = ts - preIframeTs;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.iframeIntervalTs,
|
||
value: intervalTimestamp
|
||
});
|
||
// post 到主线程里面去。
|
||
preIframeTs = ts;
|
||
}
|
||
}
|
||
},
|
||
canVisibilityDecodeNotDrop: function () {
|
||
return decoder._opt.visibility && videoWidth * videoHeight <= 1920 * 1080;
|
||
},
|
||
isPlaybackCacheBeforeDecodeForFpsRender: function () {
|
||
return !decoder.isPlayer && decoder._opt.playbackIsCacheBeforeDecodeForFpsRender;
|
||
},
|
||
videoInfo: function (videoCode, width, height) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.videoCode,
|
||
code: videoCode
|
||
});
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.initVideo,
|
||
w: width,
|
||
h: height
|
||
});
|
||
videoWidth = width;
|
||
videoHeight = height;
|
||
if (decoder.useOffscreen()) {
|
||
decoder.offscreenCanvas = new OffscreenCanvas(width, height);
|
||
decoder.offscreenCanvasGL = decoder.offscreenCanvas.getContext("webgl");
|
||
decoder.webglObj = createWebGL(decoder.offscreenCanvasGL, decoder._opt.openWebglAlignment);
|
||
}
|
||
},
|
||
audioInfo: function (audioCode, sampleRate, channels) {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.audioCode,
|
||
code: audioCode
|
||
});
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.initAudio,
|
||
sampleRate: sampleRate,
|
||
channels: channels
|
||
});
|
||
audioChannels = channels;
|
||
},
|
||
yuvData: function (yuv, ts) {
|
||
const size = videoWidth * videoHeight * 3 / 2;
|
||
let out = Module.HEAPU8.subarray(yuv, yuv + size);
|
||
let data = new Uint8Array(out);
|
||
// newDecodedVideoFrameTimestamp = ts;
|
||
// newDecodedVideoFrameLocalTimestamp = now();
|
||
if (decoder.useOffscreen()) {
|
||
decoder.webglObj.renderYUV(videoWidth, videoHeight, data);
|
||
let image_bitmap = decoder.offscreenCanvas.transferToImageBitmap();
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.render,
|
||
buffer: image_bitmap,
|
||
delay: decoder.delay,
|
||
ts
|
||
}, [image_bitmap]);
|
||
} else {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.render,
|
||
output: data,
|
||
delay: decoder.delay,
|
||
ts
|
||
}, [data.buffer]);
|
||
}
|
||
},
|
||
pcmData: function (data, len, ts) {
|
||
let frameCount = len;
|
||
let origin = [];
|
||
let start = 0;
|
||
let audioBufferSize = decoder._opt.audioBufferSize;
|
||
for (let channel = 0; channel < 2; channel++) {
|
||
let fp = Module.HEAPU32[(data >> 2) + channel] >> 2;
|
||
origin[channel] = Module.HEAPF32.subarray(fp, fp + frameCount);
|
||
}
|
||
if (audioRemain) {
|
||
len = audioBufferSize - audioRemain;
|
||
if (frameCount >= len) {
|
||
audioOutputArray[0] = Float32Array.of(...tempAudioBuffer[0], ...origin[0].subarray(0, len));
|
||
if (audioChannels == 2) {
|
||
audioOutputArray[1] = Float32Array.of(...tempAudioBuffer[1], ...origin[1].subarray(0, len));
|
||
}
|
||
// newDecodedAudioFrameTimestamp = ts;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.playAudio,
|
||
buffer: audioOutputArray,
|
||
ts
|
||
}, audioOutputArray.map(x => x.buffer));
|
||
start = len;
|
||
frameCount -= len;
|
||
} else {
|
||
audioRemain += frameCount;
|
||
tempAudioBuffer[0] = Float32Array.of(...tempAudioBuffer[0], ...origin[0]);
|
||
if (audioChannels == 2) {
|
||
tempAudioBuffer[1] = Float32Array.of(...tempAudioBuffer[1], ...origin[1]);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
for (audioRemain = frameCount; audioRemain >= audioBufferSize; audioRemain -= audioBufferSize) {
|
||
audioOutputArray[0] = origin[0].slice(start, start += audioBufferSize);
|
||
if (audioChannels == 2) {
|
||
audioOutputArray[1] = origin[1].slice(start - audioBufferSize, start);
|
||
}
|
||
// newDecodedAudioFrameTimestamp = ts;
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.playAudio,
|
||
buffer: audioOutputArray,
|
||
ts
|
||
}, audioOutputArray.map(x => x.buffer));
|
||
}
|
||
if (audioRemain) {
|
||
tempAudioBuffer[0] = origin[0].slice(start);
|
||
if (audioChannels == 2) {
|
||
tempAudioBuffer[1] = origin[1].slice(start);
|
||
}
|
||
}
|
||
},
|
||
sendWebsocketMessage: function (message) {
|
||
if (socket) {
|
||
if (socket.readyState === WEBSOCKET_STATUS_CODE.open) {
|
||
socket.send(message);
|
||
} else {
|
||
decoder.debug.error('worker', 'socket is not open');
|
||
}
|
||
} else {
|
||
decoder.debug.error('worker', 'socket is null');
|
||
}
|
||
},
|
||
timeEnd: function () {
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.workerEnd
|
||
});
|
||
}
|
||
};
|
||
decoder.debug = new Debug(decoder);
|
||
let audioDecoder = new Module.AudioDecoder(decoder);
|
||
let videoDecoder = new Module.VideoDecoder(decoder);
|
||
postMessage({
|
||
cmd: WORKER_CMD_TYPE.init
|
||
});
|
||
self.onmessage = function (event) {
|
||
let msg = event.data;
|
||
switch (msg.cmd) {
|
||
case WORKER_SEND_TYPE.init:
|
||
try {
|
||
decoder._opt = Object.assign(decoder._opt, JSON.parse(msg.opt));
|
||
} catch (e) {}
|
||
decoder.init();
|
||
break;
|
||
case WORKER_SEND_TYPE.decode:
|
||
decoder.pushBuffer(msg.buffer, msg.options);
|
||
break;
|
||
case WORKER_SEND_TYPE.audioDecode:
|
||
decoder.decodeAudio(msg.buffer, msg.ts);
|
||
break;
|
||
case WORKER_SEND_TYPE.videoDecode:
|
||
decoder.decodeVideo(msg.buffer, msg.ts, msg.isIFrame);
|
||
break;
|
||
case WORKER_SEND_TYPE.clearBuffer:
|
||
decoder.clearBuffer(msg.needClear);
|
||
break;
|
||
case WORKER_SEND_TYPE.fetchStream:
|
||
decoder.fetchStream(msg.url, JSON.parse(msg.opt));
|
||
break;
|
||
case WORKER_SEND_TYPE.close:
|
||
decoder.close();
|
||
break;
|
||
case WORKER_SEND_TYPE.updateConfig:
|
||
decoder._opt[msg.key] = msg.value;
|
||
if (msg.key === 'playbackRate') {
|
||
if (decoder.isPlaybackCacheBeforeDecodeForFpsRender()) {
|
||
decoder.playbackCacheLoop();
|
||
}
|
||
}
|
||
break;
|
||
case WORKER_SEND_TYPE.sendWsMessage:
|
||
decoder.sendWebsocketMessage(msg.message);
|
||
break;
|
||
}
|
||
};
|
||
}
|
||
|
||
decoderProSimd.postRun = function () {
|
||
workerPostRun(decoderProSimd);
|
||
};
|
||
|
||
}));
|
||
|