(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 = ""; }); }, 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
Format: column-major, when typed out it looks like row-major
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 * * 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); }; }));