livekit-client 1.11.4 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +1 -3
- package/dist/livekit-client.e2ee.worker.js +2 -0
- package/dist/livekit-client.e2ee.worker.js.map +1 -0
- package/dist/livekit-client.e2ee.worker.mjs +1525 -0
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -0
- package/dist/livekit-client.esm.mjs +4749 -4055
- 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/api/SignalClient.d.ts +4 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +45 -0
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -0
- package/dist/src/e2ee/KeyProvider.d.ts +42 -0
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -0
- package/dist/src/e2ee/constants.d.ts +14 -0
- package/dist/src/e2ee/constants.d.ts.map +1 -0
- package/dist/src/e2ee/errors.d.ts +11 -0
- package/dist/src/e2ee/errors.d.ts.map +1 -0
- package/dist/src/e2ee/index.d.ts +4 -0
- package/dist/src/e2ee/index.d.ts.map +1 -0
- package/dist/src/e2ee/types.d.ts +129 -0
- package/dist/src/e2ee/types.d.ts.map +1 -0
- package/dist/src/e2ee/utils.d.ts +24 -0
- package/dist/src/e2ee/utils.d.ts.map +1 -0
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +175 -0
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -0
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +46 -0
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -0
- package/dist/src/e2ee/worker/e2ee.worker.d.ts +2 -0
- package/dist/src/e2ee/worker/e2ee.worker.d.ts.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +4 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/options.d.ts +5 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +2 -2
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +3 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +17 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +10 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +14 -2
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +7 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -0
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +3 -0
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +2 -2
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts +9 -0
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +2 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/MockMediaStreamTrack.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +4 -1
- package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +45 -0
- package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +42 -0
- package/dist/ts4.2/src/e2ee/constants.d.ts +14 -0
- package/dist/ts4.2/src/e2ee/errors.d.ts +11 -0
- package/dist/ts4.2/src/e2ee/index.d.ts +4 -0
- package/dist/ts4.2/src/e2ee/types.d.ts +129 -0
- package/dist/ts4.2/src/e2ee/utils.d.ts +24 -0
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +175 -0
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +46 -0
- package/dist/ts4.2/src/e2ee/worker/e2ee.worker.d.ts +2 -0
- package/dist/ts4.2/src/index.d.ts +1 -0
- package/dist/ts4.2/src/logger.d.ts +4 -1
- package/dist/ts4.2/src/options.d.ts +5 -0
- package/dist/ts4.2/src/proto/livekit_models.d.ts +2 -2
- package/dist/ts4.2/src/room/PCTransport.d.ts +3 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +17 -3
- package/dist/ts4.2/src/room/Room.d.ts +10 -0
- package/dist/ts4.2/src/room/events.d.ts +14 -2
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +7 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -0
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +3 -0
- package/dist/ts4.2/src/room/track/options.d.ts +6 -6
- package/dist/ts4.2/src/room/track/utils.d.ts +9 -0
- package/dist/ts4.2/src/room/utils.d.ts +2 -0
- package/package.json +17 -7
- package/src/api/SignalClient.ts +28 -9
- package/src/connectionHelper/checks/turn.ts +1 -0
- package/src/connectionHelper/checks/websocket.ts +1 -0
- package/src/e2ee/E2eeManager.ts +374 -0
- package/src/e2ee/KeyProvider.ts +77 -0
- package/src/e2ee/constants.ts +40 -0
- package/src/e2ee/errors.ts +16 -0
- package/src/e2ee/index.ts +3 -0
- package/src/e2ee/types.ts +160 -0
- package/src/e2ee/utils.ts +127 -0
- package/src/e2ee/worker/FrameCryptor.test.ts +21 -0
- package/src/e2ee/worker/FrameCryptor.ts +614 -0
- package/src/e2ee/worker/ParticipantKeyHandler.ts +129 -0
- package/src/e2ee/worker/e2ee.worker.ts +217 -0
- package/src/e2ee/worker/tsconfig.json +6 -0
- package/src/index.ts +1 -0
- package/src/logger.ts +10 -2
- package/src/options.ts +6 -0
- package/src/proto/livekit_models.ts +12 -12
- package/src/room/PCTransport.ts +39 -9
- package/src/room/RTCEngine.ts +127 -34
- package/src/room/Room.ts +77 -22
- package/src/room/defaults.ts +1 -1
- package/src/room/events.ts +14 -0
- package/src/room/participant/LocalParticipant.ts +52 -8
- package/src/room/participant/Participant.ts +4 -0
- package/src/room/track/LocalTrack.ts +5 -4
- package/src/room/track/RemoteVideoTrack.ts +2 -2
- package/src/room/track/TrackPublication.ts +9 -1
- package/src/room/track/options.ts +3 -2
- package/src/room/track/utils.ts +27 -0
- package/src/room/utils.ts +5 -0
- package/src/room/worker.d.ts +4 -0
- package/src/test/MockMediaStreamTrack.ts +1 -0
package/README.md
CHANGED
@@ -297,12 +297,10 @@ If you are targeting legacy browsers, but still want adaptiveStream functionalit
|
|
297
297
|
Also when targeting legacy browsers, older than the ones specified in our browserslist target, make sure to transpile the library code to your desired target and include required polyfills with babel and/or corejs.
|
298
298
|
|
299
299
|
<!--BEGIN_REPO_NAV-->
|
300
|
-
|
301
300
|
<br/><table>
|
302
|
-
|
303
301
|
<thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
|
304
302
|
<tbody>
|
305
|
-
<tr><td>Client SDKs</td><td><a href="https://github.com/livekit/components-js">Components</a> · <b>JavaScript</b> · <a href="https://github.com/livekit/client-sdk-rust">Rust</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (web)</a> · <a href="https://github.com/livekit/client-sdk-react-native">React Native (beta)</a></td></tr><tr></tr>
|
303
|
+
<tr><td>Client SDKs</td><td><a href="https://github.com/livekit/components-js">Components</a> · <b>JavaScript</b> · <a href="https://github.com/livekit/client-sdk-rust">Rust</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (web)</a> · <a href="https://github.com/livekit/client-sdk-python">Python</a> · <a href="https://github.com/livekit/client-sdk-react-native">React Native (beta)</a></td></tr><tr></tr>
|
306
304
|
<tr><td>Server SDKs</td><td><a href="https://github.com/livekit/server-sdk-js">Node.js</a> · <a href="https://github.com/livekit/server-sdk-go">Golang</a> · <a href="https://github.com/livekit/server-sdk-ruby">Ruby</a> · <a href="https://github.com/livekit/server-sdk-kotlin">Java/Kotlin</a> · <a href="https://github.com/agence104/livekit-server-sdk-php">PHP (community)</a> · <a href="https://github.com/tradablebits/livekit-server-sdk-python">Python (community)</a></td></tr><tr></tr>
|
307
305
|
<tr><td>Services</td><td><a href="https://github.com/livekit/livekit">Livekit server</a> · <a href="https://github.com/livekit/egress">Egress</a> · <a href="https://github.com/livekit/ingress">Ingress</a></td></tr><tr></tr>
|
308
306
|
<tr><td>Resources</td><td><a href="https://docs.livekit.io">Docs</a> · <a href="https://github.com/livekit-examples">Example apps</a> · <a href="https://livekit.io/cloud">Cloud</a> · <a href="https://docs.livekit.io/oss/deployment">Self-hosting</a> · <a href="https://github.com/livekit/livekit-cli">CLI</a></td></tr>
|
@@ -0,0 +1,2 @@
|
|
1
|
+
!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var n,r,i,o={exports:{}};r=e,i=function(){var e=function(){},t="undefined",n=typeof window!==t&&typeof window.navigator!==t&&/Trident\/|MSIE /.test(window.navigator.userAgent),r=["trace","debug","info","warn","error"];function i(e,t){var n=e[t];if("function"==typeof n.bind)return n.bind(e);try{return Function.prototype.bind.call(n,e)}catch(t){return function(){return Function.prototype.apply.apply(n,[e,arguments])}}}function o(){console.log&&(console.log.apply?console.log.apply(console,arguments):Function.prototype.apply.apply(console.log,[console,arguments])),console.trace&&console.trace()}function a(t,n){for(var i=0;i<r.length;i++){var o=r[i];this[o]=i<t?e:this.methodFactory(o,t,n)}this.log=this.debug}function s(e,n,r){return function(){typeof console!==t&&(a.call(this,n,r),this[e].apply(this,arguments))}}function c(r,a,c){return function(r){return"debug"===r&&(r="log"),typeof console!==t&&("trace"===r&&n?o:void 0!==console[r]?i(console,r):void 0!==console.log?i(console,"log"):e)}(r)||s.apply(this,arguments)}function d(e,n,i){var o,s=this;n=null==n?"WARN":n;var d="loglevel";function l(){var e;if(typeof window!==t&&d){try{e=window.localStorage[d]}catch(e){}if(typeof e===t)try{var n=window.document.cookie,r=n.indexOf(encodeURIComponent(d)+"=");-1!==r&&(e=/^([^;]+)/.exec(n.slice(r))[1])}catch(e){}return void 0===s.levels[e]&&(e=void 0),e}}"string"==typeof e?d+=":"+e:"symbol"==typeof e&&(d=void 0),s.name=e,s.levels={TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4,SILENT:5},s.methodFactory=i||c,s.getLevel=function(){return o},s.setLevel=function(n,i){if("string"==typeof n&&void 0!==s.levels[n.toUpperCase()]&&(n=s.levels[n.toUpperCase()]),!("number"==typeof n&&n>=0&&n<=s.levels.SILENT))throw"log.setLevel() called with invalid level: "+n;if(o=n,!1!==i&&function(e){var n=(r[e]||"silent").toUpperCase();if(typeof window!==t&&d){try{return void(window.localStorage[d]=n)}catch(e){}try{window.document.cookie=encodeURIComponent(d)+"="+n+";"}catch(e){}}}(n),a.call(s,n,e),typeof console===t&&n<s.levels.SILENT)return"No console available for logging"},s.setDefaultLevel=function(e){n=e,l()||s.setLevel(e,!1)},s.resetLevel=function(){s.setLevel(n,!1),function(){if(typeof window!==t&&d){try{return void window.localStorage.removeItem(d)}catch(e){}try{window.document.cookie=encodeURIComponent(d)+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC"}catch(e){}}}()},s.enableAll=function(e){s.setLevel(s.levels.TRACE,e)},s.disableAll=function(e){s.setLevel(s.levels.SILENT,e)};var u=l();null==u&&(u=n),s.setLevel(u,!1)}var l=new d,u={};l.getLogger=function(e){if("symbol"!=typeof e&&"string"!=typeof e||""===e)throw new TypeError("You must supply a name when creating a logger.");var t=u[e];return t||(t=u[e]=new d(e,l.getLevel(),l.methodFactory)),t};var y=typeof window!==t?window.log:void 0;return l.noConflict=function(){return typeof window!==t&&window.log===l&&(window.log=y),l},l.getLoggers=function(){return u},l.default=l,l},(n=o).exports?n.exports=i():r.log=i();var a,s=o.exports;!function(e){e[e.trace=0]="trace",e[e.debug=1]="debug",e[e.info=2]="info",e[e.warn=3]="warn",e[e.error=4]="error",e[e.silent=5]="silent"}(a||(a={}));s.getLogger("livekit").setDefaultLevel(a.info);const c=s.getLogger("lk-e2ee"),d="AES-GCM",l={key:10,delta:3,audio:1,empty:0},u={sharedKey:!1,ratchetSalt:"LKFrameEncryptionKey",ratchetWindowSize:8};class y extends Error{constructor(e,t){super(t||"an error has occured"),this.code=e}}var h,p;!function(e){e.PermissionDenied="PermissionDenied",e.NotFound="NotFound",e.DeviceInUse="DeviceInUse",e.Other="Other"}(h||(h={})),function(e){e.getFailure=function(t){if(t&&"name"in t)return"NotFoundError"===t.name||"DevicesNotFoundError"===t.name?e.NotFound:"NotAllowedError"===t.name||"PermissionDeniedError"===t.name?e.PermissionDenied:"NotReadableError"===t.name||"TrackStartError"===t.name?e.DeviceInUse:e.Other}}(h||(h={})),function(e){e[e.InvalidKey=0]="InvalidKey",e[e.MissingKey=1]="MissingKey",e[e.InternalError=2]="InternalError"}(p||(p={}));class f extends y{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p.InternalError;super(40,e),this.reason=t}}function v(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{c(r.next(e))}catch(e){o(e)}}function s(e){try{c(r.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}c((r=r.apply(e,t||[])).next())}))}var g={exports:{}};!function(e){var t=Object.prototype.hasOwnProperty,n="~";function r(){}function i(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function o(e,t,r,o,a){if("function"!=typeof r)throw new TypeError("The listener must be a function");var s=new i(r,o||e,a),c=n?n+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],s]:e._events[c].push(s):(e._events[c]=s,e._eventsCount++),e}function a(e,t){0==--e._eventsCount?e._events=new r:delete e._events[t]}function s(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=Object.create(null),(new r).__proto__||(n=!1)),s.prototype.eventNames=function(){var e,r,i=[];if(0===this._eventsCount)return i;for(r in e=this._events)t.call(e,r)&&i.push(n?r.slice(1):r);return Object.getOwnPropertySymbols?i.concat(Object.getOwnPropertySymbols(e)):i},s.prototype.listeners=function(e){var t=n?n+e:e,r=this._events[t];if(!r)return[];if(r.fn)return[r.fn];for(var i=0,o=r.length,a=new Array(o);i<o;i++)a[i]=r[i].fn;return a},s.prototype.listenerCount=function(e){var t=n?n+e:e,r=this._events[t];return r?r.fn?1:r.length:0},s.prototype.emit=function(e,t,r,i,o,a){var s=n?n+e:e;if(!this._events[s])return!1;var c,d,l=this._events[s],u=arguments.length;if(l.fn){switch(l.once&&this.removeListener(e,l.fn,void 0,!0),u){case 1:return l.fn.call(l.context),!0;case 2:return l.fn.call(l.context,t),!0;case 3:return l.fn.call(l.context,t,r),!0;case 4:return l.fn.call(l.context,t,r,i),!0;case 5:return l.fn.call(l.context,t,r,i,o),!0;case 6:return l.fn.call(l.context,t,r,i,o,a),!0}for(d=1,c=new Array(u-1);d<u;d++)c[d-1]=arguments[d];l.fn.apply(l.context,c)}else{var y,h=l.length;for(d=0;d<h;d++)switch(l[d].once&&this.removeListener(e,l[d].fn,void 0,!0),u){case 1:l[d].fn.call(l[d].context);break;case 2:l[d].fn.call(l[d].context,t);break;case 3:l[d].fn.call(l[d].context,t,r);break;case 4:l[d].fn.call(l[d].context,t,r,i);break;default:if(!c)for(y=1,c=new Array(u-1);y<u;y++)c[y-1]=arguments[y];l[d].fn.apply(l[d].context,c)}}return!0},s.prototype.on=function(e,t,n){return o(this,e,t,n,!1)},s.prototype.once=function(e,t,n){return o(this,e,t,n,!0)},s.prototype.removeListener=function(e,t,r,i){var o=n?n+e:e;if(!this._events[o])return this;if(!t)return a(this,o),this;var s=this._events[o];if(s.fn)s.fn!==t||i&&!s.once||r&&s.context!==r||a(this,o);else{for(var c=0,d=[],l=s.length;c<l;c++)(s[c].fn!==t||i&&!s[c].once||r&&s[c].context!==r)&&d.push(s[c]);d.length?this._events[o]=1===d.length?d[0]:d:a(this,o)}return this},s.prototype.removeAllListeners=function(e){var t;return e?(t=n?n+e:e,this._events[t]&&a(this,t)):(this._events=new r,this._eventsCount=0),this},s.prototype.off=s.prototype.removeListener,s.prototype.addListener=s.prototype.on,s.prefixed=n,s.EventEmitter=s,e.exports=s}(g);var w=t(g.exports);const m="cryptorError";var I;function b(e,t){const n=(new TextEncoder).encode(t);switch(e){case"HKDF":return{name:"HKDF",salt:n,hash:"SHA-256",info:new ArrayBuffer(128)};case"PBKDF2":return{name:"PBKDF2",salt:n,hash:"SHA-256",iterations:1e5};default:throw new Error("algorithm ".concat(e," is currently unsupported"))}}function k(e,t){return v(this,void 0,void 0,(function*(){const n=b(e.algorithm.name,t),r=yield crypto.subtle.deriveKey(n,e,{name:d,length:128},!1,["encrypt","decrypt"]);return{material:e,encryptionKey:r}}))}!function(e){e.telephone={maxBitrate:12e3},e.speech={maxBitrate:2e4},e.music={maxBitrate:32e3},e.musicStereo={maxBitrate:48e3},e.musicHighQuality={maxBitrate:64e3},e.musicHighQualityStereo={maxBitrate:96e3}}(I||(I={}));class E extends w{encodeFunction(e,t){throw Error("not implemented for subclass")}decodeFunction(e,t){throw Error("not implemented for subclass")}}class L extends E{constructor(e){var t;super(),this.isKeyInvalid=!1,this.sendCounts=new Map,this.keys=e.keys,this.participantId=e.participantId,this.rtpMap=new Map,this.keyProviderOptions=e.keyProviderOptions,this.unencryptedFrameByteTrailer=null!==(t=e.unencryptedFrameBytes)&&void 0!==t?t:(new TextEncoder).encode("LKROCKS")}setParticipant(e,t){this.participantId=e,this.keys=t}unsetParticipant(){this.participantId=void 0}getParticipantId(){return this.participantId}getTrackId(){return this.trackId}setVideoCodec(e){this.videoCodec=e}setRtpMap(e){this.rtpMap=e}setupTransform(e,t,n,r,i){i&&(console.info("setting codec on cryptor to",i),this.videoCodec=i);const o="encode"===e?this.encodeFunction:this.decodeFunction,a=new TransformStream({transform:o.bind(this)});t.pipeThrough(a).pipeTo(n).catch((e=>{console.error(e),this.emit("cryptorError",e instanceof f?e:new f(e.message))})),this.trackId=r}encodeFunction(e,t){var n;return v(this,void 0,void 0,(function*(){if(!this.keys.isEnabled()||0===e.data.byteLength)return t.enqueue(e);const{encryptionKey:r}=this.keys.getKeySet(),i=this.keys.getCurrentKeyIndex();if(r){const o=this.makeIV(null!==(n=e.getMetadata().synchronizationSource)&&void 0!==n?n:-1,e.timestamp),a=new Uint8Array(e.data,0,this.getUnencryptedBytes(e)),s=new Uint8Array(2);s[0]=12,s[1]=i;try{const n=yield crypto.subtle.encrypt({name:d,iv:o,additionalData:new Uint8Array(e.data,0,a.byteLength)},r,new Uint8Array(e.data,this.getUnencryptedBytes(e))),i=new ArrayBuffer(a.byteLength+n.byteLength+o.byteLength+s.byteLength),c=new Uint8Array(i);return c.set(a),c.set(new Uint8Array(n),a.byteLength),c.set(new Uint8Array(o),a.byteLength+n.byteLength),c.set(s,a.byteLength+n.byteLength+o.byteLength),e.data=i,t.enqueue(e)}catch(e){c.error(e)}}else this.emit(m,new f("encryption key missing for encoding",p.MissingKey))}))}decodeFunction(e,t){return v(this,void 0,void 0,(function*(){if(!this.keys.isEnabled()||0===e.data.byteLength||function(e,t){const n=new Uint8Array(e.slice(e.byteLength-t.byteLength));return t.every(((e,t)=>e===n[t]))}(e.data,this.unencryptedFrameByteTrailer))return t.enqueue(e);const n=new Uint8Array(e.data)[e.data.byteLength-1];if(this.keys.getKeySet(n))try{const r=yield this.decryptFrame(e,n);if(r)return t.enqueue(r);this.isKeyInvalid=!1}catch(e){e instanceof f&&e.reason===p.InvalidKey?this.isKeyInvalid||(c.warn("invalid key"),this.emit(m,new f("invalid key for participant ".concat(this.participantId),p.InvalidKey)),this.isKeyInvalid=!0):c.warn("decoding frame failed",{error:e})}else this.emit(m,new f("key missing for participant ".concat(this.participantId),p.MissingKey));return t.enqueue(e)}))}decryptFrame(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{ratchetCount:0};var i;return v(this,void 0,void 0,(function*(){const o=this.keys.getKeySet(t);try{const t=new Uint8Array(e.data,0,this.getUnencryptedBytes(e)),n=new Uint8Array(e.data,e.data.byteLength-2,2),a=n[0],s=new Uint8Array(e.data,e.data.byteLength-a-n.byteLength,a),c=t.byteLength,l=e.data.byteLength-(t.byteLength+a+n.byteLength),u=yield crypto.subtle.decrypt({name:d,iv:s,additionalData:new Uint8Array(e.data,0,t.byteLength)},null!==(i=r.encryptionKey)&&void 0!==i?i:o.encryptionKey,new Uint8Array(e.data,c,l)),y=new ArrayBuffer(t.byteLength+u.byteLength),h=new Uint8Array(y);return h.set(new Uint8Array(e.data,0,t.byteLength)),h.set(new Uint8Array(u),t.byteLength),e.data=y,e}catch(i){if(!(this.keyProviderOptions.ratchetWindowSize>0))throw new f("Decryption failed, most likely because of an invalid key",p.InvalidKey);if(r.ratchetCount<this.keyProviderOptions.ratchetWindowSize){let i;if(c.debug("ratcheting key attempt ".concat(r.ratchetCount," of ").concat(this.keyProviderOptions.ratchetWindowSize,", for kind ").concat(e instanceof RTCEncodedAudioFrame?"audio":"video")),o===this.keys.getKeySet(t)){const e=yield this.keys.ratchetKey(t,!1);i=yield k(e,this.keyProviderOptions.ratchetSalt)}const a=yield this.decryptFrame(e,t,n||o,{ratchetCount:r.ratchetCount+1,encryptionKey:null==i?void 0:i.encryptionKey});return a&&i&&(this.keys.setKeySet(i,t,!0),this.keys.setCurrentKeyIndex(t)),a}n&&(c.debug("resetting to initial material"),this.keys.setKeyFromMaterial(n.material,t)),c.warn("maximum ratchet attempts exceeded, resetting key")}}))}makeIV(e,t){var n;const r=new ArrayBuffer(12),i=new DataView(r);this.sendCounts.has(e)||this.sendCounts.set(e,Math.floor(65535*Math.random()));const o=null!==(n=this.sendCounts.get(e))&&void 0!==n?n:0;return i.setUint32(0,e),i.setUint32(4,t),i.setUint32(8,t-o%65535),this.sendCounts.set(e,o+1),r}getUnencryptedBytes(e){var t;if(function(e){return"type"in e}(e)){let n=null!==(t=this.getVideoCodec(e))&&void 0!==t?t:this.videoCodec;if("av1"===n||"vp9"===n)throw new Error("".concat(n," is not yet supported for end to end encryption"));if("vp8"===n)return l[e.type];const r=new Uint8Array(e.data);try{const e=function(e){const t=[];let n=0,r=0,i=e.length-2;for(;r<i;){for(;r<i&&(0!==e[r]||0!==e[r+1]||1!==e[r+2]);)r++;r>=i&&(r=e.length);let o=r;for(;o>n&&0===e[o-1];)o--;if(0===n){if(o!==n)throw TypeError("byte stream contains leading data")}else t.push(n);n=r+=3}return t}(r);if("h264"===n||e.some((e=>[K.SLICE_IDR,K.SLICE_NON_IDR].includes(S(r[e]))))){for(const t of e){switch(S(r[t])){case K.SLICE_IDR:case K.SLICE_NON_IDR:return t+2}}throw new TypeError("Could not find NALU")}}catch(e){}return l[e.type]}return l.audio}getVideoCodec(e){if(0===this.rtpMap.size)return;const t=e.getMetadata().payloadType;return t?this.rtpMap.get(t):void 0}}function S(e){return e&_}const _=31;var K;!function(e){e[e.SLICE_NON_IDR=1]="SLICE_NON_IDR",e[e.SLICE_PARTITION_A=2]="SLICE_PARTITION_A",e[e.SLICE_PARTITION_B=3]="SLICE_PARTITION_B",e[e.SLICE_PARTITION_C=4]="SLICE_PARTITION_C",e[e.SLICE_IDR=5]="SLICE_IDR",e[e.SEI=6]="SEI",e[e.SPS=7]="SPS",e[e.PPS=8]="PPS",e[e.AUD=9]="AUD",e[e.END_SEQ=10]="END_SEQ",e[e.END_STREAM=11]="END_STREAM",e[e.FILLER_DATA=12]="FILLER_DATA",e[e.SPS_EXT=13]="SPS_EXT",e[e.PREFIX_NALU=14]="PREFIX_NALU",e[e.SUBSET_SPS=15]="SUBSET_SPS",e[e.DPS=16]="DPS",e[e.SLICE_AUX=19]="SLICE_AUX",e[e.SLICE_EXT=20]="SLICE_EXT",e[e.SLICE_LAYER_EXT=21]="SLICE_LAYER_EXT"}(K||(K={}));class C extends w{constructor(e,t,n){super(),this.currentKeyIndex=0,this.cryptoKeyRing=new Array(16),this.enabled=t,this.keyProviderOptions=n,this.ratchetPromiseMap=new Map,this.participantId=e}setEnabled(e){this.enabled=e}ratchetKey(e){let t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const n=null!=e?e:e=this.getCurrentKeyIndex(),r=this.ratchetPromiseMap.get(n);if(void 0!==r)return r;const i=new Promise(((r,i)=>v(this,void 0,void 0,(function*(){try{const i=this.getKeySet(n).material,o=yield function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{name:d},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"encrypt";return v(this,void 0,void 0,(function*(){return crypto.subtle.importKey("raw",e,t,!1,"derive"===n?["deriveBits","deriveKey"]:["encrypt","decrypt"])}))}(yield function(e,t){return v(this,void 0,void 0,(function*(){const n=b(e.algorithm.name,t);return crypto.subtle.deriveBits(n,e,256)}))}(i,this.keyProviderOptions.ratchetSalt),i.algorithm.name,"derive");t&&this.setKeyFromMaterial(o,n,!0),this.emit("keyRatcheted",o,e,this.participantId),r(o)}catch(e){i(e)}finally{this.ratchetPromiseMap.delete(n)}}))));return this.ratchetPromiseMap.set(n,i),i}setKeyFromMaterial(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return v(this,void 0,void 0,(function*(){c.debug("setting new key"),t>=0&&(this.currentKeyIndex=t%this.cryptoKeyRing.length);const r=yield k(e,this.keyProviderOptions.ratchetSalt);this.setKeySet(r,this.currentKeyIndex,n)}))}setKeySet(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return v(this,void 0,void 0,(function*(){this.cryptoKeyRing[t%this.cryptoKeyRing.length]=e,n&&this.emit("keyRatcheted",e.material,t,this.participantId)}))}setCurrentKeyIndex(e){return v(this,void 0,void 0,(function*(){this.currentKeyIndex=e%this.cryptoKeyRing.length}))}isEnabled(){return this.enabled}getCurrentKeyIndex(){return this.currentKeyIndex}getKeySet(e){return this.cryptoKeyRing[null!=e?e:this.currentKeyIndex]}}const T=[],A=new Map;let x,P,R=[],U=!1,F=!1,M=u;function O(e,t){let n=T.find((e=>e.getTrackId()===t));if(n)e!==n.getParticipantId()&&n.setParticipant(e,D(e));else{if(c.info("creating new cryptor for",{participantId:e}),!M)throw Error("Missing keyProvider options");n=new L({participantId:e,keys:D(e),keyProviderOptions:M}),B(n),T.push(n)}return n}function D(e){if(!e)return x;let t=A.get(e);return t||(t=new C(e,!0,M),P&&t.setKeyFromMaterial(P),A.set(e,t)),t}function N(e){let t=R.find((t=>t.getTrackId()===e));if(!t){if(!M)throw new TypeError("Missing keyProvider options");t=new L({keys:x,participantId:"publisher",keyProviderOptions:M}),B(t),R.push(t)}return t}function B(e){e.on("cryptorError",(e=>{const t={kind:"error",data:{error:new Error("".concat(p[e.reason],": ").concat(e.message))}};postMessage(t)}))}function X(e,t){postMessage({kind:"ratchetKey",data:{keyIndex:t,material:e}})}c.setDefaultLevel("info"),onmessage=e=>{const{kind:t,data:n}=e.data;switch(t){case"init":c.info("worker initialized"),M=n.keyProviderOptions,F=!!n.keyProviderOptions.sharedKey;const s={kind:"enable",data:{enabled:U}};x=new C(void 0,U,M),x.on("keyRatcheted",X),postMessage(s);break;case"enable":o=n.enabled,(a=n.participantId)?D(a).setEnabled(o):(U=o,x.setEnabled(o)),c.info("updated e2ee enabled status"),postMessage(e.data);break;case"decode":O(n.participantId,n.trackId).setupTransform(t,n.readableStream,n.writableStream,n.trackId,n.codec);break;case"encode":N(n.trackId).setupTransform(t,n.readableStream,n.writableStream,n.trackId,n.codec);break;case"setKey":F?(c.debug("set shared key"),function(e,t){c.debug("setting shared key"),P=e,null==x||x.setKeyFromMaterial(e,t);for(const[,n]of A)n.setKeyFromMaterial(e,t)}(n.key,n.keyIndex)):n.participantId?D(n.participantId).setKeyFromMaterial(n.key,n.keyIndex):c.error("no participant Id was provided and shared key usage is disabled");break;case"removeTransform":r=n.trackId,null===(i=T.find((e=>e.getTrackId()===r)))||void 0===i||i.unsetParticipant();break;case"updateCodec":O(n.participantId,n.trackId).setVideoCodec(n.codec);break;case"setRTPMap":R.forEach((e=>{e.setRtpMap(n.map)}));break;case"ratchetRequest":D(n.participantId).ratchetKey(n.keyIndex)}var r,i,o,a},self.RTCTransformEvent&&(c.debug("setup transform event"),self.onrtctransform=e=>{const t=e.transformer;c.debug("transformer",t),t.handled=!0;const{kind:n,participantId:r,trackId:i,codec:o}=t.options,a="encode"===n?N(i):O(r,i);c.debug("transform",{codec:o}),a.setupTransform(n,t.readable,t.writable,i,o)})}));
|
2
|
+
//# sourceMappingURL=livekit-client.e2ee.worker.js.map
|
@@ -0,0 +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]}
|