UNPKG

livekit-client

Version:

JavaScript/TypeScript client SDK for LiveKit

1,290 lines (1,234 loc) 101 kB
/****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ function __awaiter(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()); }); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var loglevel$1 = {exports: {}}; /* * loglevel - https://github.com/pimterry/loglevel * * Copyright (c) 2013 Tim Perry * Licensed under the MIT license. */ var loglevel = loglevel$1.exports; var hasRequiredLoglevel; function requireLoglevel() { if (hasRequiredLoglevel) return loglevel$1.exports; hasRequiredLoglevel = 1; (function (module) { (function (root, definition) { if (module.exports) { module.exports = definition(); } else { root.log = definition(); } })(loglevel, function () { // Slightly dubious tricks to cut down minimized file size var noop = function () {}; var undefinedType = "undefined"; var isIE = typeof window !== undefinedType && typeof window.navigator !== undefinedType && /Trident\/|MSIE /.test(window.navigator.userAgent); var logMethods = ["trace", "debug", "info", "warn", "error"]; var _loggersByName = {}; var defaultLogger = null; // Cross-browser bind equivalent that works at least back to IE6 function bindMethod(obj, methodName) { var method = obj[methodName]; if (typeof method.bind === 'function') { return method.bind(obj); } else { try { return Function.prototype.bind.call(method, obj); } catch (e) { // Missing bind shim or IE8 + Modernizr, fallback to wrapping return function () { return Function.prototype.apply.apply(method, [obj, arguments]); }; } } } // Trace() doesn't print the message in IE, so for that case we need to wrap it function traceForIE() { if (console.log) { if (console.log.apply) { console.log.apply(console, arguments); } else { // In old IE, native console methods themselves don't have apply(). Function.prototype.apply.apply(console.log, [console, arguments]); } } if (console.trace) console.trace(); } // Build the best logging method possible for this env // Wherever possible we want to bind, not wrap, to preserve stack traces function realMethod(methodName) { if (methodName === 'debug') { methodName = 'log'; } if (typeof console === undefinedType) { return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives } else if (methodName === 'trace' && isIE) { return traceForIE; } else if (console[methodName] !== undefined) { return bindMethod(console, methodName); } else if (console.log !== undefined) { return bindMethod(console, 'log'); } else { return noop; } } // These private functions always need `this` to be set properly function replaceLoggingMethods() { /*jshint validthis:true */ var level = this.getLevel(); // Replace the actual methods. for (var i = 0; i < logMethods.length; i++) { var methodName = logMethods[i]; this[methodName] = i < level ? noop : this.methodFactory(methodName, level, this.name); } // Define log.log as an alias for log.debug this.log = this.debug; // Return any important warnings. if (typeof console === undefinedType && level < this.levels.SILENT) { return "No console available for logging"; } } // In old IE versions, the console isn't present until you first open it. // We build realMethod() replacements here that regenerate logging methods function enableLoggingWhenConsoleArrives(methodName) { return function () { if (typeof console !== undefinedType) { replaceLoggingMethods.call(this); this[methodName].apply(this, arguments); } }; } // By default, we use closely bound real methods wherever possible, and // otherwise we wait for a console to appear, and then try again. function defaultMethodFactory(methodName, _level, _loggerName) { /*jshint validthis:true */ return realMethod(methodName) || enableLoggingWhenConsoleArrives.apply(this, arguments); } function Logger(name, factory) { // Private instance variables. var self = this; /** * The level inherited from a parent logger (or a global default). We * cache this here rather than delegating to the parent so that it stays * in sync with the actual logging methods that we have installed (the * parent could change levels but we might not have rebuilt the loggers * in this child yet). * @type {number} */ var inheritedLevel; /** * The default level for this logger, if any. If set, this overrides * `inheritedLevel`. * @type {number|null} */ var defaultLevel; /** * A user-specific level for this logger. If set, this overrides * `defaultLevel`. * @type {number|null} */ var userLevel; var storageKey = "loglevel"; if (typeof name === "string") { storageKey += ":" + name; } else if (typeof name === "symbol") { storageKey = undefined; } function persistLevelIfPossible(levelNum) { var levelName = (logMethods[levelNum] || 'silent').toUpperCase(); if (typeof window === undefinedType || !storageKey) return; // Use localStorage if available try { window.localStorage[storageKey] = levelName; return; } catch (ignore) {} // Use session cookie as fallback try { window.document.cookie = encodeURIComponent(storageKey) + "=" + levelName + ";"; } catch (ignore) {} } function getPersistedLevel() { var storedLevel; if (typeof window === undefinedType || !storageKey) return; try { storedLevel = window.localStorage[storageKey]; } catch (ignore) {} // Fallback to cookies if local storage gives us nothing if (typeof storedLevel === undefinedType) { try { var cookie = window.document.cookie; var cookieName = encodeURIComponent(storageKey); var location = cookie.indexOf(cookieName + "="); if (location !== -1) { storedLevel = /^([^;]+)/.exec(cookie.slice(location + cookieName.length + 1))[1]; } } catch (ignore) {} } // If the stored level is not valid, treat it as if nothing was stored. if (self.levels[storedLevel] === undefined) { storedLevel = undefined; } return storedLevel; } function clearPersistedLevel() { if (typeof window === undefinedType || !storageKey) return; // Use localStorage if available try { window.localStorage.removeItem(storageKey); } catch (ignore) {} // Use session cookie as fallback try { window.document.cookie = encodeURIComponent(storageKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC"; } catch (ignore) {} } function normalizeLevel(input) { var level = input; if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) { level = self.levels[level.toUpperCase()]; } if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) { return level; } else { throw new TypeError("log.setLevel() called with invalid level: " + input); } } /* * * Public logger API - see https://github.com/pimterry/loglevel for details * */ self.name = name; self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3, "ERROR": 4, "SILENT": 5 }; self.methodFactory = factory || defaultMethodFactory; self.getLevel = function () { if (userLevel != null) { return userLevel; } else if (defaultLevel != null) { return defaultLevel; } else { return inheritedLevel; } }; self.setLevel = function (level, persist) { userLevel = normalizeLevel(level); if (persist !== false) { // defaults to true persistLevelIfPossible(userLevel); } // NOTE: in v2, this should call rebuild(), which updates children. return replaceLoggingMethods.call(self); }; self.setDefaultLevel = function (level) { defaultLevel = normalizeLevel(level); if (!getPersistedLevel()) { self.setLevel(level, false); } }; self.resetLevel = function () { userLevel = null; clearPersistedLevel(); replaceLoggingMethods.call(self); }; self.enableAll = function (persist) { self.setLevel(self.levels.TRACE, persist); }; self.disableAll = function (persist) { self.setLevel(self.levels.SILENT, persist); }; self.rebuild = function () { if (defaultLogger !== self) { inheritedLevel = normalizeLevel(defaultLogger.getLevel()); } replaceLoggingMethods.call(self); if (defaultLogger === self) { for (var childName in _loggersByName) { _loggersByName[childName].rebuild(); } } }; // Initialize all the internal levels. inheritedLevel = normalizeLevel(defaultLogger ? defaultLogger.getLevel() : "WARN"); var initialLevel = getPersistedLevel(); if (initialLevel != null) { userLevel = normalizeLevel(initialLevel); } replaceLoggingMethods.call(self); } /* * * Top-level API * */ defaultLogger = new Logger(); defaultLogger.getLogger = function getLogger(name) { if (typeof name !== "symbol" && typeof name !== "string" || name === "") { throw new TypeError("You must supply a name when creating a logger."); } var logger = _loggersByName[name]; if (!logger) { logger = _loggersByName[name] = new Logger(name, defaultLogger.methodFactory); } return logger; }; // Grab the current global log variable in case of overwrite var _log = typeof window !== undefinedType ? window.log : undefined; defaultLogger.noConflict = function () { if (typeof window !== undefinedType && window.log === defaultLogger) { window.log = _log; } return defaultLogger; }; defaultLogger.getLoggers = function getLoggers() { return _loggersByName; }; // ES6 default export, for compatibility defaultLogger['default'] = defaultLogger; return defaultLogger; }); })(loglevel$1); return loglevel$1.exports; } var loglevelExports = requireLoglevel(); var LogLevel; (function (LogLevel) { LogLevel[LogLevel["trace"] = 0] = "trace"; LogLevel[LogLevel["debug"] = 1] = "debug"; LogLevel[LogLevel["info"] = 2] = "info"; LogLevel[LogLevel["warn"] = 3] = "warn"; LogLevel[LogLevel["error"] = 4] = "error"; LogLevel[LogLevel["silent"] = 5] = "silent"; })(LogLevel || (LogLevel = {})); var LoggerNames; (function (LoggerNames) { LoggerNames["Default"] = "livekit"; LoggerNames["Room"] = "livekit-room"; LoggerNames["TokenSource"] = "livekit-token-source"; LoggerNames["Participant"] = "livekit-participant"; LoggerNames["Track"] = "livekit-track"; LoggerNames["Publication"] = "livekit-track-publication"; LoggerNames["Engine"] = "livekit-engine"; LoggerNames["Signal"] = "livekit-signal"; LoggerNames["PCManager"] = "livekit-pc-manager"; LoggerNames["PCTransport"] = "livekit-pc-transport"; LoggerNames["E2EE"] = "lk-e2ee"; LoggerNames["DataTracks"] = "livekit-data-tracks"; })(LoggerNames || (LoggerNames = {})); let livekitLogger = loglevelExports.getLogger('livekit'); Object.values(LoggerNames).map(name => loglevelExports.getLogger(name)); livekitLogger.setDefaultLevel(LogLevel.info); const workerLogger = loglevelExports.getLogger('lk-e2ee'); var e = Object.defineProperty; var h = (i, s, t) => s in i ? e(i, s, { enumerable: true, configurable: true, writable: true, value: t }) : i[s] = t; var o = (i, s, t) => h(i, typeof s != "symbol" ? s + "" : s, t); class _ { constructor() { o(this, "_locking"); o(this, "_locks"); this._locking = Promise.resolve(), this._locks = 0; } isLocked() { return this._locks > 0; } lock() { this._locks += 1; let s; const t = new Promise(l => s = () => { this._locks -= 1, l(); }), c = this._locking.then(() => s); return this._locking = this._locking.then(() => t), c; } } var QueueTaskStatus; (function (QueueTaskStatus) { QueueTaskStatus[QueueTaskStatus["WAITING"] = 0] = "WAITING"; QueueTaskStatus[QueueTaskStatus["RUNNING"] = 1] = "RUNNING"; QueueTaskStatus[QueueTaskStatus["COMPLETED"] = 2] = "COMPLETED"; })(QueueTaskStatus || (QueueTaskStatus = {})); class AsyncQueue { constructor() { this.pendingTasks = new Map(); this.taskMutex = new _(); this.nextTaskIndex = 0; } run(task) { return __awaiter(this, void 0, void 0, function* () { const taskInfo = { id: this.nextTaskIndex++, enqueuedAt: Date.now(), status: QueueTaskStatus.WAITING }; this.pendingTasks.set(taskInfo.id, taskInfo); const unlock = yield this.taskMutex.lock(); try { taskInfo.executedAt = Date.now(); taskInfo.status = QueueTaskStatus.RUNNING; return yield task(); } finally { taskInfo.status = QueueTaskStatus.COMPLETED; this.pendingTasks.delete(taskInfo.id); unlock(); } }); } flush() { return __awaiter(this, void 0, void 0, function* () { return this.run(() => __awaiter(this, void 0, void 0, function* () {})); }); } snapshot() { return Array.from(this.pendingTasks.values()); } } const ENCRYPTION_ALGORITHM = 'AES-GCM'; // How many consecutive frames can fail decrypting before a particular key gets marked as invalid const DECRYPTION_FAILURE_TOLERANCE = 10; // We copy the first bytes of the VP8 payload unencrypted. // For keyframes this is 10 bytes, for non-keyframes (delta) 3. See // https://tools.ietf.org/html/rfc6386#section-9.1 // This allows the bridge to continue detecting keyframes (only one byte needed in the JVB) // and is also a bit easier for the VP8 decoder (i.e. it generates funny garbage pictures // instead of being unable to decode). // This is a bit for show and we might want to reduce to 1 unconditionally in the final version. // // For audio (where frame.type is not set) we do not encrypt the opus TOC byte: // https://tools.ietf.org/html/rfc6716#section-3.1 const UNENCRYPTED_BYTES = { key: 10, delta: 3, audio: 1, // frame.type is not set on audio, so this is set manually empty: 0 }; /* We use a 12 byte bit IV. This is signalled in plain together with the packet. See https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters */ const IV_LENGTH = 12; const SALT = 'LKFrameEncryptionKey'; const KEY_PROVIDER_DEFAULTS = { sharedKey: false, ratchetSalt: SALT, ratchetWindowSize: 8, failureTolerance: DECRYPTION_FAILURE_TOLERANCE, keyringSize: 16, keySize: 128 }; /** Base error that all LiveKit specific custom errors inherit from. */ class LivekitError extends Error { constructor(code, message, options) { super(message || 'an error has occurred'); this.name = 'LiveKitError'; this.code = code; if (typeof (options === null || options === void 0 ? void 0 : options.cause) !== 'undefined') { this.cause = options === null || options === void 0 ? void 0 : options.cause; } } } var ConnectionErrorReason; (function (ConnectionErrorReason) { ConnectionErrorReason[ConnectionErrorReason["NotAllowed"] = 0] = "NotAllowed"; ConnectionErrorReason[ConnectionErrorReason["ServerUnreachable"] = 1] = "ServerUnreachable"; ConnectionErrorReason[ConnectionErrorReason["InternalError"] = 2] = "InternalError"; ConnectionErrorReason[ConnectionErrorReason["Cancelled"] = 3] = "Cancelled"; ConnectionErrorReason[ConnectionErrorReason["LeaveRequest"] = 4] = "LeaveRequest"; ConnectionErrorReason[ConnectionErrorReason["Timeout"] = 5] = "Timeout"; ConnectionErrorReason[ConnectionErrorReason["WebSocket"] = 6] = "WebSocket"; ConnectionErrorReason[ConnectionErrorReason["ServiceNotFound"] = 7] = "ServiceNotFound"; })(ConnectionErrorReason || (ConnectionErrorReason = {})); // NOTE: matches with https://github.com/livekit/client-sdk-swift/blob/f37bbd260d61e165084962db822c79f995f1a113/Sources/LiveKit/DataStream/StreamError.swift#L17 var DataStreamErrorReason; (function (DataStreamErrorReason) { // Unable to open a stream with the same ID more than once. DataStreamErrorReason[DataStreamErrorReason["AlreadyOpened"] = 0] = "AlreadyOpened"; // Stream closed abnormally by remote participant. DataStreamErrorReason[DataStreamErrorReason["AbnormalEnd"] = 1] = "AbnormalEnd"; // Incoming chunk data could not be decoded. DataStreamErrorReason[DataStreamErrorReason["DecodeFailed"] = 2] = "DecodeFailed"; // Read length exceeded total length specified in stream header. DataStreamErrorReason[DataStreamErrorReason["LengthExceeded"] = 3] = "LengthExceeded"; // Read length less than total length specified in stream header. DataStreamErrorReason[DataStreamErrorReason["Incomplete"] = 4] = "Incomplete"; // Unable to register a stream handler more than once. DataStreamErrorReason[DataStreamErrorReason["HandlerAlreadyRegistered"] = 7] = "HandlerAlreadyRegistered"; // Encryption type mismatch. DataStreamErrorReason[DataStreamErrorReason["EncryptionTypeMismatch"] = 8] = "EncryptionTypeMismatch"; })(DataStreamErrorReason || (DataStreamErrorReason = {})); var MediaDeviceFailure; (function (MediaDeviceFailure) { // user rejected permissions MediaDeviceFailure["PermissionDenied"] = "PermissionDenied"; // device is not available MediaDeviceFailure["NotFound"] = "NotFound"; // device is in use. On Windows, only a single tab may get access to a device at a time. MediaDeviceFailure["DeviceInUse"] = "DeviceInUse"; MediaDeviceFailure["Other"] = "Other"; })(MediaDeviceFailure || (MediaDeviceFailure = {})); (function (MediaDeviceFailure) { function getFailure(error) { if (error && 'name' in error) { if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') { return MediaDeviceFailure.NotFound; } if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') { return MediaDeviceFailure.PermissionDenied; } if (error.name === 'NotReadableError' || error.name === 'TrackStartError') { return MediaDeviceFailure.DeviceInUse; } return MediaDeviceFailure.Other; } } MediaDeviceFailure.getFailure = getFailure; })(MediaDeviceFailure || (MediaDeviceFailure = {})); var CryptorErrorReason; (function (CryptorErrorReason) { CryptorErrorReason[CryptorErrorReason["InvalidKey"] = 0] = "InvalidKey"; CryptorErrorReason[CryptorErrorReason["MissingKey"] = 1] = "MissingKey"; CryptorErrorReason[CryptorErrorReason["InternalError"] = 2] = "InternalError"; })(CryptorErrorReason || (CryptorErrorReason = {})); class CryptorError extends LivekitError { constructor(message) { let reason = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : CryptorErrorReason.InternalError; let participantIdentity = arguments.length > 2 ? arguments[2] : undefined; super(40, message); this.reason = reason; this.participantIdentity = participantIdentity; } } var KeyProviderEvent; (function (KeyProviderEvent) { KeyProviderEvent["SetKey"] = "setKey"; /** Event for requesting to ratchet the key used to encrypt the stream */ KeyProviderEvent["RatchetRequest"] = "ratchetRequest"; /** Emitted when a key is ratcheted. Could be after auto-ratcheting on decryption failure or * following a `RatchetRequest`, will contain the ratcheted key material */ KeyProviderEvent["KeyRatcheted"] = "keyRatcheted"; })(KeyProviderEvent || (KeyProviderEvent = {})); var KeyHandlerEvent; (function (KeyHandlerEvent) { /** Emitted when a key has been ratcheted. Is emitted when any key has been ratcheted * i.e. when the FrameCryptor tried to ratchet when decryption is failing */ KeyHandlerEvent["KeyRatcheted"] = "keyRatcheted"; })(KeyHandlerEvent || (KeyHandlerEvent = {})); var EncryptionEvent; (function (EncryptionEvent) { EncryptionEvent["ParticipantEncryptionStatusChanged"] = "participantEncryptionStatusChanged"; EncryptionEvent["EncryptionError"] = "encryptionError"; })(EncryptionEvent || (EncryptionEvent = {})); var CryptorEvent; (function (CryptorEvent) { CryptorEvent["Error"] = "cryptorError"; })(CryptorEvent || (CryptorEvent = {})); function isVideoFrame(frame) { return 'type' in frame; } function importKey(keyBytes_1) { return __awaiter(this, arguments, void 0, function (keyBytes) { let algorithm = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { name: ENCRYPTION_ALGORITHM }; let usage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'encrypt'; return function* () { // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey return crypto.subtle.importKey('raw', keyBytes, algorithm, false, usage === 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt']); }(); }); } function getAlgoOptions(algorithmName, salt) { const textEncoder = new TextEncoder(); const encodedSalt = textEncoder.encode(salt); switch (algorithmName) { case 'HKDF': return { name: 'HKDF', salt: encodedSalt, hash: 'SHA-256', info: new ArrayBuffer(128) }; case 'PBKDF2': { return { name: 'PBKDF2', salt: encodedSalt, hash: 'SHA-256', iterations: 100000 }; } default: throw new Error("algorithm ".concat(algorithmName, " is currently unsupported")); } } /** * Derives a set of keys from the master key. * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1 */ function deriveKeys(material, options) { return __awaiter(this, void 0, void 0, function* () { const algorithmOptions = getAlgoOptions(material.algorithm.name, options.ratchetSalt); // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams const encryptionKey = yield crypto.subtle.deriveKey(algorithmOptions, material, { name: ENCRYPTION_ALGORITHM, length: options.keySize }, false, ['encrypt', 'decrypt']); return { material, encryptionKey }; }); } /** * Ratchets a key. See * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1 */ function ratchet(material, salt) { return __awaiter(this, void 0, void 0, function* () { const algorithmOptions = getAlgoOptions(material.algorithm.name, salt); // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits return crypto.subtle.deriveBits(algorithmOptions, material, 256); }); } function needsRbspUnescaping(frameData) { for (var i = 0; i < frameData.length - 3; i++) { if (frameData[i] == 0 && frameData[i + 1] == 0 && frameData[i + 2] == 3) return true; } return false; } function parseRbsp(stream) { const dataOut = []; var length = stream.length; for (var i = 0; i < stream.length;) { // Be careful about over/underflow here. byte_length_ - 3 can underflow, and // i + 3 can overflow, but byte_length_ - i can't, because i < byte_length_ // above, and that expression will produce the number of bytes left in // the stream including the byte at i. if (length - i >= 3 && !stream[i] && !stream[i + 1] && stream[i + 2] == 3) { // Two rbsp bytes. dataOut.push(stream[i++]); dataOut.push(stream[i++]); // Skip the emulation byte. i++; } else { // Single rbsp byte. dataOut.push(stream[i++]); } } return new Uint8Array(dataOut); } const kZerosInStartSequence = 2; const kEmulationByte = 3; function writeRbsp(data_in) { const dataOut = []; var numConsecutiveZeros = 0; for (var i = 0; i < data_in.length; ++i) { var byte = data_in[i]; if (byte <= kEmulationByte && numConsecutiveZeros >= kZerosInStartSequence) { // Need to escape. dataOut.push(kEmulationByte); numConsecutiveZeros = 0; } dataOut.push(byte); if (byte == 0) { ++numConsecutiveZeros; } else { numConsecutiveZeros = 0; } } return new Uint8Array(dataOut); } class DataCryptor { static makeIV(timestamp) { const iv = new ArrayBuffer(12); const ivView = new DataView(iv); const randomBytes = crypto.getRandomValues(new Uint32Array(1)); ivView.setUint32(0, randomBytes[0]); ivView.setUint32(4, timestamp); ivView.setUint32(8, timestamp - DataCryptor.sendCount % 0xffff); DataCryptor.sendCount++; return iv; } static encrypt(data, keys) { return __awaiter(this, void 0, void 0, function* () { const iv = DataCryptor.makeIV(performance.now()); const keySet = yield keys.getKeySet(); if (!keySet) { throw new Error('No key set found'); } const cipherText = yield crypto.subtle.encrypt({ name: ENCRYPTION_ALGORITHM, iv }, keySet.encryptionKey, new Uint8Array(data)); return { payload: new Uint8Array(cipherText), iv: new Uint8Array(iv), keyIndex: keys.getCurrentKeyIndex() }; }); } static decrypt(data_1, iv_1, keys_1) { return __awaiter(this, arguments, void 0, function (data, iv, keys) { let keyIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; let initialMaterial = arguments.length > 4 ? arguments[4] : undefined; let ratchetOpts = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : { ratchetCount: 0 }; return function* () { const keySet = yield keys.getKeySet(keyIndex); if (!keySet) { throw new Error('No key set found'); } try { const plainText = yield crypto.subtle.decrypt({ name: ENCRYPTION_ALGORITHM, iv }, keySet.encryptionKey, new Uint8Array(data)); return { payload: new Uint8Array(plainText) }; } catch (error) { if (keys.keyProviderOptions.ratchetWindowSize > 0) { if (ratchetOpts.ratchetCount < keys.keyProviderOptions.ratchetWindowSize) { workerLogger.debug("DataCryptor: ratcheting key attempt ".concat(ratchetOpts.ratchetCount, " of ").concat(keys.keyProviderOptions.ratchetWindowSize, ", for data packet")); let ratchetedKeySet; let ratchetResult; if ((initialMaterial !== null && initialMaterial !== void 0 ? initialMaterial : keySet) === keys.getKeySet(keyIndex)) { // only ratchet if the currently set key is still the same as the one used to decrypt this frame // if not, it might be that a different frame has already ratcheted and we try with that one first ratchetResult = yield keys.ratchetKey(keyIndex, false); ratchetedKeySet = yield deriveKeys(ratchetResult.cryptoKey, keys.keyProviderOptions); } const decryptedData = yield DataCryptor.decrypt(data, iv, keys, keyIndex, initialMaterial, { ratchetCount: ratchetOpts.ratchetCount + 1, encryptionKey: ratchetedKeySet === null || ratchetedKeySet === void 0 ? void 0 : ratchetedKeySet.encryptionKey }); if (decryptedData && ratchetedKeySet) { // before updating the keys, make sure that the keySet used for this frame is still the same as the currently set key // if it's not, a new key might have been set already, which we don't want to override if ((initialMaterial !== null && initialMaterial !== void 0 ? initialMaterial : keySet) === keys.getKeySet(keyIndex)) { keys.setKeySet(ratchetedKeySet, keyIndex, ratchetResult); // decryption was successful, set the new key index to reflect the ratcheted key set keys.setCurrentKeyIndex(keyIndex); } } return decryptedData; } else { /** * Because we only set a new key once decryption has been successful, * we can be sure that we don't need to reset the key to the initial material at this point * as the key has not been updated on the keyHandler instance */ workerLogger.warn('DataCryptor: maximum ratchet attempts exceeded'); throw new CryptorError("DataCryptor: valid key missing for participant ".concat(keys.participantIdentity), CryptorErrorReason.InvalidKey, keys.participantIdentity); } } else { throw new CryptorError("DataCryptor: Decryption failed: ".concat(error.message), CryptorErrorReason.InvalidKey, keys.participantIdentity); } } }(); }); } } DataCryptor.sendCount = 0; var events = {exports: {}}; var hasRequiredEvents; function requireEvents() { if (hasRequiredEvents) return events.exports; hasRequiredEvents = 1; var R = typeof Reflect === 'object' ? Reflect : null; var ReflectApply = R && typeof R.apply === 'function' ? R.apply : function ReflectApply(target, receiver, args) { return Function.prototype.apply.call(target, receiver, args); }; var ReflectOwnKeys; if (R && typeof R.ownKeys === 'function') { ReflectOwnKeys = R.ownKeys; } else if (Object.getOwnPropertySymbols) { ReflectOwnKeys = function ReflectOwnKeys(target) { return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)); }; } else { ReflectOwnKeys = function ReflectOwnKeys(target) { return Object.getOwnPropertyNames(target); }; } function ProcessEmitWarning(warning) { if (console && console.warn) console.warn(warning); } var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { return value !== value; }; function EventEmitter() { EventEmitter.init.call(this); } events.exports = EventEmitter; events.exports.once = once; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. var defaultMaxListeners = 10; function checkListener(listener) { if (typeof listener !== 'function') { throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); } } Object.defineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function () { return defaultMaxListeners; }, set: function (arg) { if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); } defaultMaxListeners = arg; } }); EventEmitter.init = function () { if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { this._events = Object.create(null); this._eventsCount = 0; } this._maxListeners = this._maxListeners || undefined; }; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); } this._maxListeners = n; return this; }; function _getMaxListeners(that) { if (that._maxListeners === undefined) return EventEmitter.defaultMaxListeners; return that._maxListeners; } EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return _getMaxListeners(this); }; EventEmitter.prototype.emit = function emit(type) { var args = []; for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); var doError = type === 'error'; var events = this._events; if (events !== undefined) doError = doError && events.error === undefined;else if (!doError) return false; // If there is no 'error' event listener then throw. if (doError) { var er; if (args.length > 0) er = args[0]; if (er instanceof Error) { // Note: The comments on the `throw` lines are intentional, they show // up in Node's output if this results in an unhandled exception. throw er; // Unhandled 'error' event } // At least give some kind of context to the user var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); err.context = er; throw err; // Unhandled 'error' event } var handler = events[type]; if (handler === undefined) return false; if (typeof handler === 'function') { ReflectApply(handler, this, args); } else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args); } return true; }; function _addListener(target, type, listener, prepend) { var m; var events; var existing; checkListener(listener); events = target._events; if (events === undefined) { events = target._events = Object.create(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener !== undefined) { target.emit('newListener', type, listener.listener ? listener.listener : listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object events = target._events; } existing = events[type]; } if (existing === undefined) { // Optimize the case of one listener. Don't need the extra array object. existing = events[type] = listener; ++target._eventsCount; } else { if (typeof existing === 'function') { // Adding the second element, need to change to array. existing = events[type] = prepend ? [listener, existing] : [existing, listener]; // If we've already got an array, just append. } else if (prepend) { existing.unshift(listener); } else { existing.push(listener); } // Check for listener leak m = _getMaxListeners(target); if (m > 0 && existing.length > m && !existing.warned) { existing.warned = true; // No error code for this since it is a Warning // eslint-disable-next-line no-restricted-syntax var w = new Error('Possible EventEmitter memory leak detected. ' + existing.length + ' ' + String(type) + ' listeners ' + 'added. Use emitter.setMaxListeners() to ' + 'increase limit'); w.name = 'MaxListenersExceededWarning'; w.emitter = target; w.type = type; w.count = existing.length; ProcessEmitWarning(w); } } return target; } EventEmitter.prototype.addListener = function addListener(type, listener) { return _addListener(this, type, listener, false); }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.prependListener = function prependListener(type, listener) { return _addListener(this, type, listener, true); }; function onceWrapper() { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; if (arguments.length === 0) return this.listener.call(this.target); return this.listener.apply(this.target, arguments); } } function _onceWrap(target, type, listener) { var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; var wrapped = onceWrapper.bind(state); wrapped.listener = listener; state.wrapFn = wrapped; return wrapped; } EventEmitter.prototype.once = function once(type, listener) { checkListener(listener); this.on(type, _onceWrap(this, type, listener)); return this; }; EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) { checkListener(listener); this.prependListener(type, _onceWrap(this, type, listener)); return this; }; // Emits a 'removeListener' event if and only if the listener was removed. EventEmitter.prototype.removeListener = function removeListener(type, listener) { var list, events, position, i, originalListener; checkListener(listener); events = this._events; if (events === undefined) return this; list = events[type]; if (list === undefined) return this; if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) this._events = Object.create(null);else { delete events[type]; if (events.removeListener) this.emit('removeListener', type, list.listener || listener); } } else if (typeof list !== 'function') { position = -1; for (i = list.length - 1; i >= 0; i--) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener; position = i; break; } } if (position < 0) return this; if (position === 0) list.shift();else { spliceOne(list, position); } if (list.length === 1) events[type] = list[0]; if (events.removeListener !== undefined) this.emit('removeListener', type, originalListener || listener); } return this; }; EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { var listeners, events, i; events = this._events; if (events === undefined) return this; // not listening for removeListener, no need to emit if (events.removeListener === undefined) { if (arguments.length === 0) { this._events = Object.create(null); this._eventsCount = 0; } else if (events[type] !== undefined) { if (--this._eventsCount === 0) this._events = Object.create(null);else delete events[type]; } return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { var keys = Object.keys(events); var key; for (i = 0; i < keys.length; ++i) { key = keys[i]; if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = Object.create(null); this._eventsCount = 0; return this; } listeners = events[type]; if (typeof listeners === 'function') { this.removeListener(type, listeners); } else if (listeners !== undefined) { // LIFO order for (i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]); } } return this; }; function _listeners(target, type, unwrap) { var events = target._events; if (events === undefined) return []; var evlistener = events[type]; if (evlistener === undefined) return []; if (typeof evlistener === 'function') return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); } EventEmitter.prototype.listeners = function listeners(type) { return _listeners(this, type, true); }; EventEmitter.prototype.rawListeners = function rawListeners(type) { return _listeners(this, type, false); }; EventEmitter.listenerCount = function (emitter, type) { if (typeof emitter.listenerCount === 'function') { return emitter.listenerCount(type); } else { return listenerCount.call(emitter, type); } }; EventEmitter.prototype.listenerCount = listenerCount; function listenerCount(type) { var events = this._events; if (events !== undefined) { var evlistener = events[type]; if (typeof evlistener === 'function') { return 1; } else if (evlistener !== undefined) { return evlistener.length; } } return 0; } EventEmitter.prototype.eventNames = function eventNames() { return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; }; function arrayClone(arr, n) { var copy = new Array(n); for (var i = 0; i < n; ++i) copy[i] = arr[i]; return copy; } function spliceOne(list, index) { for (; index + 1 < list.length; index++) list[index] = list[index + 1]; list.pop(); } function unwrapListeners(arr) { var ret = new Array(arr.length); for (var i = 0; i < ret.length; ++i) { ret[i] = arr[i].listener || arr[i]; } return ret; } function once(emitter, name) { return new Promise(function (resolve, reject) { function errorListener(err) { emitter.removeListener(name, resolver); reject(err); } function resolver() { if (typeof emitter.removeListener === 'function') { emitter.removeListener('error', errorListener); } resolve([].slice.call(arguments)); } eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); if (name !== 'error') { addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); } }); } function addErrorHandlerIfEventEmitter(emitter, handler, flags) { if (typeof emitter.on === 'function') { eventTargetAgnosticAddListener(emitter, 'error', handler, flags); } } function eventTargetAgnosticAddListener(emitter, name, listener, flags) { if (typeof emitter.on === 'function') { if (flags.once) { emitter.once(name, listener); } else { emitter.on(name, listener); } } else if (typeof emitter.addEventListener === 'function') { // EventTarget does not have `error` event semantics like Node // EventEmitters, we do not listen for `error` events here. emitter.addEventListener(name, function wrapListener(arg) { // IE does not have builtin `{ once: true }` support so we // have to do it manually. if (flags.once) { emitter.removeEventListener(name, wrapListener); } listener(arg); }); } else { throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); } } return events.exports; } var eventsExports = requireEvents(); /** * NALU (Network Abstraction Layer Unit) utilities for H.264 and H.265 video processing * Contains functions for parsing and working with NALUs in video frames */ /** * Mask for extracting NALU type from H.264 header byte */ const kH264NaluTypeMask = 0x1f; /** * H.264 NALU types according to RFC 6184 */ var H264NALUType; (function (H264NALUType) { /** Coded slice of a non-IDR picture */ H264NALUType[H264NALUType["SLICE_NON_IDR"] = 1] = "SLICE_NON_IDR"; /** Coded slice data partition A */ H264NALUType[H264NALUType["SLICE_PARTITION_A"] = 2] = "SLICE_PARTITION_A"; /** Coded slice data partition B */ H264NALUType[H264NALUType["SLICE_PARTITION_B"] = 3] = "SLICE_PARTITION_B"; /** Coded slice data partition C */ H264NALUType[H264NALUType["SLICE_PARTITION_C"] = 4] = "SLICE_PARTITION_C"; /** Coded slice of an IDR picture */ H264NALUType[H264NALUType["SLICE_IDR"] = 5] = "SLICE_IDR"; /** Supplemental enhancement information */ H264NALUType[H264NALUType["SEI"] = 6] = "SEI"; /** Sequence parameter set */ H264NALUType[H264NALUType["SPS"] = 7] = "SPS"; /** Picture parameter set */ H264NALUType[H264NALUType["PPS"] = 8] = "PPS"; /** Access unit delimiter */ H264NALUType[H264NALUType["AUD"] = 9] = "AUD"; /** End of sequence */ H264NALUType[H264NALUType["END_SEQ"] = 10] = "END_SEQ"; /** End of stream */ H264NALUType[H264NALUType["END_STREAM"] = 11] = "END_STREAM"; /** Filler data */ H264NALUType[H264NALUType["FILLER_DATA"] = 12] = "FILLER_DATA"; /** Sequence parameter set extension */ H264NALUType[H264NALUType["SPS_EXT"] = 13] = "SPS_EXT"; /** Prefix NAL unit */ H264NALUType[H264NALUType["PREFIX_NALU"] = 14] = "PREFIX_NALU"; /** Subset sequence parameter set */ H264NALUType[H264NALUType["SUBSET_SPS"] = 15] = "SUBSET_SPS"; /** Depth parameter set */ H264NALUType[H264NALUType["DPS"] = 16] = "DPS"; // 17, 18 reserved /** Coded slice of an auxiliary coded picture without partitioning */ H264NALUType[H264NALUType["SLICE_AUX"] = 19] = "SLICE_AUX"; /** Coded slice extension */ H264NALUType[H264NALUType["SLICE_EXT"] = 20] = "SLICE_EXT"; /** Coded slice extension for a depth view component or a 3D-AVC texture view component */ H264NALUType[H264NALUType["SLICE_LAYER_EXT"] = 21] = "SLICE_LAYER_EXT"; // 22, 23 reserved })(H264NALUType || (H264NALUType = {})); /** * H.265/HEVC NALU types according to ITU-T H.265 */ var H265NALUType; (function (H265NALUType) { /** Coded slice segment of a non-TSA, non-STSA trailing picture */ H265NALUType[H265NALUType["TRAIL_N"] = 0] = "TRAIL_N"; /** Coded slice segment of a non-TSA, non-STSA trailing picture */ H265NALUType[H265NALUType["TRAIL_R"] = 1] = "TRAIL_R"; /** Coded slice segment of a TSA picture */ H265NALUType[H265NALUType["TSA_N"] = 2] = "TSA_N"; /** Coded slice segment of a TSA picture */ H265NALUType[H265NALUType["TSA_R"] = 3] = "TSA_R"; /** Coded slice segment of an STSA picture */ H265NALUType[H265NALUType["STSA_N"] = 4] = "STSA_N"; /** Coded slice segment of an STSA picture */ H265NALUType[H265NALUType["STSA_R"] = 5] = "STSA_R"; /** Coded slice segment of a RADL picture */ H265NALUType[H265NALUType["RADL_N"] = 6] = "RADL_N"; /** Coded slice segment of a RADL picture */ H265NALUType[H265NALUType["RADL_R"] = 7] = "RADL_R"; /** Coded slice segment of a RASL picture */ H265NALUType[H265NALUType["RASL_N"] = 8] = "RASL_N"; /** Coded slice segment of a RASL picture */ H265NALUType[H265NALUType["RASL_R"] = 9] = "RASL_R"; // 10-15 reserved /** Coded slice segment of a BLA picture */ H265NALUType[H265NALUType["BLA_W_LP"] = 16] = "BLA_W_LP"; /** Coded slice segment of a BLA picture */ H265NALUType[H265NALUType["BLA_W_RADL"] = 17] = "BLA_W_RADL"; /** Coded slice segment of a BLA picture */ H265NALUType[H265NALUType["BLA_N_LP"] = 18] = "BLA_N_LP"; /** Coded slice segment of an IDR picture */ H265NALUType[H265NALUType["IDR_W_RADL"] = 19] = "IDR_W_RADL"; /** Coded slice segment of an IDR picture */ H265NALUType[H265NALUType["IDR_N_LP"] = 20] = "IDR_N_LP"; /** Coded slice segment of a CRA picture */ H265NALUType[H265NALUType["CRA_NUT"] = 21]