livekit-client 1.12.0 → 1.12.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +14 -0
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +59 -39
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +44 -17
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +0 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +8 -0
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +6 -4
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/utils/browserParser.d.ts +2 -0
- package/dist/src/utils/browserParser.d.ts.map +1 -1
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +0 -1
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +8 -0
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +6 -4
- package/dist/ts4.2/src/utils/browserParser.d.ts +2 -0
- package/package.json +1 -1
- package/src/e2ee/worker/FrameCryptor.ts +12 -14
- package/src/e2ee/worker/ParticipantKeyHandler.ts +15 -0
- package/src/e2ee/worker/e2ee.worker.ts +12 -6
- package/src/room/Room.ts +17 -7
- package/src/room/participant/RemoteParticipant.ts +19 -15
- package/src/room/track/create.ts +9 -0
- package/src/utils/browserParser.ts +5 -0
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"livekit-client.e2ee.worker.js","sources":["../node_modules/loglevel/lib/loglevel.js","../src/logger.ts","../src/e2ee/constants.ts","../src/room/errors.ts","../src/e2ee/errors.ts","../node_modules/eventemitter3/index.js","../src/e2ee/types.ts","../src/room/track/options.ts","../src/e2ee/utils.ts","../src/e2ee/worker/FrameCryptor.ts","../src/e2ee/worker/ParticipantKeyHandler.ts","../src/e2ee/worker/e2ee.worker.ts"],"sourcesContent":["/*\n* loglevel - https://github.com/pimterry/loglevel\n*\n* Copyright (c) 2013 Tim Perry\n* Licensed under the MIT license.\n*/\n(function (root, definition) {\n \"use strict\";\n if (typeof define === 'function' && define.amd) {\n define(definition);\n } else if (typeof module === 'object' && module.exports) {\n module.exports = definition();\n } else {\n root.log = definition();\n }\n}(this, function () {\n \"use strict\";\n\n // Slightly dubious tricks to cut down minimized file size\n var noop = function() {};\n var undefinedType = \"undefined\";\n var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && (\n /Trident\\/|MSIE /.test(window.navigator.userAgent)\n );\n\n var logMethods = [\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\"\n ];\n\n // Cross-browser bind equivalent that works at least back to IE6\n function bindMethod(obj, methodName) {\n var method = obj[methodName];\n if (typeof method.bind === 'function') {\n return method.bind(obj);\n } else {\n try {\n return Function.prototype.bind.call(method, obj);\n } catch (e) {\n // Missing bind shim or IE8 + Modernizr, fallback to wrapping\n return function() {\n return Function.prototype.apply.apply(method, [obj, arguments]);\n };\n }\n }\n }\n\n // Trace() doesn't print the message in IE, so for that case we need to wrap it\n function traceForIE() {\n if (console.log) {\n if (console.log.apply) {\n console.log.apply(console, arguments);\n } else {\n // In old IE, native console methods themselves don't have apply().\n Function.prototype.apply.apply(console.log, [console, arguments]);\n }\n }\n if (console.trace) console.trace();\n }\n\n // Build the best logging method possible for this env\n // Wherever possible we want to bind, not wrap, to preserve stack traces\n function realMethod(methodName) {\n if (methodName === 'debug') {\n methodName = 'log';\n }\n\n if (typeof console === undefinedType) {\n return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives\n } else if (methodName === 'trace' && isIE) {\n return traceForIE;\n } else if (console[methodName] !== undefined) {\n return bindMethod(console, methodName);\n } else if (console.log !== undefined) {\n return bindMethod(console, 'log');\n } else {\n return noop;\n }\n }\n\n // These private functions always need `this` to be set properly\n\n function replaceLoggingMethods(level, loggerName) {\n /*jshint validthis:true */\n for (var i = 0; i < logMethods.length; i++) {\n var methodName = logMethods[i];\n this[methodName] = (i < level) ?\n noop :\n this.methodFactory(methodName, level, loggerName);\n }\n\n // Define log.log as an alias for log.debug\n this.log = this.debug;\n }\n\n // In old IE versions, the console isn't present until you first open it.\n // We build realMethod() replacements here that regenerate logging methods\n function enableLoggingWhenConsoleArrives(methodName, level, loggerName) {\n return function () {\n if (typeof console !== undefinedType) {\n replaceLoggingMethods.call(this, level, loggerName);\n this[methodName].apply(this, arguments);\n }\n };\n }\n\n // By default, we use closely bound real methods wherever possible, and\n // otherwise we wait for a console to appear, and then try again.\n function defaultMethodFactory(methodName, level, loggerName) {\n /*jshint validthis:true */\n return realMethod(methodName) ||\n enableLoggingWhenConsoleArrives.apply(this, arguments);\n }\n\n function Logger(name, defaultLevel, factory) {\n var self = this;\n var currentLevel;\n defaultLevel = defaultLevel == null ? \"WARN\" : defaultLevel;\n\n var storageKey = \"loglevel\";\n if (typeof name === \"string\") {\n storageKey += \":\" + name;\n } else if (typeof name === \"symbol\") {\n storageKey = undefined;\n }\n\n function persistLevelIfPossible(levelNum) {\n var levelName = (logMethods[levelNum] || 'silent').toUpperCase();\n\n if (typeof window === undefinedType || !storageKey) return;\n\n // Use localStorage if available\n try {\n window.localStorage[storageKey] = levelName;\n return;\n } catch (ignore) {}\n\n // Use session cookie as fallback\n try {\n window.document.cookie =\n encodeURIComponent(storageKey) + \"=\" + levelName + \";\";\n } catch (ignore) {}\n }\n\n function getPersistedLevel() {\n var storedLevel;\n\n if (typeof window === undefinedType || !storageKey) return;\n\n try {\n storedLevel = window.localStorage[storageKey];\n } catch (ignore) {}\n\n // Fallback to cookies if local storage gives us nothing\n if (typeof storedLevel === undefinedType) {\n try {\n var cookie = window.document.cookie;\n var location = cookie.indexOf(\n encodeURIComponent(storageKey) + \"=\");\n if (location !== -1) {\n storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1];\n }\n } catch (ignore) {}\n }\n\n // If the stored level is not valid, treat it as if nothing was stored.\n if (self.levels[storedLevel] === undefined) {\n storedLevel = undefined;\n }\n\n return storedLevel;\n }\n\n function clearPersistedLevel() {\n if (typeof window === undefinedType || !storageKey) return;\n\n // Use localStorage if available\n try {\n window.localStorage.removeItem(storageKey);\n return;\n } catch (ignore) {}\n\n // Use session cookie as fallback\n try {\n window.document.cookie =\n encodeURIComponent(storageKey) + \"=; expires=Thu, 01 Jan 1970 00:00:00 UTC\";\n } catch (ignore) {}\n }\n\n /*\n *\n * Public logger API - see https://github.com/pimterry/loglevel for details\n *\n */\n\n self.name = name;\n\n self.levels = { \"TRACE\": 0, \"DEBUG\": 1, \"INFO\": 2, \"WARN\": 3,\n \"ERROR\": 4, \"SILENT\": 5};\n\n self.methodFactory = factory || defaultMethodFactory;\n\n self.getLevel = function () {\n return currentLevel;\n };\n\n self.setLevel = function (level, persist) {\n if (typeof level === \"string\" && self.levels[level.toUpperCase()] !== undefined) {\n level = self.levels[level.toUpperCase()];\n }\n if (typeof level === \"number\" && level >= 0 && level <= self.levels.SILENT) {\n currentLevel = level;\n if (persist !== false) { // defaults to true\n persistLevelIfPossible(level);\n }\n replaceLoggingMethods.call(self, level, name);\n if (typeof console === undefinedType && level < self.levels.SILENT) {\n return \"No console available for logging\";\n }\n } else {\n throw \"log.setLevel() called with invalid level: \" + level;\n }\n };\n\n self.setDefaultLevel = function (level) {\n defaultLevel = level;\n if (!getPersistedLevel()) {\n self.setLevel(level, false);\n }\n };\n\n self.resetLevel = function () {\n self.setLevel(defaultLevel, false);\n clearPersistedLevel();\n };\n\n self.enableAll = function(persist) {\n self.setLevel(self.levels.TRACE, persist);\n };\n\n self.disableAll = function(persist) {\n self.setLevel(self.levels.SILENT, persist);\n };\n\n // Initialize with the right level\n var initialLevel = getPersistedLevel();\n if (initialLevel == null) {\n initialLevel = defaultLevel;\n }\n self.setLevel(initialLevel, false);\n }\n\n /*\n *\n * Top-level API\n *\n */\n\n var defaultLogger = new Logger();\n\n var _loggersByName = {};\n defaultLogger.getLogger = function getLogger(name) {\n if ((typeof name !== \"symbol\" && typeof name !== \"string\") || name === \"\") {\n throw new TypeError(\"You must supply a name when creating a logger.\");\n }\n\n var logger = _loggersByName[name];\n if (!logger) {\n logger = _loggersByName[name] = new Logger(\n name, defaultLogger.getLevel(), defaultLogger.methodFactory);\n }\n return logger;\n };\n\n // Grab the current global log variable in case of overwrite\n var _log = (typeof window !== undefinedType) ? window.log : undefined;\n defaultLogger.noConflict = function() {\n if (typeof window !== undefinedType &&\n window.log === defaultLogger) {\n window.log = _log;\n }\n\n return defaultLogger;\n };\n\n defaultLogger.getLoggers = function getLoggers() {\n return _loggersByName;\n };\n\n // ES6 default export, for compatibility\n defaultLogger['default'] = defaultLogger;\n\n return defaultLogger;\n}));\n","import * as log from 'loglevel';\n\nexport enum LogLevel {\n trace = 0,\n debug = 1,\n info = 2,\n warn = 3,\n error = 4,\n silent = 5,\n}\n\ntype LogLevelString = keyof typeof LogLevel;\n\ntype StructuredLogger = {\n trace: (msg: string, context?: object) => void;\n debug: (msg: string, context?: object) => void;\n info: (msg: string, context?: object) => void;\n warn: (msg: string, context?: object) => void;\n error: (msg: string, context?: object) => void;\n setDefaultLevel: (level: log.LogLevelDesc) => void;\n};\n\nconst livekitLogger = log.getLogger('livekit');\n\nlivekitLogger.setDefaultLevel(LogLevel.info);\n\nexport default livekitLogger as StructuredLogger;\n\nexport function setLogLevel(level: LogLevel | LogLevelString, loggerName?: 'livekit' | 'lk-e2ee') {\n if (loggerName) {\n log.getLogger(loggerName).setLevel(level);\n }\n for (const logger of Object.values(log.getLoggers())) {\n logger.setLevel(level);\n }\n}\n\nexport type LogExtension = (level: LogLevel, msg: string, context?: object) => void;\n\n/**\n * use this to hook into the logging function to allow sending internal livekit logs to third party services\n * if set, the browser logs will lose their stacktrace information (see https://github.com/pimterry/loglevel#writing-plugins)\n */\nexport function setLogExtension(extension: LogExtension) {\n const originalFactory = livekitLogger.methodFactory;\n\n livekitLogger.methodFactory = (methodName, configLevel, loggerName) => {\n const rawMethod = originalFactory(methodName, configLevel, loggerName);\n\n const logLevel = LogLevel[methodName as LogLevelString];\n const needLog = logLevel >= configLevel && logLevel < LogLevel.silent;\n\n return (msg, context?: [msg: string, context: object]) => {\n if (context) rawMethod(msg, context);\n else rawMethod(msg);\n if (needLog) {\n extension(logLevel, msg, context);\n }\n };\n };\n livekitLogger.setLevel(livekitLogger.getLevel()); // Be sure to call setLevel method in order to apply plugin\n}\n\nexport const workerLogger = log.getLogger('lk-e2ee') as StructuredLogger;\n","import type { KeyProviderOptions } from './types';\n\nexport const ENCRYPTION_ALGORITHM = 'AES-GCM';\n\n// We use a ringbuffer of keys so we can change them and still decode packets that were\n// encrypted with an old key. We use a size of 16 which corresponds to the four bits\n// in the frame trailer.\nexport const KEYRING_SIZE = 16;\n\n// We copy the first bytes of the VP8 payload unencrypted.\n// For keyframes this is 10 bytes, for non-keyframes (delta) 3. See\n// https://tools.ietf.org/html/rfc6386#section-9.1\n// This allows the bridge to continue detecting keyframes (only one byte needed in the JVB)\n// and is also a bit easier for the VP8 decoder (i.e. it generates funny garbage pictures\n// instead of being unable to decode).\n// This is a bit for show and we might want to reduce to 1 unconditionally in the final version.\n//\n// For audio (where frame.type is not set) we do not encrypt the opus TOC byte:\n// https://tools.ietf.org/html/rfc6716#section-3.1\nexport const UNENCRYPTED_BYTES = {\n key: 10,\n delta: 3,\n audio: 1, // frame.type is not set on audio, so this is set manually\n empty: 0,\n} as const;\n\n/* We use a 12 byte bit IV. This is signalled in plain together with the\n packet. See https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters */\nexport const IV_LENGTH = 12;\n\n// flag set to indicate that e2ee has been setup for sender/receiver;\nexport const E2EE_FLAG = 'lk_e2ee';\n\nexport const SALT = 'LKFrameEncryptionKey';\n\nexport const KEY_PROVIDER_DEFAULTS: KeyProviderOptions = {\n sharedKey: false,\n ratchetSalt: SALT,\n ratchetWindowSize: 8,\n} as const;\n","export class LivekitError extends Error {\n code: number;\n\n constructor(code: number, message?: string) {\n super(message || 'an error has occured');\n this.code = code;\n }\n}\n\nexport const enum ConnectionErrorReason {\n NotAllowed,\n ServerUnreachable,\n InternalError,\n Cancelled,\n}\n\nexport class ConnectionError extends LivekitError {\n status?: number;\n\n reason?: ConnectionErrorReason;\n\n constructor(message?: string, reason?: ConnectionErrorReason, status?: number) {\n super(1, message);\n this.status = status;\n this.reason = reason;\n }\n}\n\nexport class DeviceUnsupportedError extends LivekitError {\n constructor(message?: string) {\n super(21, message ?? 'device is unsupported');\n }\n}\n\nexport class TrackInvalidError extends LivekitError {\n constructor(message?: string) {\n super(20, message ?? 'track is invalid');\n }\n}\n\nexport class UnsupportedServer extends LivekitError {\n constructor(message?: string) {\n super(10, message ?? 'unsupported server');\n }\n}\n\nexport class UnexpectedConnectionState extends LivekitError {\n constructor(message?: string) {\n super(12, message ?? 'unexpected connection state');\n }\n}\n\nexport class NegotiationError extends LivekitError {\n constructor(message?: string) {\n super(13, message ?? 'unable to negotiate');\n }\n}\n\nexport class PublishDataError extends LivekitError {\n constructor(message?: string) {\n super(13, message ?? 'unable to publish data');\n }\n}\n\nexport enum MediaDeviceFailure {\n // user rejected permissions\n PermissionDenied = 'PermissionDenied',\n // device is not available\n NotFound = 'NotFound',\n // device is in use. On Windows, only a single tab may get access to a device at a time.\n DeviceInUse = 'DeviceInUse',\n Other = 'Other',\n}\n\nexport namespace MediaDeviceFailure {\n export function getFailure(error: any): MediaDeviceFailure | undefined {\n if (error && 'name' in error) {\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\n return MediaDeviceFailure.NotFound;\n }\n if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\n return MediaDeviceFailure.PermissionDenied;\n }\n if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\n return MediaDeviceFailure.DeviceInUse;\n }\n return MediaDeviceFailure.Other;\n }\n }\n}\n","import { LivekitError } from '../room/errors';\n\nexport enum CryptorErrorReason {\n InvalidKey = 0,\n MissingKey = 1,\n InternalError = 2,\n}\n\nexport class CryptorError extends LivekitError {\n reason: CryptorErrorReason;\n\n constructor(message?: string, reason: CryptorErrorReason = CryptorErrorReason.InternalError) {\n super(40, message);\n this.reason = reason;\n }\n}\n","'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n Events.prototype = Object.create(null);\n\n //\n // This hack is needed because the `__proto__` property is still inherited in\n // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n //\n if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {*} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @private\n */\nfunction EE(fn, context, once) {\n this.fn = fn;\n this.context = context;\n this.once = once || false;\n}\n\n/**\n * Add a listener for a given event.\n *\n * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} context The context to invoke the listener with.\n * @param {Boolean} once Specify if the listener is a one-time listener.\n * @returns {EventEmitter}\n * @private\n */\nfunction addListener(emitter, event, fn, context, once) {\n if (typeof fn !== 'function') {\n throw new TypeError('The listener must be a function');\n }\n\n var listener = new EE(fn, context || emitter, once)\n , evt = prefix ? prefix + event : event;\n\n if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;\n else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);\n else emitter._events[evt] = [emitter._events[evt], listener];\n\n return emitter;\n}\n\n/**\n * Clear event by name.\n *\n * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n * @param {(String|Symbol)} evt The Event name.\n * @private\n */\nfunction clearEvent(emitter, evt) {\n if (--emitter._eventsCount === 0) emitter._events = new Events();\n else delete emitter._events[evt];\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @public\n */\nfunction EventEmitter() {\n this._events = new Events();\n this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n var names = []\n , events\n , name;\n\n if (this._eventsCount === 0) return names;\n\n for (name in (events = this._events)) {\n if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n }\n\n if (Object.getOwnPropertySymbols) {\n return names.concat(Object.getOwnPropertySymbols(events));\n }\n\n return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Array} The registered listeners.\n * @public\n */\nEventEmitter.prototype.listeners = function listeners(event) {\n var evt = prefix ? prefix + event : event\n , handlers = this._events[evt];\n\n if (!handlers) return [];\n if (handlers.fn) return [handlers.fn];\n\n for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {\n ee[i] = handlers[i].fn;\n }\n\n return ee;\n};\n\n/**\n * Return the number of listeners listening to a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Number} The number of listeners.\n * @public\n */\nEventEmitter.prototype.listenerCount = function listenerCount(event) {\n var evt = prefix ? prefix + event : event\n , listeners = this._events[evt];\n\n if (!listeners) return 0;\n if (listeners.fn) return 1;\n return listeners.length;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return false;\n\n var listeners = this._events[evt]\n , len = arguments.length\n , args\n , i;\n\n if (listeners.fn) {\n if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n switch (len) {\n case 1: return listeners.fn.call(listeners.context), true;\n case 2: return listeners.fn.call(listeners.context, a1), true;\n case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n }\n\n for (i = 1, args = new Array(len -1); i < len; i++) {\n args[i - 1] = arguments[i];\n }\n\n listeners.fn.apply(listeners.context, args);\n } else {\n var length = listeners.length\n , j;\n\n for (i = 0; i < length; i++) {\n if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n switch (len) {\n case 1: listeners[i].fn.call(listeners[i].context); break;\n case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n default:\n if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n args[j - 1] = arguments[j];\n }\n\n listeners[i].fn.apply(listeners[i].context, args);\n }\n }\n }\n\n return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n return addListener(this, event, fn, context, false);\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n return addListener(this, event, fn, context, true);\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {*} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return this;\n if (!fn) {\n clearEvent(this, evt);\n return this;\n }\n\n var listeners = this._events[evt];\n\n if (listeners.fn) {\n if (\n listeners.fn === fn &&\n (!once || listeners.once) &&\n (!context || listeners.context === context)\n ) {\n clearEvent(this, evt);\n }\n } else {\n for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n if (\n listeners[i].fn !== fn ||\n (once && !listeners[i].once) ||\n (context && listeners[i].context !== context)\n ) {\n events.push(listeners[i]);\n }\n }\n\n //\n // Reset the array, or remove it completely if we have no more listeners.\n //\n if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n else clearEvent(this, evt);\n }\n\n return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {(String|Symbol)} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n var evt;\n\n if (event) {\n evt = prefix ? prefix + event : event;\n if (this._events[evt]) clearEvent(this, evt);\n } else {\n this._events = new Events();\n this._eventsCount = 0;\n }\n\n return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n module.exports = EventEmitter;\n}\n","import type Participant from '../room/participant/Participant';\nimport type { VideoCodec } from '../room/track/options';\nimport type { BaseKeyProvider } from './KeyProvider';\nimport type { CryptorError } from './errors';\n\nexport interface BaseMessage {\n kind: string;\n data?: unknown;\n}\n\nexport interface InitMessage extends BaseMessage {\n kind: 'init';\n data: {\n keyProviderOptions: KeyProviderOptions;\n };\n}\n\nexport interface SetKeyMessage extends BaseMessage {\n kind: 'setKey';\n data: {\n participantId?: string;\n key: CryptoKey;\n keyIndex?: number;\n };\n}\n\nexport interface RTPVideoMapMessage extends BaseMessage {\n kind: 'setRTPMap';\n data: {\n map: Map<number, VideoCodec>;\n };\n}\n\nexport interface EncodeMessage extends BaseMessage {\n kind: 'decode' | 'encode';\n data: {\n participantId: string;\n readableStream: ReadableStream;\n writableStream: WritableStream;\n trackId: string;\n codec?: VideoCodec;\n };\n}\n\nexport interface RemoveTransformMessage extends BaseMessage {\n kind: 'removeTransform';\n data: {\n participantId: string;\n trackId: string;\n };\n}\n\nexport interface UpdateCodecMessage extends BaseMessage {\n kind: 'updateCodec';\n data: {\n participantId: string;\n trackId: string;\n codec: VideoCodec;\n };\n}\n\nexport interface RatchetRequestMessage extends BaseMessage {\n kind: 'ratchetRequest';\n data: {\n participantId: string | undefined;\n keyIndex?: number;\n };\n}\n\nexport interface RatchetMessage extends BaseMessage {\n kind: 'ratchetKey';\n data: {\n // participantId: string | undefined;\n keyIndex?: number;\n material: CryptoKey;\n };\n}\n\nexport interface ErrorMessage extends BaseMessage {\n kind: 'error';\n data: {\n error: Error;\n };\n}\n\nexport interface EnableMessage extends BaseMessage {\n kind: 'enable';\n data: {\n // if no participant id is set it indicates publisher encryption enable/disable\n participantId?: string;\n enabled: boolean;\n };\n}\n\nexport type E2EEWorkerMessage =\n | InitMessage\n | SetKeyMessage\n | EncodeMessage\n | ErrorMessage\n | EnableMessage\n | RemoveTransformMessage\n | RTPVideoMapMessage\n | UpdateCodecMessage\n | RatchetRequestMessage\n | RatchetMessage;\n\nexport type KeySet = { material: CryptoKey; encryptionKey: CryptoKey };\n\nexport type KeyProviderOptions = {\n sharedKey: boolean;\n ratchetSalt: string;\n ratchetWindowSize: number;\n};\n\nexport type KeyProviderCallbacks = {\n setKey: (keyInfo: KeyInfo) => void;\n ratchetRequest: (participantId?: string, keyIndex?: number) => void;\n /** currently only emitted for local participant */\n keyRatcheted: (material: CryptoKey, keyIndex?: number) => void;\n};\n\nexport type ParticipantKeyHandlerCallbacks = {\n keyRatcheted: (material: CryptoKey, keyIndex?: number, participantId?: string) => void;\n};\n\nexport type E2EEManagerCallbacks = {\n participantEncryptionStatusChanged: (enabled: boolean, participant?: Participant) => void;\n encryptionError: (error: Error) => void;\n};\n\nexport const EncryptionEvent = {\n ParticipantEncryptionStatusChanged: 'participantEncryptionStatusChanged',\n Error: 'encryptionError',\n} as const;\n\nexport type CryptorCallbacks = {\n cryptorError: (error: CryptorError) => void;\n};\n\nexport const CryptorEvent = {\n Error: 'cryptorError',\n} as const;\n\nexport type KeyInfo = {\n key: CryptoKey;\n participantId?: string;\n keyIndex?: number;\n};\n\nexport type E2EEOptions = {\n keyProvider: BaseKeyProvider;\n worker: Worker;\n};\n\nexport type DecodeRatchetOptions = {\n /** attempts */\n ratchetCount: number;\n /** ratcheted key to try */\n encryptionKey?: CryptoKey;\n};\n","import type { Track } from './Track';\n\nexport interface TrackPublishDefaults {\n /**\n * encoding parameters for camera track\n */\n videoEncoding?: VideoEncoding;\n\n /**\n * @experimental\n */\n backupCodec?: { codec: BackupVideoCodec; encoding: VideoEncoding } | false;\n\n /**\n * encoding parameters for screen share track\n */\n screenShareEncoding?: VideoEncoding;\n\n /**\n * codec, defaults to vp8; for svc codecs, auto enable vp8\n * as backup. (TBD)\n */\n videoCodec?: VideoCodec;\n\n /**\n * max audio bitrate, defaults to [[AudioPresets.music]]\n * @deprecated use `audioPreset` instead\n */\n audioBitrate?: number;\n\n /**\n * which audio preset should be used for publishing (audio) tracks\n * defaults to [[AudioPresets.music]]\n */\n audioPreset?: AudioPreset;\n\n /**\n * dtx (Discontinuous Transmission of audio), enabled by default for mono tracks.\n */\n dtx?: boolean;\n\n /**\n * red (Redundant Audio Data), enabled by default for mono tracks.\n */\n red?: boolean;\n\n /**\n * publish track in stereo mode (or set to false to disable). defaults determined by capture channel count.\n */\n forceStereo?: boolean;\n\n /**\n * use simulcast, defaults to true.\n * When using simulcast, LiveKit will publish up to three versions of the stream\n * at various resolutions.\n */\n simulcast?: boolean;\n\n /**\n * scalability mode for svc codecs, defaults to 'L3T3'.\n * for svc codecs, simulcast is disabled.\n */\n scalabilityMode?: ScalabilityMode;\n\n /**\n * Up to two additional simulcast layers to publish in addition to the original\n * Track.\n * When left blank, it defaults to h180, h360.\n * If a SVC codec is used (VP9 or AV1), this field has no effect.\n *\n * To publish three total layers, you would specify:\n * {\n * videoEncoding: {...}, // encoding of the primary layer\n * videoSimulcastLayers: [\n * VideoPresets.h540,\n * VideoPresets.h216,\n * ],\n * }\n */\n videoSimulcastLayers?: Array<VideoPreset>;\n\n /**\n * custom video simulcast layers for screen tracks\n * Note: the layers need to be ordered from lowest to highest quality\n */\n screenShareSimulcastLayers?: Array<VideoPreset>;\n\n /**\n * For local tracks, stop the underlying MediaStreamTrack when the track is muted (or paused)\n * on some platforms, this option is necessary to disable the microphone recording indicator.\n * Note: when this is enabled, and BT devices are connected, they will transition between\n * profiles (e.g. HFP to A2DP) and there will be an audible difference in playback.\n *\n * defaults to false\n */\n stopMicTrackOnMute?: boolean;\n}\n\n/**\n * Options when publishing tracks\n */\nexport interface TrackPublishOptions extends TrackPublishDefaults {\n /**\n * set a track name\n */\n name?: string;\n\n /**\n * Source of track, camera, microphone, or screen\n */\n source?: Track.Source;\n}\n\nexport interface CreateLocalTracksOptions {\n /**\n * audio track options, true to create with defaults. false if audio shouldn't be created\n * default true\n */\n audio?: boolean | AudioCaptureOptions;\n\n /**\n * video track options, true to create with defaults. false if video shouldn't be created\n * default true\n */\n video?: boolean | VideoCaptureOptions;\n}\n\nexport interface VideoCaptureOptions {\n /**\n * A ConstrainDOMString object specifying a device ID or an array of device\n * IDs which are acceptable and/or required.\n */\n deviceId?: ConstrainDOMString;\n\n /**\n * a facing or an array of facings which are acceptable and/or required.\n */\n facingMode?: 'user' | 'environment' | 'left' | 'right';\n\n resolution?: VideoResolution;\n}\n\nexport interface ScreenShareCaptureOptions {\n /**\n * true to capture audio shared. browser support for audio capturing in\n * screenshare is limited: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#browser_compatibility\n */\n audio?: boolean | AudioCaptureOptions;\n\n /** capture resolution, defaults to full HD */\n resolution?: VideoResolution;\n\n /** a CaptureController object instance containing methods that can be used to further manipulate the capture session if included. */\n controller?: unknown; // TODO replace type with CaptureController once it lands in TypeScript\n\n /** specifies whether the browser should allow the user to select the current tab for capture */\n selfBrowserSurface?: 'include' | 'exclude';\n\n /** specifies whether the browser should display a control to allow the user to dynamically switch the shared tab during screen-sharing. */\n surfaceSwitching?: 'include' | 'exclude';\n\n /** specifies whether the browser should include the system audio among the possible audio sources offered to the user */\n systemAudio?: 'include' | 'exclude';\n\n /**\n * Experimental option to control whether the audio playing in a tab will continue to be played out of a user's\n * local speakers when the tab is captured.\n */\n suppressLocalAudioPlayback?: boolean;\n}\n\nexport interface AudioCaptureOptions {\n /**\n * specifies whether automatic gain control is preferred and/or required\n */\n autoGainControl?: ConstrainBoolean;\n\n /**\n * the channel count or range of channel counts which are acceptable and/or required\n */\n channelCount?: ConstrainULong;\n\n /**\n * A ConstrainDOMString object specifying a device ID or an array of device\n * IDs which are acceptable and/or required.\n */\n deviceId?: ConstrainDOMString;\n\n /**\n * whether or not echo cancellation is preferred and/or required\n */\n echoCancellation?: ConstrainBoolean;\n\n /**\n * the latency or range of latencies which are acceptable and/or required.\n */\n latency?: ConstrainDouble;\n\n /**\n * whether noise suppression is preferred and/or required.\n */\n noiseSuppression?: ConstrainBoolean;\n\n /**\n * the sample rate or range of sample rates which are acceptable and/or required.\n */\n sampleRate?: ConstrainULong;\n\n /**\n * sample size or range of sample sizes which are acceptable and/or required.\n */\n sampleSize?: ConstrainULong;\n}\n\nexport interface AudioOutputOptions {\n /**\n * deviceId to output audio\n *\n * Only supported on browsers where `setSinkId` is available\n */\n deviceId?: string;\n}\n\nexport interface VideoResolution {\n width: number;\n height: number;\n frameRate?: number;\n aspectRatio?: number;\n}\n\nexport interface VideoEncoding {\n maxBitrate: number;\n maxFramerate?: number;\n priority?: RTCPriorityType;\n}\n\nexport class VideoPreset {\n encoding: VideoEncoding;\n\n width: number;\n\n height: number;\n\n constructor(\n width: number,\n height: number,\n maxBitrate: number,\n maxFramerate?: number,\n priority?: RTCPriorityType,\n ) {\n this.width = width;\n this.height = height;\n this.encoding = {\n maxBitrate,\n maxFramerate,\n priority,\n };\n }\n\n get resolution(): VideoResolution {\n return {\n width: this.width,\n height: this.height,\n frameRate: this.encoding.maxFramerate,\n aspectRatio: this.width / this.height,\n };\n }\n}\n\nexport interface AudioPreset {\n maxBitrate: number;\n priority?: RTCPriorityType;\n}\n\nconst backupCodecs = ['vp8', 'h264'] as const;\n\nexport const videoCodecs = ['vp8', 'h264', 'vp9', 'av1'] as const;\n\nexport type VideoCodec = (typeof videoCodecs)[number];\n\nexport type BackupVideoCodec = (typeof backupCodecs)[number];\n\nexport function isBackupCodec(codec: string): codec is BackupVideoCodec {\n return !!backupCodecs.find((backup) => backup === codec);\n}\n\nexport function isCodecEqual(c1: string | undefined, c2: string | undefined): boolean {\n return (\n c1?.toLowerCase().replace(/audio\\/|video\\//y, '') ===\n c2?.toLowerCase().replace(/audio\\/|video\\//y, '')\n );\n}\n\n/**\n * scalability modes for svc, only supprot l3t3 now.\n */\nexport type ScalabilityMode = 'L3T3' | 'L3T3_KEY';\n\nexport namespace AudioPresets {\n export const telephone: AudioPreset = {\n maxBitrate: 12_000,\n };\n export const speech: AudioPreset = {\n maxBitrate: 20_000,\n };\n export const music: AudioPreset = {\n maxBitrate: 32_000,\n };\n export const musicStereo: AudioPreset = {\n maxBitrate: 48_000,\n };\n export const musicHighQuality: AudioPreset = {\n maxBitrate: 64_000,\n };\n export const musicHighQualityStereo: AudioPreset = {\n maxBitrate: 96_000,\n };\n}\n\n/**\n * Sane presets for video resolution/encoding\n */\nexport const VideoPresets = {\n h90: new VideoPreset(160, 90, 60_000, 15),\n h180: new VideoPreset(320, 180, 120_000, 15),\n h216: new VideoPreset(384, 216, 180_000, 15),\n h360: new VideoPreset(640, 360, 300_000, 20),\n h540: new VideoPreset(960, 540, 600_000, 25),\n h720: new VideoPreset(1280, 720, 1_700_000, 30),\n h1080: new VideoPreset(1920, 1080, 3_000_000, 30),\n h1440: new VideoPreset(2560, 1440, 5_000_000, 30),\n h2160: new VideoPreset(3840, 2160, 8_000_000, 30),\n} as const;\n\n/**\n * Four by three presets\n */\nexport const VideoPresets43 = {\n h120: new VideoPreset(160, 120, 80_000, 15),\n h180: new VideoPreset(240, 180, 100_000, 15),\n h240: new VideoPreset(320, 240, 150_000, 15),\n h360: new VideoPreset(480, 360, 225_000, 20),\n h480: new VideoPreset(640, 480, 300_000, 20),\n h540: new VideoPreset(720, 540, 450_000, 25),\n h720: new VideoPreset(960, 720, 1_500_000, 30),\n h1080: new VideoPreset(1440, 1080, 2_500_000, 30),\n h1440: new VideoPreset(1920, 1440, 3_500_000, 30),\n} as const;\n\nexport const ScreenSharePresets = {\n h360fps3: new VideoPreset(640, 360, 200_000, 3, 'medium'),\n h720fps5: new VideoPreset(1280, 720, 400_000, 5, 'medium'),\n h720fps15: new VideoPreset(1280, 720, 1_000_000, 15, 'medium'),\n h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15, 'medium'),\n h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30, 'medium'),\n} as const;\n","import { videoCodecs } from '../room/track/options';\nimport type { VideoCodec } from '../room/track/options';\nimport { ENCRYPTION_ALGORITHM } from './constants';\n\nexport function isE2EESupported() {\n return isInsertableStreamSupported() || isScriptTransformSupported();\n}\n\nexport function isScriptTransformSupported() {\n // @ts-ignore\n return typeof window.RTCRtpScriptTransform !== 'undefined';\n}\n\nexport function isInsertableStreamSupported() {\n return (\n typeof window.RTCRtpSender !== 'undefined' &&\n // @ts-ignore\n typeof window.RTCRtpSender.prototype.createEncodedStreams !== 'undefined'\n );\n}\n\nexport function isVideoFrame(\n frame: RTCEncodedAudioFrame | RTCEncodedVideoFrame,\n): frame is RTCEncodedVideoFrame {\n return 'type' in frame;\n}\n\nexport async function importKey(\n keyBytes: Uint8Array | ArrayBuffer,\n algorithm: string | { name: string } = { name: ENCRYPTION_ALGORITHM },\n usage: 'derive' | 'encrypt' = 'encrypt',\n) {\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey\n return crypto.subtle.importKey(\n 'raw',\n keyBytes,\n algorithm,\n false,\n usage === 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt'],\n );\n}\n\nexport async function createKeyMaterialFromString(password: string) {\n let enc = new TextEncoder();\n\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n enc.encode(password),\n {\n name: 'PBKDF2',\n },\n false,\n ['deriveBits', 'deriveKey'],\n );\n\n return keyMaterial;\n}\n\nfunction getAlgoOptions(algorithmName: string, salt: string) {\n const textEncoder = new TextEncoder();\n const encodedSalt = textEncoder.encode(salt);\n switch (algorithmName) {\n case 'HKDF':\n return {\n name: 'HKDF',\n salt: encodedSalt,\n hash: 'SHA-256',\n info: new ArrayBuffer(128),\n };\n case 'PBKDF2': {\n return {\n name: 'PBKDF2',\n salt: encodedSalt,\n hash: 'SHA-256',\n iterations: 100000,\n };\n }\n default:\n throw new Error(`algorithm ${algorithmName} is currently unsupported`);\n }\n}\n\n/**\n * Derives a set of keys from the master key.\n * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1\n */\nexport async function deriveKeys(material: CryptoKey, salt: string) {\n const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF\n // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams\n const encryptionKey = await crypto.subtle.deriveKey(\n algorithmOptions,\n material,\n {\n name: ENCRYPTION_ALGORITHM,\n length: 128,\n },\n false,\n ['encrypt', 'decrypt'],\n );\n\n return { material, encryptionKey };\n}\n\nexport function createE2EEKey(): Uint8Array {\n return window.crypto.getRandomValues(new Uint8Array(32));\n}\n\nexport function mimeTypeToVideoCodecString(mimeType: string) {\n const codec = mimeType.split('/')[1].toLowerCase() as VideoCodec;\n if (!videoCodecs.includes(codec)) {\n throw Error(`Video codec not supported: ${codec}`);\n }\n return codec;\n}\n\n/**\n * Ratchets a key. See\n * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1\n */\nexport async function ratchet(material: CryptoKey, salt: string): Promise<ArrayBuffer> {\n const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits\n return crypto.subtle.deriveBits(algorithmOptions, material, 256);\n}\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n// TODO code inspired by https://github.com/webrtc/samples/blob/gh-pages/src/content/insertable-streams/endtoend-encryption/js/worker.js\nimport EventEmitter from 'eventemitter3';\nimport { workerLogger } from '../../logger';\nimport type { VideoCodec } from '../../room/track/options';\nimport { ENCRYPTION_ALGORITHM, IV_LENGTH, UNENCRYPTED_BYTES } from '../constants';\nimport { CryptorError, CryptorErrorReason } from '../errors';\nimport {\n CryptorCallbacks,\n CryptorEvent,\n DecodeRatchetOptions,\n KeyProviderOptions,\n KeySet,\n} from '../types';\nimport { deriveKeys, isVideoFrame } from '../utils';\nimport type { ParticipantKeyHandler } from './ParticipantKeyHandler';\n\nexport interface FrameCryptorConstructor {\n new (opts?: unknown): BaseFrameCryptor;\n}\n\nexport interface TransformerInfo {\n readable: ReadableStream;\n writable: WritableStream;\n transformer: TransformStream;\n abortController: AbortController;\n}\n\nexport class BaseFrameCryptor extends EventEmitter<CryptorCallbacks> {\n encodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ): Promise<any> {\n throw Error('not implemented for subclass');\n }\n\n decodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ): Promise<any> {\n throw Error('not implemented for subclass');\n }\n}\n\n/**\n * Cryptor is responsible for en-/decrypting media frames.\n * Each Cryptor instance is responsible for en-/decrypting a single mediaStreamTrack.\n */\nexport class FrameCryptor extends BaseFrameCryptor {\n private sendCounts: Map<number, number>;\n\n private isKeyInvalid = false;\n\n private participantId: string | undefined;\n\n private trackId: string | undefined;\n\n private keys: ParticipantKeyHandler;\n\n private videoCodec?: VideoCodec;\n\n private rtpMap: Map<number, VideoCodec>;\n\n private keyProviderOptions: KeyProviderOptions;\n\n /**\n * used for detecting server injected unencrypted frames\n */\n private unencryptedFrameByteTrailer: Uint8Array;\n\n constructor(opts: {\n keys: ParticipantKeyHandler;\n participantId: string;\n keyProviderOptions: KeyProviderOptions;\n unencryptedFrameBytes?: Uint8Array;\n }) {\n super();\n this.sendCounts = new Map();\n this.keys = opts.keys;\n this.participantId = opts.participantId;\n this.rtpMap = new Map();\n this.keyProviderOptions = opts.keyProviderOptions;\n this.unencryptedFrameByteTrailer =\n opts.unencryptedFrameBytes ?? new TextEncoder().encode('LKROCKS');\n }\n\n /**\n * Assign a different participant to the cryptor.\n * useful for transceiver re-use\n * @param id\n * @param keys\n */\n setParticipant(id: string, keys: ParticipantKeyHandler) {\n this.participantId = id;\n this.keys = keys;\n }\n\n unsetParticipant() {\n this.participantId = undefined;\n }\n\n getParticipantId() {\n return this.participantId;\n }\n\n getTrackId() {\n return this.trackId;\n }\n\n /**\n * Update the video codec used by the mediaStreamTrack\n * @param codec\n */\n setVideoCodec(codec: VideoCodec) {\n this.videoCodec = codec;\n }\n\n /**\n * rtp payload type map used for figuring out codec of payload type when encoding\n * @param map\n */\n setRtpMap(map: Map<number, VideoCodec>) {\n this.rtpMap = map;\n }\n\n setupTransform(\n operation: 'encode' | 'decode',\n readable: ReadableStream,\n writable: WritableStream,\n trackId: string,\n codec?: VideoCodec,\n ) {\n if (codec) {\n console.info('setting codec on cryptor to', codec);\n this.videoCodec = codec;\n }\n const transformFn = operation === 'encode' ? this.encodeFunction : this.decodeFunction;\n const transformStream = new TransformStream({\n transform: transformFn.bind(this),\n });\n\n readable\n .pipeThrough(transformStream)\n .pipeTo(writable)\n .catch((e) => {\n console.error(e);\n this.emit('cryptorError', e instanceof CryptorError ? e : new CryptorError(e.message));\n });\n this.trackId = trackId;\n }\n\n /**\n * Function that will be injected in a stream and will encrypt the given encoded frames.\n *\n * @param {RTCEncodedVideoFrame|RTCEncodedAudioFrame} encodedFrame - Encoded video frame.\n * @param {TransformStreamDefaultController} controller - TransportStreamController.\n *\n * The VP8 payload descriptor described in\n * https://tools.ietf.org/html/rfc7741#section-4.2\n * is part of the RTP packet and not part of the frame and is not controllable by us.\n * This is fine as the SFU keeps having access to it for routing.\n *\n * The encrypted frame is formed as follows:\n * 1) Find unencrypted byte length, depending on the codec, frame type and kind.\n * 2) Form the GCM IV for the frame as described above.\n * 3) Encrypt the rest of the frame using AES-GCM.\n * 4) Allocate space for the encrypted frame.\n * 5) Copy the unencrypted bytes to the start of the encrypted frame.\n * 6) Append the ciphertext to the encrypted frame.\n * 7) Append the IV.\n * 8) Append a single byte for the key identifier.\n * 9) Enqueue the encrypted frame for sending.\n */\n async encodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ) {\n if (\n !this.keys.isEnabled() ||\n // skip for encryption for empty dtx frames\n encodedFrame.data.byteLength === 0\n ) {\n return controller.enqueue(encodedFrame);\n }\n\n const { encryptionKey } = this.keys.getKeySet();\n const keyIndex = this.keys.getCurrentKeyIndex();\n\n if (encryptionKey) {\n const iv = this.makeIV(\n encodedFrame.getMetadata().synchronizationSource ?? -1,\n encodedFrame.timestamp,\n );\n\n // Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.\n const frameHeader = new Uint8Array(\n encodedFrame.data,\n 0,\n this.getUnencryptedBytes(encodedFrame),\n );\n\n // Frame trailer contains the R|IV_LENGTH and key index\n const frameTrailer = new Uint8Array(2);\n\n frameTrailer[0] = IV_LENGTH;\n frameTrailer[1] = keyIndex;\n\n // Construct frame trailer. Similar to the frame header described in\n // https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2\n // but we put it at the end.\n //\n // ---------+-------------------------+-+---------+----\n // payload |IV...(length = IV_LENGTH)|R|IV_LENGTH|KID |\n // ---------+-------------------------+-+---------+----\n try {\n const cipherText = await crypto.subtle.encrypt(\n {\n name: ENCRYPTION_ALGORITHM,\n iv,\n additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength),\n },\n encryptionKey,\n new Uint8Array(encodedFrame.data, this.getUnencryptedBytes(encodedFrame)),\n );\n\n const newData = new ArrayBuffer(\n frameHeader.byteLength + cipherText.byteLength + iv.byteLength + frameTrailer.byteLength,\n );\n const newUint8 = new Uint8Array(newData);\n\n newUint8.set(frameHeader); // copy first bytes.\n newUint8.set(new Uint8Array(cipherText), frameHeader.byteLength); // add ciphertext.\n newUint8.set(new Uint8Array(iv), frameHeader.byteLength + cipherText.byteLength); // append IV.\n newUint8.set(frameTrailer, frameHeader.byteLength + cipherText.byteLength + iv.byteLength); // append frame trailer.\n\n encodedFrame.data = newData;\n\n return controller.enqueue(encodedFrame);\n } catch (e: any) {\n // TODO: surface this to the app.\n workerLogger.error(e);\n }\n } else {\n this.emit(\n CryptorEvent.Error,\n new CryptorError(`encryption key missing for encoding`, CryptorErrorReason.MissingKey),\n );\n }\n }\n\n /**\n * Function that will be injected in a stream and will decrypt the given encoded frames.\n *\n * @param {RTCEncodedVideoFrame|RTCEncodedAudioFrame} encodedFrame - Encoded video frame.\n * @param {TransformStreamDefaultController} controller - TransportStreamController.\n */\n async decodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ) {\n if (\n !this.keys.isEnabled() ||\n // skip for decryption for empty dtx frames\n encodedFrame.data.byteLength === 0 ||\n // skip decryption if frame is server injected\n isFrameServerInjected(encodedFrame.data, this.unencryptedFrameByteTrailer)\n ) {\n return controller.enqueue(encodedFrame);\n }\n const data = new Uint8Array(encodedFrame.data);\n const keyIndex = data[encodedFrame.data.byteLength - 1];\n\n if (this.keys.getKeySet(keyIndex)) {\n try {\n const decodedFrame = await this.decryptFrame(encodedFrame, keyIndex);\n if (decodedFrame) {\n return controller.enqueue(decodedFrame);\n }\n this.isKeyInvalid = false;\n } catch (error) {\n if (error instanceof CryptorError && error.reason === CryptorErrorReason.InvalidKey) {\n if (!this.isKeyInvalid) {\n workerLogger.warn('invalid key');\n this.emit(\n CryptorEvent.Error,\n new CryptorError(\n `invalid key for participant ${this.participantId}`,\n CryptorErrorReason.InvalidKey,\n ),\n );\n this.isKeyInvalid = true;\n }\n } else {\n workerLogger.warn('decoding frame failed', { error });\n }\n }\n } else {\n this.emit(\n CryptorEvent.Error,\n new CryptorError(\n `key missing for participant ${this.participantId}`,\n CryptorErrorReason.MissingKey,\n ),\n );\n }\n\n return controller.enqueue(encodedFrame);\n }\n\n /**\n * Function that will decrypt the given encoded frame. If the decryption fails, it will\n * ratchet the key for up to RATCHET_WINDOW_SIZE times.\n */\n async decryptFrame(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n keyIndex: number,\n initialMaterial: KeySet | undefined = undefined,\n ratchetOpts: DecodeRatchetOptions = { ratchetCount: 0 },\n ): Promise<RTCEncodedVideoFrame | RTCEncodedAudioFrame | undefined> {\n const keySet = this.keys.getKeySet(keyIndex);\n\n // Construct frame trailer. Similar to the frame header described in\n // https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2\n // but we put it at the end.\n //\n // ---------+-------------------------+-+---------+----\n // payload |IV...(length = IV_LENGTH)|R|IV_LENGTH|KID |\n // ---------+-------------------------+-+---------+----\n\n try {\n const frameHeader = new Uint8Array(\n encodedFrame.data,\n 0,\n this.getUnencryptedBytes(encodedFrame),\n );\n const frameTrailer = new Uint8Array(encodedFrame.data, encodedFrame.data.byteLength - 2, 2);\n\n const ivLength = frameTrailer[0];\n const iv = new Uint8Array(\n encodedFrame.data,\n encodedFrame.data.byteLength - ivLength - frameTrailer.byteLength,\n ivLength,\n );\n\n const cipherTextStart = frameHeader.byteLength;\n const cipherTextLength =\n encodedFrame.data.byteLength -\n (frameHeader.byteLength + ivLength + frameTrailer.byteLength);\n\n const plainText = await crypto.subtle.decrypt(\n {\n name: ENCRYPTION_ALGORITHM,\n iv,\n additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength),\n },\n ratchetOpts.encryptionKey ?? keySet.encryptionKey,\n new Uint8Array(encodedFrame.data, cipherTextStart, cipherTextLength),\n );\n\n const newData = new ArrayBuffer(frameHeader.byteLength + plainText.byteLength);\n const newUint8 = new Uint8Array(newData);\n\n newUint8.set(new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength));\n newUint8.set(new Uint8Array(plainText), frameHeader.byteLength);\n\n encodedFrame.data = newData;\n\n return encodedFrame;\n } catch (error: any) {\n if (this.keyProviderOptions.ratchetWindowSize > 0) {\n if (ratchetOpts.ratchetCount < this.keyProviderOptions.ratchetWindowSize) {\n workerLogger.debug(\n `ratcheting key attempt ${ratchetOpts.ratchetCount} of ${\n this.keyProviderOptions.ratchetWindowSize\n }, for kind ${encodedFrame instanceof RTCEncodedAudioFrame ? 'audio' : 'video'}`,\n );\n\n let ratchetedKeySet: KeySet | undefined;\n if (keySet === this.keys.getKeySet(keyIndex)) {\n // only ratchet if the currently set key is still the same as the one used to decrypt this frame\n // if not, it might be that a different frame has already ratcheted and we try with that one first\n const newMaterial = await this.keys.ratchetKey(keyIndex, false);\n\n ratchetedKeySet = await deriveKeys(newMaterial, this.keyProviderOptions.ratchetSalt);\n }\n\n const frame = await this.decryptFrame(encodedFrame, keyIndex, initialMaterial || keySet, {\n ratchetCount: ratchetOpts.ratchetCount + 1,\n encryptionKey: ratchetedKeySet?.encryptionKey,\n });\n if (frame && ratchetedKeySet) {\n this.keys.setKeySet(ratchetedKeySet, keyIndex, true);\n // decryption was successful, set the new key index to reflect the ratcheted key set\n this.keys.setCurrentKeyIndex(keyIndex);\n }\n return frame;\n } else {\n /**\n * Since the key it is first send and only afterwards actually used for encrypting, there were\n * situations when the decrypting failed due to the fact that the received frame was not encrypted\n * yet and ratcheting, of course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE times,\n * we come back to the initial key.\n */\n if (initialMaterial) {\n workerLogger.debug('resetting to initial material');\n this.keys.setKeyFromMaterial(initialMaterial.material, keyIndex);\n }\n\n workerLogger.warn('maximum ratchet attempts exceeded, resetting key');\n }\n } else {\n throw new CryptorError(\n 'Decryption failed, most likely because of an invalid key',\n CryptorErrorReason.InvalidKey,\n );\n }\n }\n }\n\n /**\n * Construct the IV used for AES-GCM and sent (in plain) with the packet similar to\n * https://tools.ietf.org/html/rfc7714#section-8.1\n * It concatenates\n * - the 32 bit synchronization source (SSRC) given on the encoded frame,\n * - the 32 bit rtp timestamp given on the encoded frame,\n * - a send counter that is specific to the SSRC. Starts at a random number.\n * The send counter is essentially the pictureId but we currently have to implement this ourselves.\n * There is no XOR with a salt. Note that this IV leaks the SSRC to the receiver but since this is\n * randomly generated and SFUs may not rewrite this is considered acceptable.\n * The SSRC is used to allow demultiplexing multiple streams with the same key, as described in\n * https://tools.ietf.org/html/rfc3711#section-4.1.1\n * The RTP timestamp is 32 bits and advances by the codec clock rate (90khz for video, 48khz for\n * opus audio) every second. For video it rolls over roughly every 13 hours.\n * The send counter will advance at the frame rate (30fps for video, 50fps for 20ms opus audio)\n * every second. It will take a long time to roll over.\n *\n * See also https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams\n */\n private makeIV(synchronizationSource: number, timestamp: number) {\n const iv = new ArrayBuffer(IV_LENGTH);\n const ivView = new DataView(iv);\n\n // having to keep our own send count (similar to a picture id) is not ideal.\n if (!this.sendCounts.has(synchronizationSource)) {\n // Initialize with a random offset, similar to the RTP sequence number.\n this.sendCounts.set(synchronizationSource, Math.floor(Math.random() * 0xffff));\n }\n\n const sendCount = this.sendCounts.get(synchronizationSource) ?? 0;\n\n ivView.setUint32(0, synchronizationSource);\n ivView.setUint32(4, timestamp);\n ivView.setUint32(8, timestamp - (sendCount % 0xffff));\n\n this.sendCounts.set(synchronizationSource, sendCount + 1);\n\n return iv;\n }\n\n getUnencryptedBytes(frame: RTCEncodedVideoFrame | RTCEncodedAudioFrame): number {\n if (isVideoFrame(frame)) {\n let detectedCodec = this.getVideoCodec(frame) ?? this.videoCodec;\n\n if (detectedCodec === 'av1' || detectedCodec === 'vp9') {\n throw new Error(`${detectedCodec} is not yet supported for end to end encryption`);\n }\n\n if (detectedCodec === 'vp8') {\n return UNENCRYPTED_BYTES[frame.type];\n }\n\n const data = new Uint8Array(frame.data);\n try {\n const naluIndices = findNALUIndices(data);\n\n // if the detected codec is undefined we test whether it _looks_ like a h264 frame as a best guess\n const isH264 =\n detectedCodec === 'h264' ||\n naluIndices.some((naluIndex) =>\n [NALUType.SLICE_IDR, NALUType.SLICE_NON_IDR].includes(parseNALUType(data[naluIndex])),\n );\n\n if (isH264) {\n for (const index of naluIndices) {\n let type = parseNALUType(data[index]);\n switch (type) {\n case NALUType.SLICE_IDR:\n case NALUType.SLICE_NON_IDR:\n return index + 2;\n default:\n break;\n }\n }\n throw new TypeError('Could not find NALU');\n }\n } catch (e) {\n // no op, we just continue and fallback to vp8\n }\n\n return UNENCRYPTED_BYTES[frame.type];\n } else {\n return UNENCRYPTED_BYTES.audio;\n }\n }\n\n /**\n * inspects frame payloadtype if available and maps it to the codec specified in rtpMap\n */\n getVideoCodec(frame: RTCEncodedVideoFrame): VideoCodec | undefined {\n if (this.rtpMap.size === 0) {\n return undefined;\n }\n // @ts-expect-error payloadType is not yet part of the typescript definition and currently not supported in Safari\n const payloadType = frame.getMetadata().payloadType;\n const codec = payloadType ? this.rtpMap.get(payloadType) : undefined;\n return codec;\n }\n}\n\n/**\n * Slice the NALUs present in the supplied buffer, assuming it is already byte-aligned\n * code adapted from https://github.com/medooze/h264-frame-parser/blob/main/lib/NalUnits.ts to return indices only\n */\nexport function findNALUIndices(stream: Uint8Array): number[] {\n const result: number[] = [];\n let start = 0,\n pos = 0,\n searchLength = stream.length - 2;\n while (pos < searchLength) {\n // skip until end of current NALU\n while (\n pos < searchLength &&\n !(stream[pos] === 0 && stream[pos + 1] === 0 && stream[pos + 2] === 1)\n )\n pos++;\n if (pos >= searchLength) pos = stream.length;\n // remove trailing zeros from current NALU\n let end = pos;\n while (end > start && stream[end - 1] === 0) end--;\n // save current NALU\n if (start === 0) {\n if (end !== start) throw TypeError('byte stream contains leading data');\n } else {\n result.push(start);\n }\n // begin new NALU\n start = pos = pos + 3;\n }\n return result;\n}\n\nexport function parseNALUType(startByte: number): NALUType {\n return startByte & kNaluTypeMask;\n}\n\nconst kNaluTypeMask = 0x1f;\n\nexport enum NALUType {\n /** Coded slice of a non-IDR picture */\n SLICE_NON_IDR = 1,\n /** Coded slice data partition A */\n SLICE_PARTITION_A = 2,\n /** Coded slice data partition B */\n SLICE_PARTITION_B = 3,\n /** Coded slice data partition C */\n SLICE_PARTITION_C = 4,\n /** Coded slice of an IDR picture */\n SLICE_IDR = 5,\n /** Supplemental enhancement information */\n SEI = 6,\n /** Sequence parameter set */\n SPS = 7,\n /** Picture parameter set */\n PPS = 8,\n /** Access unit delimiter */\n AUD = 9,\n /** End of sequence */\n END_SEQ = 10,\n /** End of stream */\n END_STREAM = 11,\n /** Filler data */\n FILLER_DATA = 12,\n /** Sequence parameter set extension */\n SPS_EXT = 13,\n /** Prefix NAL unit */\n PREFIX_NALU = 14,\n /** Subset sequence parameter set */\n SUBSET_SPS = 15,\n /** Depth parameter set */\n DPS = 16,\n\n // 17, 18 reserved\n\n /** Coded slice of an auxiliary coded picture without partitioning */\n SLICE_AUX = 19,\n /** Coded slice extension */\n SLICE_EXT = 20,\n /** Coded slice extension for a depth view component or a 3D-AVC texture view component */\n SLICE_LAYER_EXT = 21,\n\n // 22, 23 reserved\n}\n\n/**\n * we use a magic frame trailer to detect whether a frame is injected\n * by the livekit server and thus to be treated as unencrypted\n * @internal\n */\nexport function isFrameServerInjected(frameData: ArrayBuffer, trailerBytes: Uint8Array): boolean {\n const frameTrailer = new Uint8Array(\n frameData.slice(frameData.byteLength - trailerBytes.byteLength),\n );\n return trailerBytes.every((value, index) => value === frameTrailer[index]);\n}\n","import EventEmitter from 'eventemitter3';\nimport { workerLogger } from '../../logger';\nimport { KEYRING_SIZE } from '../constants';\nimport type { KeyProviderOptions, KeySet, ParticipantKeyHandlerCallbacks } from '../types';\nimport { deriveKeys, importKey, ratchet } from '../utils';\n\n// TODO ParticipantKeyHandlers currently don't get destroyed on participant disconnect\n// we could do this by having a separate worker message on participant disconnected.\n\n/**\n * ParticipantKeyHandler is responsible for providing a cryptor instance with the\n * en-/decryption key of a participant. It assumes that all tracks of a specific participant\n * are encrypted with the same key.\n * Additionally it exposes a method to ratchet a key which can be used by the cryptor either automatically\n * if decryption fails or can be triggered manually on both sender and receiver side.\n *\n */\nexport class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCallbacks> {\n private currentKeyIndex: number;\n\n private cryptoKeyRing: Array<KeySet>;\n\n private enabled: boolean;\n\n private keyProviderOptions: KeyProviderOptions;\n\n private ratchetPromiseMap: Map<number, Promise<CryptoKey>>;\n\n private participantId: string | undefined;\n\n constructor(\n participantId: string | undefined,\n isEnabled: boolean,\n keyProviderOptions: KeyProviderOptions,\n ) {\n super();\n this.currentKeyIndex = 0;\n this.cryptoKeyRing = new Array(KEYRING_SIZE);\n this.enabled = isEnabled;\n this.keyProviderOptions = keyProviderOptions;\n this.ratchetPromiseMap = new Map();\n this.participantId = participantId;\n }\n\n setEnabled(enabled: boolean) {\n this.enabled = enabled;\n }\n\n /**\n * Ratchets the current key (or the one at keyIndex if provided) and\n * returns the ratcheted material\n * if `setKey` is true (default), it will also set the ratcheted key directly on the crypto key ring\n * @param keyIndex\n * @param setKey\n */\n ratchetKey(keyIndex?: number, setKey = true): Promise<CryptoKey> {\n const currentKeyIndex = (keyIndex ??= this.getCurrentKeyIndex());\n\n const existingPromise = this.ratchetPromiseMap.get(currentKeyIndex);\n if (typeof existingPromise !== 'undefined') {\n return existingPromise;\n }\n const ratchetPromise = new Promise<CryptoKey>(async (resolve, reject) => {\n try {\n const currentMaterial = this.getKeySet(currentKeyIndex).material;\n const newMaterial = await importKey(\n await ratchet(currentMaterial, this.keyProviderOptions.ratchetSalt),\n currentMaterial.algorithm.name,\n 'derive',\n );\n\n if (setKey) {\n this.setKeyFromMaterial(newMaterial, currentKeyIndex, true);\n }\n this.emit('keyRatcheted', newMaterial, keyIndex, this.participantId);\n resolve(newMaterial);\n } catch (e) {\n reject(e);\n } finally {\n this.ratchetPromiseMap.delete(currentKeyIndex);\n }\n });\n this.ratchetPromiseMap.set(currentKeyIndex, ratchetPromise);\n return ratchetPromise;\n }\n\n /**\n * takes in a key material with `deriveBits` and `deriveKey` set as key usages\n * and derives encryption keys from the material and sets it on the key ring buffer\n * together with the material\n * also updates the currentKeyIndex\n */\n async setKeyFromMaterial(material: CryptoKey, keyIndex = 0, emitRatchetEvent = false) {\n workerLogger.debug('setting new key');\n if (keyIndex >= 0) {\n this.currentKeyIndex = keyIndex % this.cryptoKeyRing.length;\n }\n const keySet = await deriveKeys(material, this.keyProviderOptions.ratchetSalt);\n this.setKeySet(keySet, this.currentKeyIndex, emitRatchetEvent);\n }\n\n async setKeySet(keySet: KeySet, keyIndex: number, emitRatchetEvent = false) {\n this.cryptoKeyRing[keyIndex % this.cryptoKeyRing.length] = keySet;\n if (emitRatchetEvent) {\n this.emit('keyRatcheted', keySet.material, keyIndex, this.participantId);\n }\n }\n\n async setCurrentKeyIndex(index: number) {\n this.currentKeyIndex = index % this.cryptoKeyRing.length;\n }\n\n isEnabled() {\n return this.enabled;\n }\n\n getCurrentKeyIndex() {\n return this.currentKeyIndex;\n }\n\n /**\n * returns currently used KeySet or the one at `keyIndex` if provided\n * @param keyIndex\n * @returns\n */\n getKeySet(keyIndex?: number) {\n return this.cryptoKeyRing[keyIndex ?? this.currentKeyIndex];\n }\n}\n","import { workerLogger } from '../../logger';\nimport { KEY_PROVIDER_DEFAULTS } from '../constants';\nimport { CryptorErrorReason } from '../errors';\nimport type {\n E2EEWorkerMessage,\n EnableMessage,\n ErrorMessage,\n KeyProviderOptions,\n RatchetMessage,\n} from '../types';\nimport { FrameCryptor } from './FrameCryptor';\nimport { ParticipantKeyHandler } from './ParticipantKeyHandler';\n\nconst participantCryptors: FrameCryptor[] = [];\nconst participantKeys: Map<string, ParticipantKeyHandler> = new Map();\n\nlet publishCryptors: FrameCryptor[] = [];\nlet publisherKeys: ParticipantKeyHandler;\n\nlet isEncryptionEnabled: boolean = false;\n\nlet useSharedKey: boolean = false;\n\nlet sharedKey: CryptoKey | undefined;\n\nlet keyProviderOptions: KeyProviderOptions = KEY_PROVIDER_DEFAULTS;\n\nworkerLogger.setDefaultLevel('info');\n\nonmessage = (ev) => {\n const { kind, data }: E2EEWorkerMessage = ev.data;\n\n switch (kind) {\n case 'init':\n workerLogger.info('worker initialized');\n keyProviderOptions = data.keyProviderOptions;\n useSharedKey = !!data.keyProviderOptions.sharedKey;\n // acknowledge init successful\n const enableMsg: EnableMessage = {\n kind: 'enable',\n data: { enabled: isEncryptionEnabled },\n };\n publisherKeys = new ParticipantKeyHandler(undefined, isEncryptionEnabled, keyProviderOptions);\n publisherKeys.on('keyRatcheted', emitRatchetedKeys);\n postMessage(enableMsg);\n break;\n case 'enable':\n setEncryptionEnabled(data.enabled, data.participantId);\n workerLogger.info('updated e2ee enabled status');\n // acknowledge enable call successful\n postMessage(ev.data);\n break;\n case 'decode':\n let cryptor = getTrackCryptor(data.participantId, data.trackId);\n cryptor.setupTransform(\n kind,\n data.readableStream,\n data.writableStream,\n data.trackId,\n data.codec,\n );\n break;\n case 'encode':\n let pubCryptor = getPublisherCryptor(data.trackId);\n pubCryptor.setupTransform(\n kind,\n data.readableStream,\n data.writableStream,\n data.trackId,\n data.codec,\n );\n break;\n case 'setKey':\n if (useSharedKey) {\n workerLogger.debug('set shared key');\n setSharedKey(data.key, data.keyIndex);\n } else if (data.participantId) {\n getParticipantKeyHandler(data.participantId).setKeyFromMaterial(data.key, data.keyIndex);\n } else {\n workerLogger.error('no participant Id was provided and shared key usage is disabled');\n }\n break;\n case 'removeTransform':\n unsetCryptorParticipant(data.trackId);\n break;\n case 'updateCodec':\n getTrackCryptor(data.participantId, data.trackId).setVideoCodec(data.codec);\n break;\n case 'setRTPMap':\n publishCryptors.forEach((cr) => {\n cr.setRtpMap(data.map);\n });\n break;\n case 'ratchetRequest':\n getParticipantKeyHandler(data.participantId).ratchetKey(data.keyIndex);\n\n default:\n break;\n }\n};\n\nfunction getTrackCryptor(participantId: string, trackId: string) {\n let cryptor = participantCryptors.find((c) => c.getTrackId() === trackId);\n if (!cryptor) {\n workerLogger.info('creating new cryptor for', { participantId });\n if (!keyProviderOptions) {\n throw Error('Missing keyProvider options');\n }\n cryptor = new FrameCryptor({\n participantId,\n keys: getParticipantKeyHandler(participantId),\n keyProviderOptions,\n });\n\n setupCryptorErrorEvents(cryptor);\n participantCryptors.push(cryptor);\n } else if (participantId !== cryptor.getParticipantId()) {\n // assign new participant id to track cryptor and pass in correct key handler\n cryptor.setParticipant(participantId, getParticipantKeyHandler(participantId));\n }\n if (sharedKey) {\n }\n return cryptor;\n}\n\nfunction getParticipantKeyHandler(participantId?: string) {\n if (!participantId) {\n return publisherKeys!;\n }\n let keys = participantKeys.get(participantId);\n if (!keys) {\n keys = new ParticipantKeyHandler(participantId, true, keyProviderOptions);\n if (sharedKey) {\n keys.setKeyFromMaterial(sharedKey);\n }\n participantKeys.set(participantId, keys);\n }\n return keys;\n}\n\nfunction unsetCryptorParticipant(trackId: string) {\n participantCryptors.find((c) => c.getTrackId() === trackId)?.unsetParticipant();\n}\n\nfunction getPublisherCryptor(trackId: string) {\n let publishCryptor = publishCryptors.find((cryptor) => cryptor.getTrackId() === trackId);\n if (!publishCryptor) {\n if (!keyProviderOptions) {\n throw new TypeError('Missing keyProvider options');\n }\n publishCryptor = new FrameCryptor({\n keys: publisherKeys!,\n participantId: 'publisher',\n keyProviderOptions,\n });\n setupCryptorErrorEvents(publishCryptor);\n publishCryptors.push(publishCryptor);\n }\n return publishCryptor;\n}\n\nfunction setEncryptionEnabled(enable: boolean, participantId?: string) {\n if (!participantId) {\n isEncryptionEnabled = enable;\n publisherKeys.setEnabled(enable);\n } else {\n getParticipantKeyHandler(participantId).setEnabled(enable);\n }\n}\n\nfunction setSharedKey(key: CryptoKey, index?: number) {\n workerLogger.debug('setting shared key');\n sharedKey = key;\n publisherKeys?.setKeyFromMaterial(key, index);\n for (const [, keyHandler] of participantKeys) {\n keyHandler.setKeyFromMaterial(key, index);\n }\n}\n\nfunction setupCryptorErrorEvents(cryptor: FrameCryptor) {\n cryptor.on('cryptorError', (error) => {\n const msg: ErrorMessage = {\n kind: 'error',\n data: { error: new Error(`${CryptorErrorReason[error.reason]}: ${error.message}`) },\n };\n postMessage(msg);\n });\n}\n\nfunction emitRatchetedKeys(material: CryptoKey, keyIndex?: number) {\n const msg: RatchetMessage = {\n kind: `ratchetKey`,\n data: {\n // participantId,\n keyIndex,\n material,\n },\n };\n postMessage(msg);\n}\n\n// Operations using RTCRtpScriptTransform.\n// @ts-ignore\nif (self.RTCTransformEvent) {\n workerLogger.debug('setup transform event');\n // @ts-ignore\n self.onrtctransform = (event) => {\n const transformer = event.transformer;\n workerLogger.debug('transformer', transformer);\n transformer.handled = true;\n const { kind, participantId, trackId, codec } = transformer.options;\n const cryptor =\n kind === 'encode' ? getPublisherCryptor(trackId) : getTrackCryptor(participantId, trackId);\n workerLogger.debug('transform', { codec });\n cryptor.setupTransform(kind, transformer.readable, transformer.writable, trackId, codec);\n };\n}\n"],"names":["root","definition","this","noop","undefinedType","isIE","window","navigator","test","userAgent","logMethods","bindMethod","obj","methodName","method","bind","Function","prototype","call","e","apply","arguments","traceForIE","console","log","trace","replaceLoggingMethods","level","loggerName","i","length","methodFactory","debug","enableLoggingWhenConsoleArrives","defaultMethodFactory","undefined","realMethod","Logger","name","defaultLevel","factory","currentLevel","self","storageKey","getPersistedLevel","storedLevel","localStorage","ignore","cookie","document","location","indexOf","encodeURIComponent","exec","slice","levels","TRACE","DEBUG","INFO","WARN","ERROR","SILENT","getLevel","setLevel","persist","toUpperCase","levelNum","levelName","persistLevelIfPossible","setDefaultLevel","resetLevel","removeItem","clearPersistedLevel","enableAll","disableAll","initialLevel","defaultLogger","_loggersByName","getLogger","TypeError","logger","_log","noConflict","getLoggers","exports","module","LogLevel","info","workerLogger","ENCRYPTION_ALGORITHM","UNENCRYPTED_BYTES","key","delta","audio","empty","KEY_PROVIDER_DEFAULTS","sharedKey","ratchetSalt","ratchetWindowSize","LivekitError","Error","constructor","code","message","super","MediaDeviceFailure","CryptorErrorReason","getFailure","error","NotFound","PermissionDenied","DeviceInUse","Other","CryptorError","reason","InternalError","has","Object","hasOwnProperty","prefix","Events","EE","fn","context","once","addListener","emitter","event","listener","evt","_events","push","_eventsCount","clearEvent","EventEmitter","create","__proto__","eventNames","events","names","getOwnPropertySymbols","concat","listeners","handlers","l","ee","Array","listenerCount","emit","a1","a2","a3","a4","a5","args","len","removeListener","j","on","removeAllListeners","off","prefixed","CryptorEvent","AudioPresets","getAlgoOptions","algorithmName","salt","encodedSalt","TextEncoder","encode","hash","ArrayBuffer","iterations","deriveKeys","material","algorithmOptions","algorithm","encryptionKey","crypto","subtle","deriveKey","telephone","maxBitrate","speech","music","musicStereo","musicHighQuality","musicHighQualityStereo","BaseFrameCryptor","encodeFunction","encodedFrame","controller","decodeFunction","FrameCryptor","opts","isKeyInvalid","sendCounts","Map","keys","participantId","rtpMap","keyProviderOptions","unencryptedFrameByteTrailer","_a","unencryptedFrameBytes","setParticipant","id","unsetParticipant","getParticipantId","getTrackId","trackId","setVideoCodec","codec","videoCodec","setRtpMap","map","setupTransform","operation","readable","writable","transformFn","transformStream","TransformStream","transform","pipeThrough","pipeTo","catch","isEnabled","data","byteLength","enqueue","getKeySet","keyIndex","getCurrentKeyIndex","iv","makeIV","getMetadata","synchronizationSource","timestamp","frameHeader","Uint8Array","getUnencryptedBytes","frameTrailer","cipherText","encrypt","additionalData","newData","newUint8","set","MissingKey","frameData","trailerBytes","every","value","index","isFrameServerInjected","decodedFrame","decryptFrame","InvalidKey","warn","initialMaterial","ratchetOpts","ratchetCount","keySet","ivLength","cipherTextStart","cipherTextLength","plainText","decrypt","ratchetedKeySet","RTCEncodedAudioFrame","newMaterial","ratchetKey","frame","setKeySet","setCurrentKeyIndex","setKeyFromMaterial","ivView","DataView","Math","floor","random","sendCount","get","setUint32","isVideoFrame","detectedCodec","getVideoCodec","type","naluIndices","stream","result","start","pos","searchLength","end","findNALUIndices","some","naluIndex","NALUType","SLICE_IDR","SLICE_NON_IDR","includes","parseNALUType","size","payloadType","startByte","kNaluTypeMask","ParticipantKeyHandler","currentKeyIndex","cryptoKeyRing","enabled","ratchetPromiseMap","setEnabled","setKey","existingPromise","ratchetPromise","Promise","resolve","reject","__awaiter","currentMaterial","keyBytes","usage","importKey","deriveBits","ratchet","delete","emitRatchetEvent","participantCryptors","participantKeys","publisherKeys","publishCryptors","isEncryptionEnabled","useSharedKey","getTrackCryptor","cryptor","find","c","getParticipantKeyHandler","setupCryptorErrorEvents","getPublisherCryptor","publishCryptor","msg","kind","postMessage","emitRatchetedKeys","onmessage","ev","enableMsg","enable","readableStream","writableStream","keyHandler","setSharedKey","forEach","cr","RTCTransformEvent","onrtctransform","transformer","handled","options"],"mappings":"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","x_google_ignoreList":[0,5]}
|
1
|
+
{"version":3,"file":"livekit-client.e2ee.worker.js","sources":["../node_modules/loglevel/lib/loglevel.js","../src/logger.ts","../src/e2ee/constants.ts","../src/room/errors.ts","../src/e2ee/errors.ts","../node_modules/eventemitter3/index.js","../src/e2ee/types.ts","../src/room/track/options.ts","../src/e2ee/utils.ts","../src/e2ee/worker/FrameCryptor.ts","../src/e2ee/worker/ParticipantKeyHandler.ts","../src/e2ee/worker/e2ee.worker.ts"],"sourcesContent":["/*\n* loglevel - https://github.com/pimterry/loglevel\n*\n* Copyright (c) 2013 Tim Perry\n* Licensed under the MIT license.\n*/\n(function (root, definition) {\n \"use strict\";\n if (typeof define === 'function' && define.amd) {\n define(definition);\n } else if (typeof module === 'object' && module.exports) {\n module.exports = definition();\n } else {\n root.log = definition();\n }\n}(this, function () {\n \"use strict\";\n\n // Slightly dubious tricks to cut down minimized file size\n var noop = function() {};\n var undefinedType = \"undefined\";\n var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && (\n /Trident\\/|MSIE /.test(window.navigator.userAgent)\n );\n\n var logMethods = [\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\"\n ];\n\n // Cross-browser bind equivalent that works at least back to IE6\n function bindMethod(obj, methodName) {\n var method = obj[methodName];\n if (typeof method.bind === 'function') {\n return method.bind(obj);\n } else {\n try {\n return Function.prototype.bind.call(method, obj);\n } catch (e) {\n // Missing bind shim or IE8 + Modernizr, fallback to wrapping\n return function() {\n return Function.prototype.apply.apply(method, [obj, arguments]);\n };\n }\n }\n }\n\n // Trace() doesn't print the message in IE, so for that case we need to wrap it\n function traceForIE() {\n if (console.log) {\n if (console.log.apply) {\n console.log.apply(console, arguments);\n } else {\n // In old IE, native console methods themselves don't have apply().\n Function.prototype.apply.apply(console.log, [console, arguments]);\n }\n }\n if (console.trace) console.trace();\n }\n\n // Build the best logging method possible for this env\n // Wherever possible we want to bind, not wrap, to preserve stack traces\n function realMethod(methodName) {\n if (methodName === 'debug') {\n methodName = 'log';\n }\n\n if (typeof console === undefinedType) {\n return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives\n } else if (methodName === 'trace' && isIE) {\n return traceForIE;\n } else if (console[methodName] !== undefined) {\n return bindMethod(console, methodName);\n } else if (console.log !== undefined) {\n return bindMethod(console, 'log');\n } else {\n return noop;\n }\n }\n\n // These private functions always need `this` to be set properly\n\n function replaceLoggingMethods(level, loggerName) {\n /*jshint validthis:true */\n for (var i = 0; i < logMethods.length; i++) {\n var methodName = logMethods[i];\n this[methodName] = (i < level) ?\n noop :\n this.methodFactory(methodName, level, loggerName);\n }\n\n // Define log.log as an alias for log.debug\n this.log = this.debug;\n }\n\n // In old IE versions, the console isn't present until you first open it.\n // We build realMethod() replacements here that regenerate logging methods\n function enableLoggingWhenConsoleArrives(methodName, level, loggerName) {\n return function () {\n if (typeof console !== undefinedType) {\n replaceLoggingMethods.call(this, level, loggerName);\n this[methodName].apply(this, arguments);\n }\n };\n }\n\n // By default, we use closely bound real methods wherever possible, and\n // otherwise we wait for a console to appear, and then try again.\n function defaultMethodFactory(methodName, level, loggerName) {\n /*jshint validthis:true */\n return realMethod(methodName) ||\n enableLoggingWhenConsoleArrives.apply(this, arguments);\n }\n\n function Logger(name, defaultLevel, factory) {\n var self = this;\n var currentLevel;\n defaultLevel = defaultLevel == null ? \"WARN\" : defaultLevel;\n\n var storageKey = \"loglevel\";\n if (typeof name === \"string\") {\n storageKey += \":\" + name;\n } else if (typeof name === \"symbol\") {\n storageKey = undefined;\n }\n\n function persistLevelIfPossible(levelNum) {\n var levelName = (logMethods[levelNum] || 'silent').toUpperCase();\n\n if (typeof window === undefinedType || !storageKey) return;\n\n // Use localStorage if available\n try {\n window.localStorage[storageKey] = levelName;\n return;\n } catch (ignore) {}\n\n // Use session cookie as fallback\n try {\n window.document.cookie =\n encodeURIComponent(storageKey) + \"=\" + levelName + \";\";\n } catch (ignore) {}\n }\n\n function getPersistedLevel() {\n var storedLevel;\n\n if (typeof window === undefinedType || !storageKey) return;\n\n try {\n storedLevel = window.localStorage[storageKey];\n } catch (ignore) {}\n\n // Fallback to cookies if local storage gives us nothing\n if (typeof storedLevel === undefinedType) {\n try {\n var cookie = window.document.cookie;\n var location = cookie.indexOf(\n encodeURIComponent(storageKey) + \"=\");\n if (location !== -1) {\n storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1];\n }\n } catch (ignore) {}\n }\n\n // If the stored level is not valid, treat it as if nothing was stored.\n if (self.levels[storedLevel] === undefined) {\n storedLevel = undefined;\n }\n\n return storedLevel;\n }\n\n function clearPersistedLevel() {\n if (typeof window === undefinedType || !storageKey) return;\n\n // Use localStorage if available\n try {\n window.localStorage.removeItem(storageKey);\n return;\n } catch (ignore) {}\n\n // Use session cookie as fallback\n try {\n window.document.cookie =\n encodeURIComponent(storageKey) + \"=; expires=Thu, 01 Jan 1970 00:00:00 UTC\";\n } catch (ignore) {}\n }\n\n /*\n *\n * Public logger API - see https://github.com/pimterry/loglevel for details\n *\n */\n\n self.name = name;\n\n self.levels = { \"TRACE\": 0, \"DEBUG\": 1, \"INFO\": 2, \"WARN\": 3,\n \"ERROR\": 4, \"SILENT\": 5};\n\n self.methodFactory = factory || defaultMethodFactory;\n\n self.getLevel = function () {\n return currentLevel;\n };\n\n self.setLevel = function (level, persist) {\n if (typeof level === \"string\" && self.levels[level.toUpperCase()] !== undefined) {\n level = self.levels[level.toUpperCase()];\n }\n if (typeof level === \"number\" && level >= 0 && level <= self.levels.SILENT) {\n currentLevel = level;\n if (persist !== false) { // defaults to true\n persistLevelIfPossible(level);\n }\n replaceLoggingMethods.call(self, level, name);\n if (typeof console === undefinedType && level < self.levels.SILENT) {\n return \"No console available for logging\";\n }\n } else {\n throw \"log.setLevel() called with invalid level: \" + level;\n }\n };\n\n self.setDefaultLevel = function (level) {\n defaultLevel = level;\n if (!getPersistedLevel()) {\n self.setLevel(level, false);\n }\n };\n\n self.resetLevel = function () {\n self.setLevel(defaultLevel, false);\n clearPersistedLevel();\n };\n\n self.enableAll = function(persist) {\n self.setLevel(self.levels.TRACE, persist);\n };\n\n self.disableAll = function(persist) {\n self.setLevel(self.levels.SILENT, persist);\n };\n\n // Initialize with the right level\n var initialLevel = getPersistedLevel();\n if (initialLevel == null) {\n initialLevel = defaultLevel;\n }\n self.setLevel(initialLevel, false);\n }\n\n /*\n *\n * Top-level API\n *\n */\n\n var defaultLogger = new Logger();\n\n var _loggersByName = {};\n defaultLogger.getLogger = function getLogger(name) {\n if ((typeof name !== \"symbol\" && typeof name !== \"string\") || name === \"\") {\n throw new TypeError(\"You must supply a name when creating a logger.\");\n }\n\n var logger = _loggersByName[name];\n if (!logger) {\n logger = _loggersByName[name] = new Logger(\n name, defaultLogger.getLevel(), defaultLogger.methodFactory);\n }\n return logger;\n };\n\n // Grab the current global log variable in case of overwrite\n var _log = (typeof window !== undefinedType) ? window.log : undefined;\n defaultLogger.noConflict = function() {\n if (typeof window !== undefinedType &&\n window.log === defaultLogger) {\n window.log = _log;\n }\n\n return defaultLogger;\n };\n\n defaultLogger.getLoggers = function getLoggers() {\n return _loggersByName;\n };\n\n // ES6 default export, for compatibility\n defaultLogger['default'] = defaultLogger;\n\n return defaultLogger;\n}));\n","import * as log from 'loglevel';\n\nexport enum LogLevel {\n trace = 0,\n debug = 1,\n info = 2,\n warn = 3,\n error = 4,\n silent = 5,\n}\n\ntype LogLevelString = keyof typeof LogLevel;\n\ntype StructuredLogger = {\n trace: (msg: string, context?: object) => void;\n debug: (msg: string, context?: object) => void;\n info: (msg: string, context?: object) => void;\n warn: (msg: string, context?: object) => void;\n error: (msg: string, context?: object) => void;\n setDefaultLevel: (level: log.LogLevelDesc) => void;\n};\n\nconst livekitLogger = log.getLogger('livekit');\n\nlivekitLogger.setDefaultLevel(LogLevel.info);\n\nexport default livekitLogger as StructuredLogger;\n\nexport function setLogLevel(level: LogLevel | LogLevelString, loggerName?: 'livekit' | 'lk-e2ee') {\n if (loggerName) {\n log.getLogger(loggerName).setLevel(level);\n }\n for (const logger of Object.values(log.getLoggers())) {\n logger.setLevel(level);\n }\n}\n\nexport type LogExtension = (level: LogLevel, msg: string, context?: object) => void;\n\n/**\n * use this to hook into the logging function to allow sending internal livekit logs to third party services\n * if set, the browser logs will lose their stacktrace information (see https://github.com/pimterry/loglevel#writing-plugins)\n */\nexport function setLogExtension(extension: LogExtension) {\n const originalFactory = livekitLogger.methodFactory;\n\n livekitLogger.methodFactory = (methodName, configLevel, loggerName) => {\n const rawMethod = originalFactory(methodName, configLevel, loggerName);\n\n const logLevel = LogLevel[methodName as LogLevelString];\n const needLog = logLevel >= configLevel && logLevel < LogLevel.silent;\n\n return (msg, context?: [msg: string, context: object]) => {\n if (context) rawMethod(msg, context);\n else rawMethod(msg);\n if (needLog) {\n extension(logLevel, msg, context);\n }\n };\n };\n livekitLogger.setLevel(livekitLogger.getLevel()); // Be sure to call setLevel method in order to apply plugin\n}\n\nexport const workerLogger = log.getLogger('lk-e2ee') as StructuredLogger;\n","import type { KeyProviderOptions } from './types';\n\nexport const ENCRYPTION_ALGORITHM = 'AES-GCM';\n\n// We use a ringbuffer of keys so we can change them and still decode packets that were\n// encrypted with an old key. We use a size of 16 which corresponds to the four bits\n// in the frame trailer.\nexport const KEYRING_SIZE = 16;\n\n// We copy the first bytes of the VP8 payload unencrypted.\n// For keyframes this is 10 bytes, for non-keyframes (delta) 3. See\n// https://tools.ietf.org/html/rfc6386#section-9.1\n// This allows the bridge to continue detecting keyframes (only one byte needed in the JVB)\n// and is also a bit easier for the VP8 decoder (i.e. it generates funny garbage pictures\n// instead of being unable to decode).\n// This is a bit for show and we might want to reduce to 1 unconditionally in the final version.\n//\n// For audio (where frame.type is not set) we do not encrypt the opus TOC byte:\n// https://tools.ietf.org/html/rfc6716#section-3.1\nexport const UNENCRYPTED_BYTES = {\n key: 10,\n delta: 3,\n audio: 1, // frame.type is not set on audio, so this is set manually\n empty: 0,\n} as const;\n\n/* We use a 12 byte bit IV. This is signalled in plain together with the\n packet. See https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters */\nexport const IV_LENGTH = 12;\n\n// flag set to indicate that e2ee has been setup for sender/receiver;\nexport const E2EE_FLAG = 'lk_e2ee';\n\nexport const SALT = 'LKFrameEncryptionKey';\n\nexport const KEY_PROVIDER_DEFAULTS: KeyProviderOptions = {\n sharedKey: false,\n ratchetSalt: SALT,\n ratchetWindowSize: 8,\n} as const;\n","export class LivekitError extends Error {\n code: number;\n\n constructor(code: number, message?: string) {\n super(message || 'an error has occured');\n this.code = code;\n }\n}\n\nexport const enum ConnectionErrorReason {\n NotAllowed,\n ServerUnreachable,\n InternalError,\n Cancelled,\n}\n\nexport class ConnectionError extends LivekitError {\n status?: number;\n\n reason?: ConnectionErrorReason;\n\n constructor(message?: string, reason?: ConnectionErrorReason, status?: number) {\n super(1, message);\n this.status = status;\n this.reason = reason;\n }\n}\n\nexport class DeviceUnsupportedError extends LivekitError {\n constructor(message?: string) {\n super(21, message ?? 'device is unsupported');\n }\n}\n\nexport class TrackInvalidError extends LivekitError {\n constructor(message?: string) {\n super(20, message ?? 'track is invalid');\n }\n}\n\nexport class UnsupportedServer extends LivekitError {\n constructor(message?: string) {\n super(10, message ?? 'unsupported server');\n }\n}\n\nexport class UnexpectedConnectionState extends LivekitError {\n constructor(message?: string) {\n super(12, message ?? 'unexpected connection state');\n }\n}\n\nexport class NegotiationError extends LivekitError {\n constructor(message?: string) {\n super(13, message ?? 'unable to negotiate');\n }\n}\n\nexport class PublishDataError extends LivekitError {\n constructor(message?: string) {\n super(13, message ?? 'unable to publish data');\n }\n}\n\nexport enum MediaDeviceFailure {\n // user rejected permissions\n PermissionDenied = 'PermissionDenied',\n // device is not available\n NotFound = 'NotFound',\n // device is in use. On Windows, only a single tab may get access to a device at a time.\n DeviceInUse = 'DeviceInUse',\n Other = 'Other',\n}\n\nexport namespace MediaDeviceFailure {\n export function getFailure(error: any): MediaDeviceFailure | undefined {\n if (error && 'name' in error) {\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\n return MediaDeviceFailure.NotFound;\n }\n if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\n return MediaDeviceFailure.PermissionDenied;\n }\n if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\n return MediaDeviceFailure.DeviceInUse;\n }\n return MediaDeviceFailure.Other;\n }\n }\n}\n","import { LivekitError } from '../room/errors';\n\nexport enum CryptorErrorReason {\n InvalidKey = 0,\n MissingKey = 1,\n InternalError = 2,\n}\n\nexport class CryptorError extends LivekitError {\n reason: CryptorErrorReason;\n\n constructor(message?: string, reason: CryptorErrorReason = CryptorErrorReason.InternalError) {\n super(40, message);\n this.reason = reason;\n }\n}\n","'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n Events.prototype = Object.create(null);\n\n //\n // This hack is needed because the `__proto__` property is still inherited in\n // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n //\n if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {*} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @private\n */\nfunction EE(fn, context, once) {\n this.fn = fn;\n this.context = context;\n this.once = once || false;\n}\n\n/**\n * Add a listener for a given event.\n *\n * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} context The context to invoke the listener with.\n * @param {Boolean} once Specify if the listener is a one-time listener.\n * @returns {EventEmitter}\n * @private\n */\nfunction addListener(emitter, event, fn, context, once) {\n if (typeof fn !== 'function') {\n throw new TypeError('The listener must be a function');\n }\n\n var listener = new EE(fn, context || emitter, once)\n , evt = prefix ? prefix + event : event;\n\n if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;\n else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);\n else emitter._events[evt] = [emitter._events[evt], listener];\n\n return emitter;\n}\n\n/**\n * Clear event by name.\n *\n * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n * @param {(String|Symbol)} evt The Event name.\n * @private\n */\nfunction clearEvent(emitter, evt) {\n if (--emitter._eventsCount === 0) emitter._events = new Events();\n else delete emitter._events[evt];\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @public\n */\nfunction EventEmitter() {\n this._events = new Events();\n this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n var names = []\n , events\n , name;\n\n if (this._eventsCount === 0) return names;\n\n for (name in (events = this._events)) {\n if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n }\n\n if (Object.getOwnPropertySymbols) {\n return names.concat(Object.getOwnPropertySymbols(events));\n }\n\n return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Array} The registered listeners.\n * @public\n */\nEventEmitter.prototype.listeners = function listeners(event) {\n var evt = prefix ? prefix + event : event\n , handlers = this._events[evt];\n\n if (!handlers) return [];\n if (handlers.fn) return [handlers.fn];\n\n for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {\n ee[i] = handlers[i].fn;\n }\n\n return ee;\n};\n\n/**\n * Return the number of listeners listening to a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Number} The number of listeners.\n * @public\n */\nEventEmitter.prototype.listenerCount = function listenerCount(event) {\n var evt = prefix ? prefix + event : event\n , listeners = this._events[evt];\n\n if (!listeners) return 0;\n if (listeners.fn) return 1;\n return listeners.length;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return false;\n\n var listeners = this._events[evt]\n , len = arguments.length\n , args\n , i;\n\n if (listeners.fn) {\n if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n switch (len) {\n case 1: return listeners.fn.call(listeners.context), true;\n case 2: return listeners.fn.call(listeners.context, a1), true;\n case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n }\n\n for (i = 1, args = new Array(len -1); i < len; i++) {\n args[i - 1] = arguments[i];\n }\n\n listeners.fn.apply(listeners.context, args);\n } else {\n var length = listeners.length\n , j;\n\n for (i = 0; i < length; i++) {\n if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n switch (len) {\n case 1: listeners[i].fn.call(listeners[i].context); break;\n case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n default:\n if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n args[j - 1] = arguments[j];\n }\n\n listeners[i].fn.apply(listeners[i].context, args);\n }\n }\n }\n\n return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n return addListener(this, event, fn, context, false);\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n return addListener(this, event, fn, context, true);\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {*} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return this;\n if (!fn) {\n clearEvent(this, evt);\n return this;\n }\n\n var listeners = this._events[evt];\n\n if (listeners.fn) {\n if (\n listeners.fn === fn &&\n (!once || listeners.once) &&\n (!context || listeners.context === context)\n ) {\n clearEvent(this, evt);\n }\n } else {\n for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n if (\n listeners[i].fn !== fn ||\n (once && !listeners[i].once) ||\n (context && listeners[i].context !== context)\n ) {\n events.push(listeners[i]);\n }\n }\n\n //\n // Reset the array, or remove it completely if we have no more listeners.\n //\n if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n else clearEvent(this, evt);\n }\n\n return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {(String|Symbol)} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n var evt;\n\n if (event) {\n evt = prefix ? prefix + event : event;\n if (this._events[evt]) clearEvent(this, evt);\n } else {\n this._events = new Events();\n this._eventsCount = 0;\n }\n\n return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n module.exports = EventEmitter;\n}\n","import type Participant from '../room/participant/Participant';\nimport type { VideoCodec } from '../room/track/options';\nimport type { BaseKeyProvider } from './KeyProvider';\nimport type { CryptorError } from './errors';\n\nexport interface BaseMessage {\n kind: string;\n data?: unknown;\n}\n\nexport interface InitMessage extends BaseMessage {\n kind: 'init';\n data: {\n keyProviderOptions: KeyProviderOptions;\n };\n}\n\nexport interface SetKeyMessage extends BaseMessage {\n kind: 'setKey';\n data: {\n participantId?: string;\n key: CryptoKey;\n keyIndex?: number;\n };\n}\n\nexport interface RTPVideoMapMessage extends BaseMessage {\n kind: 'setRTPMap';\n data: {\n map: Map<number, VideoCodec>;\n };\n}\n\nexport interface EncodeMessage extends BaseMessage {\n kind: 'decode' | 'encode';\n data: {\n participantId: string;\n readableStream: ReadableStream;\n writableStream: WritableStream;\n trackId: string;\n codec?: VideoCodec;\n };\n}\n\nexport interface RemoveTransformMessage extends BaseMessage {\n kind: 'removeTransform';\n data: {\n participantId: string;\n trackId: string;\n };\n}\n\nexport interface UpdateCodecMessage extends BaseMessage {\n kind: 'updateCodec';\n data: {\n participantId: string;\n trackId: string;\n codec: VideoCodec;\n };\n}\n\nexport interface RatchetRequestMessage extends BaseMessage {\n kind: 'ratchetRequest';\n data: {\n participantId: string | undefined;\n keyIndex?: number;\n };\n}\n\nexport interface RatchetMessage extends BaseMessage {\n kind: 'ratchetKey';\n data: {\n // participantId: string | undefined;\n keyIndex?: number;\n material: CryptoKey;\n };\n}\n\nexport interface ErrorMessage extends BaseMessage {\n kind: 'error';\n data: {\n error: Error;\n };\n}\n\nexport interface EnableMessage extends BaseMessage {\n kind: 'enable';\n data: {\n // if no participant id is set it indicates publisher encryption enable/disable\n participantId?: string;\n enabled: boolean;\n };\n}\n\nexport type E2EEWorkerMessage =\n | InitMessage\n | SetKeyMessage\n | EncodeMessage\n | ErrorMessage\n | EnableMessage\n | RemoveTransformMessage\n | RTPVideoMapMessage\n | UpdateCodecMessage\n | RatchetRequestMessage\n | RatchetMessage;\n\nexport type KeySet = { material: CryptoKey; encryptionKey: CryptoKey };\n\nexport type KeyProviderOptions = {\n sharedKey: boolean;\n ratchetSalt: string;\n ratchetWindowSize: number;\n};\n\nexport type KeyProviderCallbacks = {\n setKey: (keyInfo: KeyInfo) => void;\n ratchetRequest: (participantId?: string, keyIndex?: number) => void;\n /** currently only emitted for local participant */\n keyRatcheted: (material: CryptoKey, keyIndex?: number) => void;\n};\n\nexport type ParticipantKeyHandlerCallbacks = {\n keyRatcheted: (material: CryptoKey, keyIndex?: number, participantId?: string) => void;\n};\n\nexport type E2EEManagerCallbacks = {\n participantEncryptionStatusChanged: (enabled: boolean, participant?: Participant) => void;\n encryptionError: (error: Error) => void;\n};\n\nexport const EncryptionEvent = {\n ParticipantEncryptionStatusChanged: 'participantEncryptionStatusChanged',\n Error: 'encryptionError',\n} as const;\n\nexport type CryptorCallbacks = {\n cryptorError: (error: CryptorError) => void;\n};\n\nexport const CryptorEvent = {\n Error: 'cryptorError',\n} as const;\n\nexport type KeyInfo = {\n key: CryptoKey;\n participantId?: string;\n keyIndex?: number;\n};\n\nexport type E2EEOptions = {\n keyProvider: BaseKeyProvider;\n worker: Worker;\n};\n\nexport type DecodeRatchetOptions = {\n /** attempts */\n ratchetCount: number;\n /** ratcheted key to try */\n encryptionKey?: CryptoKey;\n};\n","import type { Track } from './Track';\n\nexport interface TrackPublishDefaults {\n /**\n * encoding parameters for camera track\n */\n videoEncoding?: VideoEncoding;\n\n /**\n * @experimental\n */\n backupCodec?: { codec: BackupVideoCodec; encoding: VideoEncoding } | false;\n\n /**\n * encoding parameters for screen share track\n */\n screenShareEncoding?: VideoEncoding;\n\n /**\n * codec, defaults to vp8; for svc codecs, auto enable vp8\n * as backup. (TBD)\n */\n videoCodec?: VideoCodec;\n\n /**\n * max audio bitrate, defaults to [[AudioPresets.music]]\n * @deprecated use `audioPreset` instead\n */\n audioBitrate?: number;\n\n /**\n * which audio preset should be used for publishing (audio) tracks\n * defaults to [[AudioPresets.music]]\n */\n audioPreset?: AudioPreset;\n\n /**\n * dtx (Discontinuous Transmission of audio), enabled by default for mono tracks.\n */\n dtx?: boolean;\n\n /**\n * red (Redundant Audio Data), enabled by default for mono tracks.\n */\n red?: boolean;\n\n /**\n * publish track in stereo mode (or set to false to disable). defaults determined by capture channel count.\n */\n forceStereo?: boolean;\n\n /**\n * use simulcast, defaults to true.\n * When using simulcast, LiveKit will publish up to three versions of the stream\n * at various resolutions.\n */\n simulcast?: boolean;\n\n /**\n * scalability mode for svc codecs, defaults to 'L3T3'.\n * for svc codecs, simulcast is disabled.\n */\n scalabilityMode?: ScalabilityMode;\n\n /**\n * Up to two additional simulcast layers to publish in addition to the original\n * Track.\n * When left blank, it defaults to h180, h360.\n * If a SVC codec is used (VP9 or AV1), this field has no effect.\n *\n * To publish three total layers, you would specify:\n * {\n * videoEncoding: {...}, // encoding of the primary layer\n * videoSimulcastLayers: [\n * VideoPresets.h540,\n * VideoPresets.h216,\n * ],\n * }\n */\n videoSimulcastLayers?: Array<VideoPreset>;\n\n /**\n * custom video simulcast layers for screen tracks\n * Note: the layers need to be ordered from lowest to highest quality\n */\n screenShareSimulcastLayers?: Array<VideoPreset>;\n\n /**\n * For local tracks, stop the underlying MediaStreamTrack when the track is muted (or paused)\n * on some platforms, this option is necessary to disable the microphone recording indicator.\n * Note: when this is enabled, and BT devices are connected, they will transition between\n * profiles (e.g. HFP to A2DP) and there will be an audible difference in playback.\n *\n * defaults to false\n */\n stopMicTrackOnMute?: boolean;\n}\n\n/**\n * Options when publishing tracks\n */\nexport interface TrackPublishOptions extends TrackPublishDefaults {\n /**\n * set a track name\n */\n name?: string;\n\n /**\n * Source of track, camera, microphone, or screen\n */\n source?: Track.Source;\n}\n\nexport interface CreateLocalTracksOptions {\n /**\n * audio track options, true to create with defaults. false if audio shouldn't be created\n * default true\n */\n audio?: boolean | AudioCaptureOptions;\n\n /**\n * video track options, true to create with defaults. false if video shouldn't be created\n * default true\n */\n video?: boolean | VideoCaptureOptions;\n}\n\nexport interface VideoCaptureOptions {\n /**\n * A ConstrainDOMString object specifying a device ID or an array of device\n * IDs which are acceptable and/or required.\n */\n deviceId?: ConstrainDOMString;\n\n /**\n * a facing or an array of facings which are acceptable and/or required.\n */\n facingMode?: 'user' | 'environment' | 'left' | 'right';\n\n resolution?: VideoResolution;\n}\n\nexport interface ScreenShareCaptureOptions {\n /**\n * true to capture audio shared. browser support for audio capturing in\n * screenshare is limited: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#browser_compatibility\n */\n audio?: boolean | AudioCaptureOptions;\n\n /** capture resolution, defaults to full HD */\n resolution?: VideoResolution;\n\n /** a CaptureController object instance containing methods that can be used to further manipulate the capture session if included. */\n controller?: unknown; // TODO replace type with CaptureController once it lands in TypeScript\n\n /** specifies whether the browser should allow the user to select the current tab for capture */\n selfBrowserSurface?: 'include' | 'exclude';\n\n /** specifies whether the browser should display a control to allow the user to dynamically switch the shared tab during screen-sharing. */\n surfaceSwitching?: 'include' | 'exclude';\n\n /** specifies whether the browser should include the system audio among the possible audio sources offered to the user */\n systemAudio?: 'include' | 'exclude';\n\n /**\n * Experimental option to control whether the audio playing in a tab will continue to be played out of a user's\n * local speakers when the tab is captured.\n */\n suppressLocalAudioPlayback?: boolean;\n}\n\nexport interface AudioCaptureOptions {\n /**\n * specifies whether automatic gain control is preferred and/or required\n */\n autoGainControl?: ConstrainBoolean;\n\n /**\n * the channel count or range of channel counts which are acceptable and/or required\n */\n channelCount?: ConstrainULong;\n\n /**\n * A ConstrainDOMString object specifying a device ID or an array of device\n * IDs which are acceptable and/or required.\n */\n deviceId?: ConstrainDOMString;\n\n /**\n * whether or not echo cancellation is preferred and/or required\n */\n echoCancellation?: ConstrainBoolean;\n\n /**\n * the latency or range of latencies which are acceptable and/or required.\n */\n latency?: ConstrainDouble;\n\n /**\n * whether noise suppression is preferred and/or required.\n */\n noiseSuppression?: ConstrainBoolean;\n\n /**\n * the sample rate or range of sample rates which are acceptable and/or required.\n */\n sampleRate?: ConstrainULong;\n\n /**\n * sample size or range of sample sizes which are acceptable and/or required.\n */\n sampleSize?: ConstrainULong;\n}\n\nexport interface AudioOutputOptions {\n /**\n * deviceId to output audio\n *\n * Only supported on browsers where `setSinkId` is available\n */\n deviceId?: string;\n}\n\nexport interface VideoResolution {\n width: number;\n height: number;\n frameRate?: number;\n aspectRatio?: number;\n}\n\nexport interface VideoEncoding {\n maxBitrate: number;\n maxFramerate?: number;\n priority?: RTCPriorityType;\n}\n\nexport class VideoPreset {\n encoding: VideoEncoding;\n\n width: number;\n\n height: number;\n\n constructor(\n width: number,\n height: number,\n maxBitrate: number,\n maxFramerate?: number,\n priority?: RTCPriorityType,\n ) {\n this.width = width;\n this.height = height;\n this.encoding = {\n maxBitrate,\n maxFramerate,\n priority,\n };\n }\n\n get resolution(): VideoResolution {\n return {\n width: this.width,\n height: this.height,\n frameRate: this.encoding.maxFramerate,\n aspectRatio: this.width / this.height,\n };\n }\n}\n\nexport interface AudioPreset {\n maxBitrate: number;\n priority?: RTCPriorityType;\n}\n\nconst backupCodecs = ['vp8', 'h264'] as const;\n\nexport const videoCodecs = ['vp8', 'h264', 'vp9', 'av1'] as const;\n\nexport type VideoCodec = (typeof videoCodecs)[number];\n\nexport type BackupVideoCodec = (typeof backupCodecs)[number];\n\nexport function isBackupCodec(codec: string): codec is BackupVideoCodec {\n return !!backupCodecs.find((backup) => backup === codec);\n}\n\nexport function isCodecEqual(c1: string | undefined, c2: string | undefined): boolean {\n return (\n c1?.toLowerCase().replace(/audio\\/|video\\//y, '') ===\n c2?.toLowerCase().replace(/audio\\/|video\\//y, '')\n );\n}\n\n/**\n * scalability modes for svc, only supprot l3t3 now.\n */\nexport type ScalabilityMode = 'L3T3' | 'L3T3_KEY';\n\nexport namespace AudioPresets {\n export const telephone: AudioPreset = {\n maxBitrate: 12_000,\n };\n export const speech: AudioPreset = {\n maxBitrate: 20_000,\n };\n export const music: AudioPreset = {\n maxBitrate: 32_000,\n };\n export const musicStereo: AudioPreset = {\n maxBitrate: 48_000,\n };\n export const musicHighQuality: AudioPreset = {\n maxBitrate: 64_000,\n };\n export const musicHighQualityStereo: AudioPreset = {\n maxBitrate: 96_000,\n };\n}\n\n/**\n * Sane presets for video resolution/encoding\n */\nexport const VideoPresets = {\n h90: new VideoPreset(160, 90, 60_000, 15),\n h180: new VideoPreset(320, 180, 120_000, 15),\n h216: new VideoPreset(384, 216, 180_000, 15),\n h360: new VideoPreset(640, 360, 300_000, 20),\n h540: new VideoPreset(960, 540, 600_000, 25),\n h720: new VideoPreset(1280, 720, 1_700_000, 30),\n h1080: new VideoPreset(1920, 1080, 3_000_000, 30),\n h1440: new VideoPreset(2560, 1440, 5_000_000, 30),\n h2160: new VideoPreset(3840, 2160, 8_000_000, 30),\n} as const;\n\n/**\n * Four by three presets\n */\nexport const VideoPresets43 = {\n h120: new VideoPreset(160, 120, 80_000, 15),\n h180: new VideoPreset(240, 180, 100_000, 15),\n h240: new VideoPreset(320, 240, 150_000, 15),\n h360: new VideoPreset(480, 360, 225_000, 20),\n h480: new VideoPreset(640, 480, 300_000, 20),\n h540: new VideoPreset(720, 540, 450_000, 25),\n h720: new VideoPreset(960, 720, 1_500_000, 30),\n h1080: new VideoPreset(1440, 1080, 2_500_000, 30),\n h1440: new VideoPreset(1920, 1440, 3_500_000, 30),\n} as const;\n\nexport const ScreenSharePresets = {\n h360fps3: new VideoPreset(640, 360, 200_000, 3, 'medium'),\n h720fps5: new VideoPreset(1280, 720, 400_000, 5, 'medium'),\n h720fps15: new VideoPreset(1280, 720, 1_000_000, 15, 'medium'),\n h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15, 'medium'),\n h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30, 'medium'),\n} as const;\n","import { videoCodecs } from '../room/track/options';\nimport type { VideoCodec } from '../room/track/options';\nimport { ENCRYPTION_ALGORITHM } from './constants';\n\nexport function isE2EESupported() {\n return isInsertableStreamSupported() || isScriptTransformSupported();\n}\n\nexport function isScriptTransformSupported() {\n // @ts-ignore\n return typeof window.RTCRtpScriptTransform !== 'undefined';\n}\n\nexport function isInsertableStreamSupported() {\n return (\n typeof window.RTCRtpSender !== 'undefined' &&\n // @ts-ignore\n typeof window.RTCRtpSender.prototype.createEncodedStreams !== 'undefined'\n );\n}\n\nexport function isVideoFrame(\n frame: RTCEncodedAudioFrame | RTCEncodedVideoFrame,\n): frame is RTCEncodedVideoFrame {\n return 'type' in frame;\n}\n\nexport async function importKey(\n keyBytes: Uint8Array | ArrayBuffer,\n algorithm: string | { name: string } = { name: ENCRYPTION_ALGORITHM },\n usage: 'derive' | 'encrypt' = 'encrypt',\n) {\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey\n return crypto.subtle.importKey(\n 'raw',\n keyBytes,\n algorithm,\n false,\n usage === 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt'],\n );\n}\n\nexport async function createKeyMaterialFromString(password: string) {\n let enc = new TextEncoder();\n\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n enc.encode(password),\n {\n name: 'PBKDF2',\n },\n false,\n ['deriveBits', 'deriveKey'],\n );\n\n return keyMaterial;\n}\n\nfunction getAlgoOptions(algorithmName: string, salt: string) {\n const textEncoder = new TextEncoder();\n const encodedSalt = textEncoder.encode(salt);\n switch (algorithmName) {\n case 'HKDF':\n return {\n name: 'HKDF',\n salt: encodedSalt,\n hash: 'SHA-256',\n info: new ArrayBuffer(128),\n };\n case 'PBKDF2': {\n return {\n name: 'PBKDF2',\n salt: encodedSalt,\n hash: 'SHA-256',\n iterations: 100000,\n };\n }\n default:\n throw new Error(`algorithm ${algorithmName} is currently unsupported`);\n }\n}\n\n/**\n * Derives a set of keys from the master key.\n * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1\n */\nexport async function deriveKeys(material: CryptoKey, salt: string) {\n const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF\n // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams\n const encryptionKey = await crypto.subtle.deriveKey(\n algorithmOptions,\n material,\n {\n name: ENCRYPTION_ALGORITHM,\n length: 128,\n },\n false,\n ['encrypt', 'decrypt'],\n );\n\n return { material, encryptionKey };\n}\n\nexport function createE2EEKey(): Uint8Array {\n return window.crypto.getRandomValues(new Uint8Array(32));\n}\n\nexport function mimeTypeToVideoCodecString(mimeType: string) {\n const codec = mimeType.split('/')[1].toLowerCase() as VideoCodec;\n if (!videoCodecs.includes(codec)) {\n throw Error(`Video codec not supported: ${codec}`);\n }\n return codec;\n}\n\n/**\n * Ratchets a key. See\n * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1\n */\nexport async function ratchet(material: CryptoKey, salt: string): Promise<ArrayBuffer> {\n const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits\n return crypto.subtle.deriveBits(algorithmOptions, material, 256);\n}\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n// TODO code inspired by https://github.com/webrtc/samples/blob/gh-pages/src/content/insertable-streams/endtoend-encryption/js/worker.js\nimport EventEmitter from 'eventemitter3';\nimport { workerLogger } from '../../logger';\nimport type { VideoCodec } from '../../room/track/options';\nimport { ENCRYPTION_ALGORITHM, IV_LENGTH, UNENCRYPTED_BYTES } from '../constants';\nimport { CryptorError, CryptorErrorReason } from '../errors';\nimport {\n CryptorCallbacks,\n CryptorEvent,\n DecodeRatchetOptions,\n KeyProviderOptions,\n KeySet,\n} from '../types';\nimport { deriveKeys, isVideoFrame } from '../utils';\nimport type { ParticipantKeyHandler } from './ParticipantKeyHandler';\n\nexport interface FrameCryptorConstructor {\n new (opts?: unknown): BaseFrameCryptor;\n}\n\nexport interface TransformerInfo {\n readable: ReadableStream;\n writable: WritableStream;\n transformer: TransformStream;\n abortController: AbortController;\n}\n\nexport class BaseFrameCryptor extends EventEmitter<CryptorCallbacks> {\n encodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ): Promise<any> {\n throw Error('not implemented for subclass');\n }\n\n decodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ): Promise<any> {\n throw Error('not implemented for subclass');\n }\n}\n\n/**\n * Cryptor is responsible for en-/decrypting media frames.\n * Each Cryptor instance is responsible for en-/decrypting a single mediaStreamTrack.\n */\nexport class FrameCryptor extends BaseFrameCryptor {\n private sendCounts: Map<number, number>;\n\n private participantId: string | undefined;\n\n private trackId: string | undefined;\n\n private keys: ParticipantKeyHandler;\n\n private videoCodec?: VideoCodec;\n\n private rtpMap: Map<number, VideoCodec>;\n\n private keyProviderOptions: KeyProviderOptions;\n\n /**\n * used for detecting server injected unencrypted frames\n */\n private unencryptedFrameByteTrailer: Uint8Array;\n\n constructor(opts: {\n keys: ParticipantKeyHandler;\n participantId: string;\n keyProviderOptions: KeyProviderOptions;\n unencryptedFrameBytes?: Uint8Array;\n }) {\n super();\n this.sendCounts = new Map();\n this.keys = opts.keys;\n this.participantId = opts.participantId;\n this.rtpMap = new Map();\n this.keyProviderOptions = opts.keyProviderOptions;\n this.unencryptedFrameByteTrailer =\n opts.unencryptedFrameBytes ?? new TextEncoder().encode('LKROCKS');\n }\n\n /**\n * Assign a different participant to the cryptor.\n * useful for transceiver re-use\n * @param id\n * @param keys\n */\n setParticipant(id: string, keys: ParticipantKeyHandler) {\n this.participantId = id;\n this.keys = keys;\n }\n\n unsetParticipant() {\n this.participantId = undefined;\n }\n\n getParticipantId() {\n return this.participantId;\n }\n\n getTrackId() {\n return this.trackId;\n }\n\n /**\n * Update the video codec used by the mediaStreamTrack\n * @param codec\n */\n setVideoCodec(codec: VideoCodec) {\n this.videoCodec = codec;\n }\n\n /**\n * rtp payload type map used for figuring out codec of payload type when encoding\n * @param map\n */\n setRtpMap(map: Map<number, VideoCodec>) {\n this.rtpMap = map;\n }\n\n setupTransform(\n operation: 'encode' | 'decode',\n readable: ReadableStream,\n writable: WritableStream,\n trackId: string,\n codec?: VideoCodec,\n ) {\n if (codec) {\n console.info('setting codec on cryptor to', codec);\n this.videoCodec = codec;\n }\n const transformFn = operation === 'encode' ? this.encodeFunction : this.decodeFunction;\n const transformStream = new TransformStream({\n transform: transformFn.bind(this),\n });\n\n readable\n .pipeThrough(transformStream)\n .pipeTo(writable)\n .catch((e) => {\n console.error(e);\n this.emit('cryptorError', e instanceof CryptorError ? e : new CryptorError(e.message));\n });\n this.trackId = trackId;\n }\n\n /**\n * Function that will be injected in a stream and will encrypt the given encoded frames.\n *\n * @param {RTCEncodedVideoFrame|RTCEncodedAudioFrame} encodedFrame - Encoded video frame.\n * @param {TransformStreamDefaultController} controller - TransportStreamController.\n *\n * The VP8 payload descriptor described in\n * https://tools.ietf.org/html/rfc7741#section-4.2\n * is part of the RTP packet and not part of the frame and is not controllable by us.\n * This is fine as the SFU keeps having access to it for routing.\n *\n * The encrypted frame is formed as follows:\n * 1) Find unencrypted byte length, depending on the codec, frame type and kind.\n * 2) Form the GCM IV for the frame as described above.\n * 3) Encrypt the rest of the frame using AES-GCM.\n * 4) Allocate space for the encrypted frame.\n * 5) Copy the unencrypted bytes to the start of the encrypted frame.\n * 6) Append the ciphertext to the encrypted frame.\n * 7) Append the IV.\n * 8) Append a single byte for the key identifier.\n * 9) Enqueue the encrypted frame for sending.\n */\n async encodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ) {\n if (\n !this.keys.isEnabled() ||\n // skip for encryption for empty dtx frames\n encodedFrame.data.byteLength === 0\n ) {\n return controller.enqueue(encodedFrame);\n }\n\n const { encryptionKey } = this.keys.getKeySet();\n const keyIndex = this.keys.getCurrentKeyIndex();\n\n if (encryptionKey) {\n const iv = this.makeIV(\n encodedFrame.getMetadata().synchronizationSource ?? -1,\n encodedFrame.timestamp,\n );\n\n // Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.\n const frameHeader = new Uint8Array(\n encodedFrame.data,\n 0,\n this.getUnencryptedBytes(encodedFrame),\n );\n\n // Frame trailer contains the R|IV_LENGTH and key index\n const frameTrailer = new Uint8Array(2);\n\n frameTrailer[0] = IV_LENGTH;\n frameTrailer[1] = keyIndex;\n\n // Construct frame trailer. Similar to the frame header described in\n // https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2\n // but we put it at the end.\n //\n // ---------+-------------------------+-+---------+----\n // payload |IV...(length = IV_LENGTH)|R|IV_LENGTH|KID |\n // ---------+-------------------------+-+---------+----\n try {\n const cipherText = await crypto.subtle.encrypt(\n {\n name: ENCRYPTION_ALGORITHM,\n iv,\n additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength),\n },\n encryptionKey,\n new Uint8Array(encodedFrame.data, this.getUnencryptedBytes(encodedFrame)),\n );\n\n const newData = new ArrayBuffer(\n frameHeader.byteLength + cipherText.byteLength + iv.byteLength + frameTrailer.byteLength,\n );\n const newUint8 = new Uint8Array(newData);\n\n newUint8.set(frameHeader); // copy first bytes.\n newUint8.set(new Uint8Array(cipherText), frameHeader.byteLength); // add ciphertext.\n newUint8.set(new Uint8Array(iv), frameHeader.byteLength + cipherText.byteLength); // append IV.\n newUint8.set(frameTrailer, frameHeader.byteLength + cipherText.byteLength + iv.byteLength); // append frame trailer.\n\n encodedFrame.data = newData;\n\n return controller.enqueue(encodedFrame);\n } catch (e: any) {\n // TODO: surface this to the app.\n workerLogger.error(e);\n }\n } else {\n this.emit(\n CryptorEvent.Error,\n new CryptorError(`encryption key missing for encoding`, CryptorErrorReason.MissingKey),\n );\n }\n }\n\n /**\n * Function that will be injected in a stream and will decrypt the given encoded frames.\n *\n * @param {RTCEncodedVideoFrame|RTCEncodedAudioFrame} encodedFrame - Encoded video frame.\n * @param {TransformStreamDefaultController} controller - TransportStreamController.\n */\n async decodeFunction(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n controller: TransformStreamDefaultController,\n ) {\n if (\n !this.keys.isEnabled() ||\n // skip for decryption for empty dtx frames\n encodedFrame.data.byteLength === 0 ||\n // skip decryption if frame is server injected\n isFrameServerInjected(encodedFrame.data, this.unencryptedFrameByteTrailer)\n ) {\n return controller.enqueue(encodedFrame);\n }\n const data = new Uint8Array(encodedFrame.data);\n const keyIndex = data[encodedFrame.data.byteLength - 1];\n\n if (this.keys.getKeySet(keyIndex) && this.keys.hasValidKey) {\n try {\n const decodedFrame = await this.decryptFrame(encodedFrame, keyIndex);\n if (decodedFrame) {\n return controller.enqueue(decodedFrame);\n }\n } catch (error) {\n if (error instanceof CryptorError && error.reason === CryptorErrorReason.InvalidKey) {\n if (this.keys.hasValidKey) {\n workerLogger.warn('invalid key');\n this.emit(\n CryptorEvent.Error,\n new CryptorError(\n `invalid key for participant ${this.participantId}`,\n CryptorErrorReason.InvalidKey,\n ),\n );\n this.keys.hasValidKey = false;\n }\n } else {\n workerLogger.warn('decoding frame failed', { error });\n }\n }\n }\n\n return controller.enqueue(encodedFrame);\n }\n\n /**\n * Function that will decrypt the given encoded frame. If the decryption fails, it will\n * ratchet the key for up to RATCHET_WINDOW_SIZE times.\n */\n async decryptFrame(\n encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,\n keyIndex: number,\n initialMaterial: KeySet | undefined = undefined,\n ratchetOpts: DecodeRatchetOptions = { ratchetCount: 0 },\n ): Promise<RTCEncodedVideoFrame | RTCEncodedAudioFrame | undefined> {\n const keySet = this.keys.getKeySet(keyIndex);\n\n // Construct frame trailer. Similar to the frame header described in\n // https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2\n // but we put it at the end.\n //\n // ---------+-------------------------+-+---------+----\n // payload |IV...(length = IV_LENGTH)|R|IV_LENGTH|KID |\n // ---------+-------------------------+-+---------+----\n\n try {\n const frameHeader = new Uint8Array(\n encodedFrame.data,\n 0,\n this.getUnencryptedBytes(encodedFrame),\n );\n const frameTrailer = new Uint8Array(encodedFrame.data, encodedFrame.data.byteLength - 2, 2);\n\n const ivLength = frameTrailer[0];\n const iv = new Uint8Array(\n encodedFrame.data,\n encodedFrame.data.byteLength - ivLength - frameTrailer.byteLength,\n ivLength,\n );\n\n const cipherTextStart = frameHeader.byteLength;\n const cipherTextLength =\n encodedFrame.data.byteLength -\n (frameHeader.byteLength + ivLength + frameTrailer.byteLength);\n\n const plainText = await crypto.subtle.decrypt(\n {\n name: ENCRYPTION_ALGORITHM,\n iv,\n additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength),\n },\n ratchetOpts.encryptionKey ?? keySet.encryptionKey,\n new Uint8Array(encodedFrame.data, cipherTextStart, cipherTextLength),\n );\n\n const newData = new ArrayBuffer(frameHeader.byteLength + plainText.byteLength);\n const newUint8 = new Uint8Array(newData);\n\n newUint8.set(new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength));\n newUint8.set(new Uint8Array(plainText), frameHeader.byteLength);\n\n encodedFrame.data = newData;\n\n return encodedFrame;\n } catch (error: any) {\n if (this.keyProviderOptions.ratchetWindowSize > 0) {\n if (ratchetOpts.ratchetCount < this.keyProviderOptions.ratchetWindowSize) {\n workerLogger.debug(\n `ratcheting key attempt ${ratchetOpts.ratchetCount} of ${\n this.keyProviderOptions.ratchetWindowSize\n }, for kind ${encodedFrame instanceof RTCEncodedAudioFrame ? 'audio' : 'video'}`,\n );\n\n let ratchetedKeySet: KeySet | undefined;\n if (keySet === this.keys.getKeySet(keyIndex)) {\n // only ratchet if the currently set key is still the same as the one used to decrypt this frame\n // if not, it might be that a different frame has already ratcheted and we try with that one first\n const newMaterial = await this.keys.ratchetKey(keyIndex, false);\n\n ratchetedKeySet = await deriveKeys(newMaterial, this.keyProviderOptions.ratchetSalt);\n }\n\n const frame = await this.decryptFrame(encodedFrame, keyIndex, initialMaterial || keySet, {\n ratchetCount: ratchetOpts.ratchetCount + 1,\n encryptionKey: ratchetedKeySet?.encryptionKey,\n });\n if (frame && ratchetedKeySet) {\n this.keys.setKeySet(ratchetedKeySet, keyIndex, true);\n // decryption was successful, set the new key index to reflect the ratcheted key set\n this.keys.setCurrentKeyIndex(keyIndex);\n }\n return frame;\n } else {\n /**\n * Since the key it is first send and only afterwards actually used for encrypting, there were\n * situations when the decrypting failed due to the fact that the received frame was not encrypted\n * yet and ratcheting, of course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE times,\n * we come back to the initial key.\n */\n if (initialMaterial) {\n workerLogger.debug('resetting to initial material');\n this.keys.setKeyFromMaterial(initialMaterial.material, keyIndex);\n }\n\n this.keys.hasValidKey = false;\n\n workerLogger.warn('maximum ratchet attempts exceeded, resetting key');\n this.emit(\n CryptorEvent.Error,\n new CryptorError(\n `valid key missing for participant ${this.participantId}`,\n CryptorErrorReason.MissingKey,\n ),\n );\n }\n } else {\n throw new CryptorError(\n 'Decryption failed, most likely because of an invalid key',\n CryptorErrorReason.InvalidKey,\n );\n }\n }\n }\n\n /**\n * Construct the IV used for AES-GCM and sent (in plain) with the packet similar to\n * https://tools.ietf.org/html/rfc7714#section-8.1\n * It concatenates\n * - the 32 bit synchronization source (SSRC) given on the encoded frame,\n * - the 32 bit rtp timestamp given on the encoded frame,\n * - a send counter that is specific to the SSRC. Starts at a random number.\n * The send counter is essentially the pictureId but we currently have to implement this ourselves.\n * There is no XOR with a salt. Note that this IV leaks the SSRC to the receiver but since this is\n * randomly generated and SFUs may not rewrite this is considered acceptable.\n * The SSRC is used to allow demultiplexing multiple streams with the same key, as described in\n * https://tools.ietf.org/html/rfc3711#section-4.1.1\n * The RTP timestamp is 32 bits and advances by the codec clock rate (90khz for video, 48khz for\n * opus audio) every second. For video it rolls over roughly every 13 hours.\n * The send counter will advance at the frame rate (30fps for video, 50fps for 20ms opus audio)\n * every second. It will take a long time to roll over.\n *\n * See also https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams\n */\n private makeIV(synchronizationSource: number, timestamp: number) {\n const iv = new ArrayBuffer(IV_LENGTH);\n const ivView = new DataView(iv);\n\n // having to keep our own send count (similar to a picture id) is not ideal.\n if (!this.sendCounts.has(synchronizationSource)) {\n // Initialize with a random offset, similar to the RTP sequence number.\n this.sendCounts.set(synchronizationSource, Math.floor(Math.random() * 0xffff));\n }\n\n const sendCount = this.sendCounts.get(synchronizationSource) ?? 0;\n\n ivView.setUint32(0, synchronizationSource);\n ivView.setUint32(4, timestamp);\n ivView.setUint32(8, timestamp - (sendCount % 0xffff));\n\n this.sendCounts.set(synchronizationSource, sendCount + 1);\n\n return iv;\n }\n\n getUnencryptedBytes(frame: RTCEncodedVideoFrame | RTCEncodedAudioFrame): number {\n if (isVideoFrame(frame)) {\n let detectedCodec = this.getVideoCodec(frame) ?? this.videoCodec;\n\n if (detectedCodec === 'av1' || detectedCodec === 'vp9') {\n throw new Error(`${detectedCodec} is not yet supported for end to end encryption`);\n }\n\n if (detectedCodec === 'vp8') {\n return UNENCRYPTED_BYTES[frame.type];\n }\n\n const data = new Uint8Array(frame.data);\n try {\n const naluIndices = findNALUIndices(data);\n\n // if the detected codec is undefined we test whether it _looks_ like a h264 frame as a best guess\n const isH264 =\n detectedCodec === 'h264' ||\n naluIndices.some((naluIndex) =>\n [NALUType.SLICE_IDR, NALUType.SLICE_NON_IDR].includes(parseNALUType(data[naluIndex])),\n );\n\n if (isH264) {\n for (const index of naluIndices) {\n let type = parseNALUType(data[index]);\n switch (type) {\n case NALUType.SLICE_IDR:\n case NALUType.SLICE_NON_IDR:\n return index + 2;\n default:\n break;\n }\n }\n throw new TypeError('Could not find NALU');\n }\n } catch (e) {\n // no op, we just continue and fallback to vp8\n }\n\n return UNENCRYPTED_BYTES[frame.type];\n } else {\n return UNENCRYPTED_BYTES.audio;\n }\n }\n\n /**\n * inspects frame payloadtype if available and maps it to the codec specified in rtpMap\n */\n getVideoCodec(frame: RTCEncodedVideoFrame): VideoCodec | undefined {\n if (this.rtpMap.size === 0) {\n return undefined;\n }\n // @ts-expect-error payloadType is not yet part of the typescript definition and currently not supported in Safari\n const payloadType = frame.getMetadata().payloadType;\n const codec = payloadType ? this.rtpMap.get(payloadType) : undefined;\n return codec;\n }\n}\n\n/**\n * Slice the NALUs present in the supplied buffer, assuming it is already byte-aligned\n * code adapted from https://github.com/medooze/h264-frame-parser/blob/main/lib/NalUnits.ts to return indices only\n */\nexport function findNALUIndices(stream: Uint8Array): number[] {\n const result: number[] = [];\n let start = 0,\n pos = 0,\n searchLength = stream.length - 2;\n while (pos < searchLength) {\n // skip until end of current NALU\n while (\n pos < searchLength &&\n !(stream[pos] === 0 && stream[pos + 1] === 0 && stream[pos + 2] === 1)\n )\n pos++;\n if (pos >= searchLength) pos = stream.length;\n // remove trailing zeros from current NALU\n let end = pos;\n while (end > start && stream[end - 1] === 0) end--;\n // save current NALU\n if (start === 0) {\n if (end !== start) throw TypeError('byte stream contains leading data');\n } else {\n result.push(start);\n }\n // begin new NALU\n start = pos = pos + 3;\n }\n return result;\n}\n\nexport function parseNALUType(startByte: number): NALUType {\n return startByte & kNaluTypeMask;\n}\n\nconst kNaluTypeMask = 0x1f;\n\nexport enum NALUType {\n /** Coded slice of a non-IDR picture */\n SLICE_NON_IDR = 1,\n /** Coded slice data partition A */\n SLICE_PARTITION_A = 2,\n /** Coded slice data partition B */\n SLICE_PARTITION_B = 3,\n /** Coded slice data partition C */\n SLICE_PARTITION_C = 4,\n /** Coded slice of an IDR picture */\n SLICE_IDR = 5,\n /** Supplemental enhancement information */\n SEI = 6,\n /** Sequence parameter set */\n SPS = 7,\n /** Picture parameter set */\n PPS = 8,\n /** Access unit delimiter */\n AUD = 9,\n /** End of sequence */\n END_SEQ = 10,\n /** End of stream */\n END_STREAM = 11,\n /** Filler data */\n FILLER_DATA = 12,\n /** Sequence parameter set extension */\n SPS_EXT = 13,\n /** Prefix NAL unit */\n PREFIX_NALU = 14,\n /** Subset sequence parameter set */\n SUBSET_SPS = 15,\n /** Depth parameter set */\n DPS = 16,\n\n // 17, 18 reserved\n\n /** Coded slice of an auxiliary coded picture without partitioning */\n SLICE_AUX = 19,\n /** Coded slice extension */\n SLICE_EXT = 20,\n /** Coded slice extension for a depth view component or a 3D-AVC texture view component */\n SLICE_LAYER_EXT = 21,\n\n // 22, 23 reserved\n}\n\n/**\n * we use a magic frame trailer to detect whether a frame is injected\n * by the livekit server and thus to be treated as unencrypted\n * @internal\n */\nexport function isFrameServerInjected(frameData: ArrayBuffer, trailerBytes: Uint8Array): boolean {\n const frameTrailer = new Uint8Array(\n frameData.slice(frameData.byteLength - trailerBytes.byteLength),\n );\n return trailerBytes.every((value, index) => value === frameTrailer[index]);\n}\n","import EventEmitter from 'eventemitter3';\nimport { workerLogger } from '../../logger';\nimport { KEYRING_SIZE } from '../constants';\nimport type { KeyProviderOptions, KeySet, ParticipantKeyHandlerCallbacks } from '../types';\nimport { deriveKeys, importKey, ratchet } from '../utils';\n\n// TODO ParticipantKeyHandlers currently don't get destroyed on participant disconnect\n// we could do this by having a separate worker message on participant disconnected.\n\n/**\n * ParticipantKeyHandler is responsible for providing a cryptor instance with the\n * en-/decryption key of a participant. It assumes that all tracks of a specific participant\n * are encrypted with the same key.\n * Additionally it exposes a method to ratchet a key which can be used by the cryptor either automatically\n * if decryption fails or can be triggered manually on both sender and receiver side.\n *\n */\nexport class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCallbacks> {\n private currentKeyIndex: number;\n\n private cryptoKeyRing: Array<KeySet>;\n\n private enabled: boolean;\n\n private keyProviderOptions: KeyProviderOptions;\n\n private ratchetPromiseMap: Map<number, Promise<CryptoKey>>;\n\n private participantId: string | undefined;\n\n hasValidKey: boolean;\n\n constructor(\n participantId: string | undefined,\n isEnabled: boolean,\n keyProviderOptions: KeyProviderOptions,\n ) {\n super();\n this.currentKeyIndex = 0;\n this.cryptoKeyRing = new Array(KEYRING_SIZE);\n this.enabled = isEnabled;\n this.keyProviderOptions = keyProviderOptions;\n this.ratchetPromiseMap = new Map();\n this.participantId = participantId;\n this.hasValidKey = false;\n }\n\n setEnabled(enabled: boolean) {\n this.enabled = enabled;\n }\n\n /**\n * Ratchets the current key (or the one at keyIndex if provided) and\n * returns the ratcheted material\n * if `setKey` is true (default), it will also set the ratcheted key directly on the crypto key ring\n * @param keyIndex\n * @param setKey\n */\n ratchetKey(keyIndex?: number, setKey = true): Promise<CryptoKey> {\n const currentKeyIndex = (keyIndex ??= this.getCurrentKeyIndex());\n\n const existingPromise = this.ratchetPromiseMap.get(currentKeyIndex);\n if (typeof existingPromise !== 'undefined') {\n return existingPromise;\n }\n const ratchetPromise = new Promise<CryptoKey>(async (resolve, reject) => {\n try {\n const currentMaterial = this.getKeySet(currentKeyIndex).material;\n const newMaterial = await importKey(\n await ratchet(currentMaterial, this.keyProviderOptions.ratchetSalt),\n currentMaterial.algorithm.name,\n 'derive',\n );\n\n if (setKey) {\n this.setKeyFromMaterial(newMaterial, currentKeyIndex, true);\n }\n this.emit('keyRatcheted', newMaterial, keyIndex, this.participantId);\n resolve(newMaterial);\n } catch (e) {\n reject(e);\n } finally {\n this.ratchetPromiseMap.delete(currentKeyIndex);\n }\n });\n this.ratchetPromiseMap.set(currentKeyIndex, ratchetPromise);\n return ratchetPromise;\n }\n\n /**\n * takes in a key material with `deriveBits` and `deriveKey` set as key usages\n * and derives encryption keys from the material and sets it on the key ring buffer\n * together with the material\n * also resets the valid key property and updates the currentKeyIndex\n */\n async setKey(material: CryptoKey, keyIndex = 0) {\n await this.setKeyFromMaterial(material, keyIndex);\n this.hasValidKey = true;\n }\n\n /**\n * takes in a key material with `deriveBits` and `deriveKey` set as key usages\n * and derives encryption keys from the material and sets it on the key ring buffer\n * together with the material\n * also updates the currentKeyIndex\n */\n async setKeyFromMaterial(material: CryptoKey, keyIndex = 0, emitRatchetEvent = false) {\n workerLogger.debug('setting new key');\n if (keyIndex >= 0) {\n this.currentKeyIndex = keyIndex % this.cryptoKeyRing.length;\n }\n const keySet = await deriveKeys(material, this.keyProviderOptions.ratchetSalt);\n this.setKeySet(keySet, this.currentKeyIndex, emitRatchetEvent);\n }\n\n async setKeySet(keySet: KeySet, keyIndex: number, emitRatchetEvent = false) {\n this.cryptoKeyRing[keyIndex % this.cryptoKeyRing.length] = keySet;\n if (emitRatchetEvent) {\n this.emit('keyRatcheted', keySet.material, keyIndex, this.participantId);\n }\n }\n\n async setCurrentKeyIndex(index: number) {\n this.currentKeyIndex = index % this.cryptoKeyRing.length;\n this.hasValidKey = true;\n }\n\n isEnabled() {\n return this.enabled;\n }\n\n getCurrentKeyIndex() {\n return this.currentKeyIndex;\n }\n\n /**\n * returns currently used KeySet or the one at `keyIndex` if provided\n * @param keyIndex\n * @returns\n */\n getKeySet(keyIndex?: number) {\n return this.cryptoKeyRing[keyIndex ?? this.currentKeyIndex];\n }\n}\n","import { workerLogger } from '../../logger';\nimport { KEY_PROVIDER_DEFAULTS } from '../constants';\nimport { CryptorErrorReason } from '../errors';\nimport type {\n E2EEWorkerMessage,\n EnableMessage,\n ErrorMessage,\n KeyProviderOptions,\n RatchetMessage,\n RatchetRequestMessage,\n} from '../types';\nimport { FrameCryptor } from './FrameCryptor';\nimport { ParticipantKeyHandler } from './ParticipantKeyHandler';\n\nconst participantCryptors: FrameCryptor[] = [];\nconst participantKeys: Map<string, ParticipantKeyHandler> = new Map();\n\nlet publishCryptors: FrameCryptor[] = [];\nlet publisherKeys: ParticipantKeyHandler;\n\nlet isEncryptionEnabled: boolean = false;\n\nlet useSharedKey: boolean = false;\n\nlet sharedKey: CryptoKey | undefined;\n\nlet keyProviderOptions: KeyProviderOptions = KEY_PROVIDER_DEFAULTS;\n\nworkerLogger.setDefaultLevel('info');\n\nonmessage = (ev) => {\n const { kind, data }: E2EEWorkerMessage = ev.data;\n\n switch (kind) {\n case 'init':\n workerLogger.info('worker initialized');\n keyProviderOptions = data.keyProviderOptions;\n useSharedKey = !!data.keyProviderOptions.sharedKey;\n // acknowledge init successful\n const enableMsg: EnableMessage = {\n kind: 'enable',\n data: { enabled: isEncryptionEnabled },\n };\n publisherKeys = new ParticipantKeyHandler(undefined, isEncryptionEnabled, keyProviderOptions);\n publisherKeys.on('keyRatcheted', emitRatchetedKeys);\n postMessage(enableMsg);\n break;\n case 'enable':\n setEncryptionEnabled(data.enabled, data.participantId);\n workerLogger.info('updated e2ee enabled status');\n // acknowledge enable call successful\n postMessage(ev.data);\n break;\n case 'decode':\n let cryptor = getTrackCryptor(data.participantId, data.trackId);\n cryptor.setupTransform(\n kind,\n data.readableStream,\n data.writableStream,\n data.trackId,\n data.codec,\n );\n break;\n case 'encode':\n let pubCryptor = getPublisherCryptor(data.trackId);\n pubCryptor.setupTransform(\n kind,\n data.readableStream,\n data.writableStream,\n data.trackId,\n data.codec,\n );\n break;\n case 'setKey':\n if (useSharedKey) {\n workerLogger.debug('set shared key');\n setSharedKey(data.key, data.keyIndex);\n } else if (data.participantId) {\n getParticipantKeyHandler(data.participantId).setKey(data.key, data.keyIndex);\n } else {\n workerLogger.error('no participant Id was provided and shared key usage is disabled');\n }\n break;\n case 'removeTransform':\n unsetCryptorParticipant(data.trackId);\n break;\n case 'updateCodec':\n getTrackCryptor(data.participantId, data.trackId).setVideoCodec(data.codec);\n break;\n case 'setRTPMap':\n publishCryptors.forEach((cr) => {\n cr.setRtpMap(data.map);\n });\n break;\n case 'ratchetRequest':\n handleRatchetRequest(data);\n default:\n break;\n }\n};\n\nasync function handleRatchetRequest(data: RatchetRequestMessage['data']) {\n const keyHandler = getParticipantKeyHandler(data.participantId);\n await keyHandler.ratchetKey(data.keyIndex);\n keyHandler.hasValidKey = true;\n}\n\nfunction getTrackCryptor(participantId: string, trackId: string) {\n let cryptor = participantCryptors.find((c) => c.getTrackId() === trackId);\n if (!cryptor) {\n workerLogger.info('creating new cryptor for', { participantId });\n if (!keyProviderOptions) {\n throw Error('Missing keyProvider options');\n }\n cryptor = new FrameCryptor({\n participantId,\n keys: getParticipantKeyHandler(participantId),\n keyProviderOptions,\n });\n\n setupCryptorErrorEvents(cryptor);\n participantCryptors.push(cryptor);\n } else if (participantId !== cryptor.getParticipantId()) {\n // assign new participant id to track cryptor and pass in correct key handler\n cryptor.setParticipant(participantId, getParticipantKeyHandler(participantId));\n }\n if (sharedKey) {\n }\n return cryptor;\n}\n\nfunction getParticipantKeyHandler(participantId?: string) {\n if (!participantId) {\n return publisherKeys!;\n }\n let keys = participantKeys.get(participantId);\n if (!keys) {\n keys = new ParticipantKeyHandler(participantId, true, keyProviderOptions);\n if (sharedKey) {\n keys.setKey(sharedKey);\n }\n participantKeys.set(participantId, keys);\n }\n return keys;\n}\n\nfunction unsetCryptorParticipant(trackId: string) {\n participantCryptors.find((c) => c.getTrackId() === trackId)?.unsetParticipant();\n}\n\nfunction getPublisherCryptor(trackId: string) {\n let publishCryptor = publishCryptors.find((cryptor) => cryptor.getTrackId() === trackId);\n if (!publishCryptor) {\n if (!keyProviderOptions) {\n throw new TypeError('Missing keyProvider options');\n }\n publishCryptor = new FrameCryptor({\n keys: publisherKeys!,\n participantId: 'publisher',\n keyProviderOptions,\n });\n setupCryptorErrorEvents(publishCryptor);\n publishCryptors.push(publishCryptor);\n }\n return publishCryptor;\n}\n\nfunction setEncryptionEnabled(enable: boolean, participantId?: string) {\n if (!participantId) {\n isEncryptionEnabled = enable;\n publisherKeys.setEnabled(enable);\n } else {\n getParticipantKeyHandler(participantId).setEnabled(enable);\n }\n}\n\nfunction setSharedKey(key: CryptoKey, index?: number) {\n workerLogger.debug('setting shared key');\n sharedKey = key;\n publisherKeys?.setKey(key, index);\n for (const [, keyHandler] of participantKeys) {\n keyHandler.setKey(key, index);\n }\n}\n\nfunction setupCryptorErrorEvents(cryptor: FrameCryptor) {\n cryptor.on('cryptorError', (error) => {\n const msg: ErrorMessage = {\n kind: 'error',\n data: { error: new Error(`${CryptorErrorReason[error.reason]}: ${error.message}`) },\n };\n postMessage(msg);\n });\n}\n\nfunction emitRatchetedKeys(material: CryptoKey, keyIndex?: number) {\n const msg: RatchetMessage = {\n kind: `ratchetKey`,\n data: {\n // participantId,\n keyIndex,\n material,\n },\n };\n postMessage(msg);\n}\n\n// Operations using RTCRtpScriptTransform.\n// @ts-ignore\nif (self.RTCTransformEvent) {\n workerLogger.debug('setup transform event');\n // @ts-ignore\n self.onrtctransform = (event) => {\n const transformer = event.transformer;\n workerLogger.debug('transformer', transformer);\n transformer.handled = true;\n const { kind, participantId, trackId, codec } = transformer.options;\n const cryptor =\n kind === 'encode' ? getPublisherCryptor(trackId) : getTrackCryptor(participantId, trackId);\n workerLogger.debug('transform', { codec });\n cryptor.setupTransform(kind, transformer.readable, transformer.writable, trackId, codec);\n };\n}\n"],"names":["root","definition","this","noop","undefinedType","isIE","window","navigator","test","userAgent","logMethods","bindMethod","obj","methodName","method","bind","Function","prototype","call","e","apply","arguments","traceForIE","console","log","trace","replaceLoggingMethods","level","loggerName","i","length","methodFactory","debug","enableLoggingWhenConsoleArrives","defaultMethodFactory","undefined","realMethod","Logger","name","defaultLevel","factory","currentLevel","self","storageKey","getPersistedLevel","storedLevel","localStorage","ignore","cookie","document","location","indexOf","encodeURIComponent","exec","slice","levels","TRACE","DEBUG","INFO","WARN","ERROR","SILENT","getLevel","setLevel","persist","toUpperCase","levelNum","levelName","persistLevelIfPossible","setDefaultLevel","resetLevel","removeItem","clearPersistedLevel","enableAll","disableAll","initialLevel","defaultLogger","_loggersByName","getLogger","TypeError","logger","_log","noConflict","getLoggers","exports","module","LogLevel","info","workerLogger","ENCRYPTION_ALGORITHM","UNENCRYPTED_BYTES","key","delta","audio","empty","KEY_PROVIDER_DEFAULTS","sharedKey","ratchetSalt","ratchetWindowSize","LivekitError","Error","constructor","code","message","super","MediaDeviceFailure","CryptorErrorReason","getFailure","error","NotFound","PermissionDenied","DeviceInUse","Other","CryptorError","reason","InternalError","has","Object","hasOwnProperty","prefix","Events","EE","fn","context","once","addListener","emitter","event","listener","evt","_events","push","_eventsCount","clearEvent","EventEmitter","create","__proto__","eventNames","events","names","getOwnPropertySymbols","concat","listeners","handlers","l","ee","Array","listenerCount","emit","a1","a2","a3","a4","a5","args","len","removeListener","j","on","removeAllListeners","off","prefixed","CryptorEvent","AudioPresets","getAlgoOptions","algorithmName","salt","encodedSalt","TextEncoder","encode","hash","ArrayBuffer","iterations","deriveKeys","material","algorithmOptions","algorithm","encryptionKey","crypto","subtle","deriveKey","telephone","maxBitrate","speech","music","musicStereo","musicHighQuality","musicHighQualityStereo","BaseFrameCryptor","encodeFunction","encodedFrame","controller","decodeFunction","FrameCryptor","opts","sendCounts","Map","keys","participantId","rtpMap","keyProviderOptions","unencryptedFrameByteTrailer","_a","unencryptedFrameBytes","setParticipant","id","unsetParticipant","getParticipantId","getTrackId","trackId","setVideoCodec","codec","videoCodec","setRtpMap","map","setupTransform","operation","readable","writable","transformFn","transformStream","TransformStream","transform","pipeThrough","pipeTo","catch","isEnabled","data","byteLength","enqueue","getKeySet","keyIndex","getCurrentKeyIndex","iv","makeIV","getMetadata","synchronizationSource","timestamp","frameHeader","Uint8Array","getUnencryptedBytes","frameTrailer","cipherText","encrypt","additionalData","newData","newUint8","set","MissingKey","frameData","trailerBytes","every","value","index","isFrameServerInjected","hasValidKey","decodedFrame","decryptFrame","InvalidKey","warn","initialMaterial","ratchetOpts","ratchetCount","keySet","ivLength","cipherTextStart","cipherTextLength","plainText","decrypt","ratchetedKeySet","RTCEncodedAudioFrame","newMaterial","ratchetKey","frame","setKeySet","setCurrentKeyIndex","setKeyFromMaterial","ivView","DataView","Math","floor","random","sendCount","get","setUint32","isVideoFrame","detectedCodec","getVideoCodec","type","naluIndices","stream","result","start","pos","searchLength","end","findNALUIndices","some","naluIndex","NALUType","SLICE_IDR","SLICE_NON_IDR","includes","parseNALUType","size","payloadType","startByte","kNaluTypeMask","ParticipantKeyHandler","currentKeyIndex","cryptoKeyRing","enabled","ratchetPromiseMap","setEnabled","setKey","existingPromise","ratchetPromise","Promise","resolve","reject","__awaiter","currentMaterial","keyBytes","usage","importKey","deriveBits","ratchet","delete","emitRatchetEvent","participantCryptors","participantKeys","publisherKeys","publishCryptors","isEncryptionEnabled","useSharedKey","getTrackCryptor","cryptor","find","c","getParticipantKeyHandler","setupCryptorErrorEvents","getPublisherCryptor","publishCryptor","msg","kind","postMessage","emitRatchetedKeys","onmessage","ev","enableMsg","enable","readableStream","writableStream","keyHandler","setSharedKey","forEach","cr","handleRatchetRequest","RTCTransformEvent","onrtctransform","transformer","handled","options"],"mappings":"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","x_google_ignoreList":[0,5]}
|