initial commit

This commit is contained in:
Antoine Ouvrard
2020-11-23 10:28:32 +01:00
commit d3277d6563
283 changed files with 78127 additions and 0 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+163
View File
@@ -0,0 +1,163 @@
/* global ga */
(function(ctx) {
/**
*
*/
function Analytics(options) {
/* eslint-disable */
if (!options.googleAnalyticsTrackingId) {
console.log(
'Failed to initialize Google Analytics handler, no tracking ID');
return;
}
/**
* Google Analytics
* TODO: Keep this local, there's no need to add it to window.
*/
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', options.googleAnalyticsTrackingId, 'auto');
ga('send', 'pageview');
/* eslint-enable */
}
/**
* Extracts the integer to use for a Google Analytics event's value field
* from a lib-jitsi-meet analytics event.
* @param {Object} event - The lib-jitsi-meet analytics event.
* @returns {Object} - The integer to use for the 'value' of a Google
* Analytics event.
* @private
*/
Analytics.prototype._extractAction = function(event) {
// Page events have a single 'name' field.
if (event.type === 'page') {
return event.name;
}
// All other events have action, actionSubject, and source fields. All
// three fields are required, and the often jitsi-meet and
// lib-jitsi-meet use the same value when separate values are not
// necessary (i.e. event.action == event.actionSubject).
// Here we concatenate these three fields, but avoid adding the same
// value twice, because it would only make the GA event's action harder
// to read.
let action = event.action;
if (event.actionSubject && event.actionSubject !== event.action) {
// Intentionally use string concatenation as analytics needs to
// work on IE but this file does not go through babel. For some
// reason disabling this globally for the file does not have an
// effect.
// eslint-disable-next-line prefer-template
action = event.actionSubject + '.' + action;
}
if (event.source && event.source !== event.action
&& event.source !== event.action) {
// eslint-disable-next-line prefer-template
action = event.source + '.' + action;
}
return action;
};
/**
* Extracts the integer to use for a Google Analytics event's value field
* from a lib-jitsi-meet analytics event.
* @param {Object} event - The lib-jitsi-meet analytics event.
* @returns {Object} - The integer to use for the 'value' of a Google
* Analytics event, or NaN if the lib-jitsi-meet event doesn't contain a
* suitable value.
* @private
*/
Analytics.prototype._extractValue = function(event) {
let value = event && event.attributes && event.attributes.value;
// Try to extract an integer from the "value" attribute.
value = Math.round(parseFloat(value));
return value;
};
/**
* Extracts the string to use for a Google Analytics event's label field
* from a lib-jitsi-meet analytics event.
* @param {Object} event - The lib-jitsi-meet analytics event.
* @returns {string} - The string to use for the 'label' of a Google
* Analytics event.
* @private
*/
Analytics.prototype._extractLabel = function(event) {
let label = '';
// The label field is limited to 500B. We will concatenate all
// attributes of the event, except the user agent because it may be
// lengthy and is probably included from elsewhere.
for (const property in event.attributes) {
if (property !== 'permanent_user_agent'
&& property !== 'permanent_callstats_name'
&& event.attributes.hasOwnProperty(property)) {
// eslint-disable-next-line prefer-template
label += property + '=' + event.attributes[property] + '&';
}
}
if (label.length > 0) {
label = label.slice(0, -1);
}
return label;
};
/**
* This is the entry point of the API. The function sends an event to
* google analytics. The format of the event is described in
* AnalyticsAdapter in lib-jitsi-meet.
* @param {Object} event - the event in the format specified by
* lib-jitsi-meet.
*/
Analytics.prototype.sendEvent = function(event) {
if (!event || !ga) {
return;
}
const ignoredEvents
= [ 'e2e_rtt', 'rtp.stats', 'rtt.by.region', 'available.device',
'stream.switch.delay', 'ice.state.changed', 'ice.duration' ];
// Temporary removing some of the events that are too noisy.
if (ignoredEvents.indexOf(event.action) !== -1) {
return;
}
const gaEvent = {
'eventCategory': 'jitsi-meet',
'eventAction': this._extractAction(event),
'eventLabel': this._extractLabel(event)
};
const value = this._extractValue(event);
if (!isNaN(value)) {
gaEvent.eventValue = value;
}
ga('send', 'event', gaEvent);
};
if (typeof ctx.JitsiMeetJS === 'undefined') {
ctx.JitsiMeetJS = {};
}
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
ctx.JitsiMeetJS.app = {};
}
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
ctx.JitsiMeetJS.app.analyticsHandlers = [];
}
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
})(window);
/* eslint-enable prefer-template */
+2
View File
@@ -0,0 +1,2 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/libs/",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);const r=(window.JitsiMeetJS||(window.JitsiMeetJS={}),window.JitsiMeetJS.app||(window.JitsiMeetJS.app={}),window.JitsiMeetJS.app);r.analyticsHandlers=r.analyticsHandlers||[],r.analyticsHandlers.push(class extends class{constructor(e={}){this._enabled=!1,this._whiteListedEvents=e.whiteListedEvents,this._blackListedEvents=[...e.blackListedEvents||[],"e2e_rtt","rtp.stats","rtt.by.region","available.device","stream.switch.delay","ice.state.changed","ice.duration","peer.conn.status.duration"]}_extractName(e){if("page"===e.type)return e.name;const{action:t,actionSubject:n,source:r}=e;let i=t;return n&&n!==t&&(i=`${n}.${t}`),r&&r!==t&&(i=`${r}.${i}`),i}_shouldIgnore(e){if(!e||!this._enabled)return!0;const t=this._extractName(e);return Array.isArray(this._whiteListedEvents)?-1===this._whiteListedEvents.indexOf(t):!!Array.isArray(this._blackListedEvents)&&-1!==this._blackListedEvents.indexOf(t)}}{constructor(e){if(super(e),this._userProperties={},!e.googleAnalyticsTrackingId)throw new Error("Failed to initialize Google Analytics handler, no tracking ID");this._enabled=!0,this._initGoogleAnalytics(e)}_initGoogleAnalytics(e){var t,n,r,i,a,s;t=window,n=document,r="script",i="ga",t.GoogleAnalyticsObject=i,t.ga=t.ga||function(){(t.ga.q=t.ga.q||[]).push(arguments)},t.ga.l=1*new Date,a=n.createElement(r),s=n.getElementsByTagName(r)[0],a.async=1,a.src="//www.google-analytics.com/analytics.js",s.parentNode.insertBefore(a,s),ga("create",e.googleAnalyticsTrackingId,"auto"),ga("send","pageview")}_extractValue(e){let t=e&&e.attributes&&e.attributes.value;return t=Math.round(parseFloat(t)),t}_extractLabel(e){const{attributes:t={}}=e,n=Object.keys(t).map(e=>`${e}=${t[e]}`);return n.push(this._userPropertiesString),n.join("&")}setUserProperties(e={}){if(!this._enabled)return;const t=["user_agent","callstats_name"];this._userPropertiesString=Object.keys(e).filter(e=>-1===t.indexOf(e)).map(t=>`permanent_${t}=${e[t]}`).join("&")}sendEvent(e){if(this._shouldIgnore(e))return;const t={eventCategory:"jitsi-meet",eventAction:this._extractName(e),eventLabel:this._extractLabel(e)},n=this._extractValue(e);isNaN(n)||(t.eventValue=n),ga("send","event",t)}})}]);
//# sourceMappingURL=analytics-ga.min.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+99
View File
@@ -0,0 +1,99 @@
{
"safari": [
{"version": "10.2" },
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": false,
"videoOut": false,
"screenSharing": false
},
"iframeCapabilities": {
"isSupported": false
}
}
],
"chrome":
[
{"version": "51" },
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": true
}
}
],
"opera":
[
{"version": "22" },
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": false
}
}
],
"firefox": [
{"version": "52.4" },
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": true
}
}
],
"edge": [
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": false
}
}
],
"nwjs": [
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": true
}
}
],
"electron": [
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": true
}
}
],
"react-native": [
{
"capabilities": {
"audioIn": true,
"audioOut": true,
"videoIn": true,
"videoOut": true,
"screenSharing": false
}
}
]
}
+2
View File
@@ -0,0 +1,2 @@
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/libs/",r(r.s=0)}([function(e,t){}]);
//# sourceMappingURL=close3.min.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,SAIjBlC,EAAoBA,EAAoBmC,EAAI,G","file":"close3.min.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/libs/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n"],"sourceRoot":""}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
!function(n){var e={};function t(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return n[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}t.m=n,t.c=e,t.d=function(n,e,o){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:o})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var r in n)t.d(o,r,function(e){return n[e]}.bind(null,r));return o},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="/libs/",t(t.s=0)}([function(n,e,t){"use strict";t.r(e);function o(n,e=!1,t="hash"){const o="search"===t?n.search:n.hash,r={},i=o&&o.substr(1).split("&")||[];if("hash"===t&&1===i.length){const n=i[0];if(n.startsWith("/")&&1===n.split("&").length)return r}return i.forEach(n=>{const t=n.split("="),o=t[0];if(!o)return;let i;try{if(i=t[1],!e){const n=decodeURIComponent(i).replace(/\\&/,"&");i="undefined"===n?void 0:JSON.parse(n)}}catch(n){return void function(n,e=""){console.error(e,n),window.onerror&&window.onerror(e,null,null,null,n)}(n,"Failed to parse URL parameter value: "+String(i))}r[o]=i}),r}function r(n){if(!n)return n;try{n=decodeURIComponent(n)}catch(n){}return n=(n=n.normalize("NFKC")).toLowerCase(),(n=encodeURIComponent(n)).toLowerCase()}if("function"==typeof createConnectionExternally){let n=o(window.location,!0,"hash")["config.externalConnectUrl"]||config.websocket?void 0:config.externalConnectUrl;const e=o(window.location,!0,"hash")["config.iAmRecorder"];let t;if(n&&(t=function(){const n=window.location.pathname;return r(n.substring(n.lastIndexOf("/")+1)||void 0)}())&&!e){n+="?room="+t;const e=o(window.location,!0,"search").jwt;e&&(n+="&token="+e),createConnectionExternally(n,n=>{window.XMPPAttachInfo={status:"success",data:n},i()},c)}else c()}else c();function i(){window.APP&&"ready"===window.APP.connect.status&&window.APP.connect.handler()}function c(n){n&&console.warn(n),window.XMPPAttachInfo={status:"error"},i()}}]);
//# sourceMappingURL=do_external_connect.min.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+67
View File
@@ -0,0 +1,67 @@
/**
* Requests the given webservice that will create the connection and will return
* the necessary details(rid, sid and jid) to attach to this connection and
* start using it. This script can be used for optimizing the connection startup
* time. The function will send AJAX request to a webservice that should
* create the bosh session much faster than the client because the webservice
* can be started on the same machine as the XMPP serever.
*
* NOTE: It's vert important to execute this function as early as you can for
* optimal results.
*
* @param webserviceUrl the url for the web service that is going to create the
* connection.
* @param successCallback callback function called with the result of the AJAX
* request if the request was successfull. The callback will receive one
* parameter which will be JS Object with properties - rid, sid and jid. This
* result should be passed to JitsiConnection.attach method in order to use that
* connection.
* @param error_callback callback function called the AJAX request fail. This
* callback is going to receive one parameter which is going to be JS error
* object with a reason for failure in it.
*/
function createConnectionExternally( // eslint-disable-line no-unused-vars
webserviceUrl,
successCallback,
error_callback) {
if (!window.XMLHttpRequest) {
error_callback(new Error('XMLHttpRequest is not supported!'));
return;
}
var HTTP_STATUS_OK = 200;
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == xhttp.DONE) {
var now = window.connectionTimes['external_connect.done']
= window.performance.now();
console.log('(TIME) external connect XHR done:\t', now);
if (xhttp.status == HTTP_STATUS_OK) {
try {
var data = JSON.parse(xhttp.responseText);
successCallback(data);
} catch (e) {
error_callback(e);
}
} else {
error_callback(new Error('XMLHttpRequest error. Status: '
+ xhttp.status + '. Error message: ' + xhttp.statusText));
}
}
};
xhttp.open('GET', webserviceUrl, true);
// Fixes external connect for IE
// The timeout property may be set only after calling the open() method
// and before calling the send() method.
xhttp.timeout = 3000;
window.connectionTimes = {};
var now = window.connectionTimes['external_connect.sending']
= window.performance.now();
console.log('(TIME) Sending external connect XHR:\t', now);
xhttp.send();
}
+2
View File
@@ -0,0 +1,2 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/libs/",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}self.FLAC_SCRIPT_LOCATION="/libs/",importScripts("/libs/libflac4-1.3.2.min.js");const i={0:"FLAC__STREAM_ENCODER_OK",1:"FLAC__STREAM_ENCODER_UNINITIALIZED",2:"FLAC__STREAM_ENCODER_OGG_ERROR",3:"FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR",4:"FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA",5:"FLAC__STREAM_ENCODER_CLIENT_ERROR",6:"FLAC__STREAM_ENCODER_IO_ERROR",7:"FLAC__STREAM_ENCODER_FRAMING_ERROR",8:"FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR"},o=Object.freeze({UNINTIALIZED:"uninitialized",WORKING:"working",FINISHED:"finished"});class a{constructor(e,t=16,n=4096){if(r(this,"_encoderId",0),r(this,"_flacBuffers",[]),r(this,"_flacLength",0),r(this,"_state",o.UNINTIALIZED),r(this,"_data",null),r(this,"_onMetadataAvailable",()=>{}),!Flac.isReady())throw new Error("libflac is not ready yet!");if(this._sampleRate=e,this._bitDepth=t,this._bufferSize=n,this._encoderId=Flac.init_libflac_encoder(this._sampleRate,1,this._bitDepth,5,0,!0,0),0===this._encoderId)throw new Error("Failed to create libflac encoder.");if(0!==Flac.init_encoder_stream(this._encoderId,this._onEncodedData.bind(this),this._onMetadataAvailable.bind(this)))throw new Error("Failed to initalise libflac encoder.");this._state=o.WORKING}encode(e){if(this._state!==o.WORKING)throw new Error("Encoder is not ready or has finished.");if(!Flac.isReady())throw new Error("Flac not ready");const t=e.length,n=new Int32Array(t),r=new DataView(n.buffer);let a=0;for(let n=0;n<t;n++)r.setInt32(a,32767*e[n],!0),a+=4;if(1!==Flac.FLAC__stream_encoder_process_interleaved(this._encoderId,n,n.length)){const e=Flac.FLAC__stream_encoder_get_state(this._encoderId);console.error("Error during encoding",i[e])}}finish(){if(this._state===o.WORKING){this._state=o.FINISHED;const e=Flac.FLAC__stream_encoder_finish(this._encoderId);console.log("Flac encoding finished: ",e),Flac.FLAC__stream_encoder_delete(this._encoderId),this._data=this._exportFlacBlob()}}getBlob(){return this._state===o.FINISHED?this._data:null}_exportFlacBlob(){const e=function(e,t){const n=new Uint8Array(t);let r=0;const i=e.length;for(let t=0;t<i;t++){const i=e[t];n.set(i,r),r+=i.length}return n}(this._flacBuffers,this._flacLength);return new Blob([e],{type:"audio/flac"})}_onEncodedData(e,t){this._flacBuffers.push(e),this._flacLength+=e.byteLength}}let s=null;self.onmessage=function(e){switch(e.data.command){case"MAIN_THREAD_INIT":{const t=e.data.config.bps,n=e.data.config.sampleRate;Flac.isReady()?(s=new a(n,t),self.postMessage({command:"WORKER_LIBFLAC_READY"})):Flac.onready=function(){setTimeout(()=>{s=new a(n,t),self.postMessage({command:"WORKER_LIBFLAC_READY"})},0)};break}case"MAIN_THREAD_NEW_DATA_ARRIVED":null===s?console.error("flacEncoderWorker received data when the encoder is not ready."):s.encode(e.data.buf);break;case"MAIN_THREAD_FINISH":if(null!==s){s.finish();const e=s.getBlob();self.postMessage({command:"WORKER_BLOB_READY",buf:e}),s=null}}}}]);
//# sourceMappingURL=flacEncodeWorker.min.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,608 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./modules/e2ee/crypto-utils.js
/**
* Derives a set of keys from the master key.
* @param {CryptoKey} material - master key to derive from
*
* See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1
*/
async function deriveKeys(material) {
const info = new ArrayBuffer();
const textEncoder = new TextEncoder();
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF
// https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams
const encryptionKey = await crypto.subtle.deriveKey({
name: 'HKDF',
salt: textEncoder.encode('JFrameEncryptionKey'),
hash: 'SHA-256',
info
}, material, {
name: 'AES-CTR',
length: 128
}, false, [ 'encrypt', 'decrypt' ]);
const authenticationKey = await crypto.subtle.deriveKey({
name: 'HKDF',
salt: textEncoder.encode('JFrameAuthenticationKey'),
hash: 'SHA-256',
info
}, material, {
name: 'HMAC',
hash: 'SHA-256'
}, false, [ 'sign' ]);
const saltKey = await crypto.subtle.deriveBits({
name: 'HKDF',
salt: textEncoder.encode('JFrameSaltKey'),
hash: 'SHA-256',
info
}, material, 128);
return {
material,
encryptionKey,
authenticationKey,
saltKey
};
}
/**
* Ratchets a key. See
* https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1
* @param {CryptoKey} material - base key material
* @returns {ArrayBuffer} - ratcheted key material
*/
async function ratchet(material) {
const textEncoder = new TextEncoder();
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits
return crypto.subtle.deriveBits({
name: 'HKDF',
salt: textEncoder.encode('JFrameRatchetKey'),
hash: 'SHA-256',
info: new ArrayBuffer()
}, material, 256);
}
/**
* Converts a raw key into a WebCrypto key object with default options
* suitable for our usage.
* @param {ArrayBuffer} keyBytes - raw key
* @param {Array} keyUsages - key usages, see importKey documentation
* @returns {CryptoKey} - the WebCrypto key.
*/
async function importKey(keyBytes) {
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
return crypto.subtle.importKey('raw', keyBytes, 'HKDF', false, [ 'deriveBits', 'deriveKey' ]);
}
// CONCATENATED MODULE: ./modules/e2ee/utils.js
/**
* Polyfill RTCEncoded(Audio|Video)Frame.getMetadata() (not available in M83, available M84+).
* The polyfill can not be done on the prototype since its not exposed in workers. Instead,
* it is done as another transformation to keep it separate.
* TODO: remove when we decode to drop M83 support.
*/
function polyFillEncodedFrameMetadata(encodedFrame, controller) {
if (!encodedFrame.getMetadata) {
encodedFrame.getMetadata = function() {
return {
// TODO: provide a more complete polyfill based on additionalData for video.
synchronizationSource: this.synchronizationSource,
contributingSources: this.contributingSources
};
};
}
controller.enqueue(encodedFrame);
}
/**
* Compares two byteArrays for equality.
*/
function isArrayEqual(a1, a2) {
if (a1.byteLength !== a2.byteLength) {
return false;
}
for (let i = 0; i < a1.byteLength; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
// CONCATENATED MODULE: ./modules/e2ee/Context.js
/* eslint-disable no-bitwise */
/* global BigInt */
// We use a ringbuffer of keys so we can change them and still decode packets that were
// encrypted with an old key. We use a size of 16 which corresponds to the four bits
// in the frame trailer.
const keyRingSize = 16;
// We copy the first bytes of the VP8 payload unencrypted.
// For keyframes this is 10 bytes, for non-keyframes (delta) 3. See
// https://tools.ietf.org/html/rfc6386#section-9.1
// This allows the bridge to continue detecting keyframes (only one byte needed in the JVB)
// and is also a bit easier for the VP8 decoder (i.e. it generates funny garbage pictures
// instead of being unable to decode).
// This is a bit for show and we might want to reduce to 1 unconditionally in the final version.
//
// For audio (where frame.type is not set) we do not encrypt the opus TOC byte:
// https://tools.ietf.org/html/rfc6716#section-3.1
const unencryptedBytes = {
key: 10,
delta: 3,
undefined: 1 // frame.type is not set on audio
};
// Use truncated SHA-256 hashes, 80 bіts for video, 32 bits for audio.
// This follows the same principles as DTLS-SRTP.
const authenticationTagOptions = {
name: 'HMAC',
hash: 'SHA-256'
};
const digestLength = {
key: 10,
delta: 10,
undefined: 4 // frame.type is not set on audio
};
// Maximum number of forward ratchets to attempt when the authentication
// tag on a remote packet does not match the current key.
const ratchetWindow = 8;
/**
* Per-participant context holding the cryptographic keys and
* encode/decode functions
*/
class Context_Context {
/**
* @param {string} id - local muc resourcepart
*/
constructor(id) {
// An array (ring) of keys that we use for sending and receiving.
this._cryptoKeyRing = new Array(keyRingSize);
// A pointer to the currently used key.
this._currentKeyIndex = -1;
// A per-sender counter that is used create the AES CTR.
// Must be incremented on every frame that is sent, can be reset on
// key changes.
this._sendCount = BigInt(0); // eslint-disable-line new-cap
this._id = id;
}
/**
* Derives the different subkeys and starts using them for encryption or
* decryption.
* @param {Uint8Array|false} key bytes. Pass false to disable.
* @param {Number} keyIndex
*/
async setKey(keyBytes, keyIndex) {
let newKey;
if (keyBytes) {
const material = await importKey(keyBytes);
newKey = await deriveKeys(material);
} else {
newKey = false;
}
this._currentKeyIndex = keyIndex % this._cryptoKeyRing.length;
this._setKeys(newKey);
}
/**
* Sets a set of keys and resets the sendCount.
* decryption.
* @param {Object} keys set of keys.
* @private
*/
_setKeys(keys) {
this._cryptoKeyRing[this._currentKeyIndex] = keys;
this._sendCount = BigInt(0); // eslint-disable-line new-cap
}
/**
* Function that will be injected in a stream and will encrypt the given encoded frames.
*
* @param {RTCEncodedVideoFrame|RTCEncodedAudioFrame} encodedFrame - Encoded video frame.
* @param {TransformStreamDefaultController} controller - TransportStreamController.
*
* The packet format is a variant of
* https://tools.ietf.org/html/draft-omara-sframe-00
* using a trailer instead of a header. One of the design goals was to not require
* changes to the SFU which for video requires not encrypting the keyframe bit of VP8
* as SFUs need to detect a keyframe (framemarking or the generic frame descriptor will
* solve this eventually). This also "hides" that a client is using E2EE a bit.
*
* Note that this operates on the full frame, i.e. for VP8 the data described in
* https://tools.ietf.org/html/rfc6386#section-9.1
*
* The VP8 payload descriptor described in
* https://tools.ietf.org/html/rfc7741#section-4.2
* is part of the RTP packet and not part of the encoded frame and is therefore not
* controllable by us. This is fine as the SFU keeps having access to it for routing.
*/
encodeFunction(encodedFrame, controller) {
const keyIndex = this._currentKeyIndex;
if (this._cryptoKeyRing[keyIndex]) {
this._sendCount++;
// Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.
const frameHeader = new Uint8Array(encodedFrame.data, 0, unencryptedBytes[encodedFrame.type]);
// Construct frame trailer. Similar to the frame header described in
// https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2
// but we put it at the end.
// 0 1 2 3 4 5 6 7
// ---------+---------------------------------+-+-+-+-+-+-+-+-+
// payload | CTR... (length=LEN) |S|LEN |KID |
// ---------+---------------------------------+-+-+-+-+-+-+-+-+
const counter = new Uint8Array(16);
const counterView = new DataView(counter.buffer);
// The counter is encoded as a variable-length field.
counterView.setBigUint64(8, this._sendCount);
let counterLength = 8;
for (let i = 8; i < counter.byteLength; i++ && counterLength--) {
if (counterView.getUint8(i) !== 0) {
break;
}
}
const frameTrailer = new Uint8Array(counterLength + 1);
frameTrailer.set(new Uint8Array(counter.buffer, counter.byteLength - counterLength));
// Since we never send a counter of 0 we send counterLength - 1 on the wire.
// This is different from the sframe draft, increases the key space and lets us
// ignore the case of a zero-length counter at the receiver.
frameTrailer[frameTrailer.byteLength - 1] = keyIndex | ((counterLength - 1) << 4);
// XOR the counter with the saltKey to construct the AES CTR.
const saltKey = new DataView(this._cryptoKeyRing[keyIndex].saltKey);
for (let i = 0; i < counter.byteLength; i++) {
counterView.setUint8(i, counterView.getUint8(i) ^ saltKey.getUint8(i));
}
return crypto.subtle.encrypt({
name: 'AES-CTR',
counter,
length: 64
}, this._cryptoKeyRing[keyIndex].encryptionKey, new Uint8Array(encodedFrame.data,
unencryptedBytes[encodedFrame.type]))
.then(cipherText => {
const newData = new ArrayBuffer(frameHeader.byteLength + cipherText.byteLength
+ digestLength[encodedFrame.type] + frameTrailer.byteLength);
const newUint8 = new Uint8Array(newData);
newUint8.set(frameHeader); // copy first bytes.
newUint8.set(new Uint8Array(cipherText), unencryptedBytes[encodedFrame.type]); // add ciphertext.
// Leave some space for the authentication tag. This is filled with 0s initially, similar to
// STUN message-integrity described in https://tools.ietf.org/html/rfc5389#section-15.4
newUint8.set(frameTrailer, frameHeader.byteLength + cipherText.byteLength
+ digestLength[encodedFrame.type]); // append trailer.
return crypto.subtle.sign(authenticationTagOptions, this._cryptoKeyRing[keyIndex].authenticationKey,
new Uint8Array(newData)).then(authTag => {
// Set the truncated authentication tag.
newUint8.set(new Uint8Array(authTag, 0, digestLength[encodedFrame.type]),
unencryptedBytes[encodedFrame.type] + cipherText.byteLength);
encodedFrame.data = newData;
return controller.enqueue(encodedFrame);
});
}, e => {
// TODO: surface this to the app.
console.error(e);
// We are not enqueuing the frame here on purpose.
});
}
/* NOTE WELL:
* This will send unencrypted data (only protected by DTLS transport encryption) when no key is configured.
* This is ok for demo purposes but should not be done once this becomes more relied upon.
*/
controller.enqueue(encodedFrame);
}
/**
* Function that will be injected in a stream and will decrypt the given encoded frames.
*
* @param {RTCEncodedVideoFrame|RTCEncodedAudioFrame} encodedFrame - Encoded video frame.
* @param {TransformStreamDefaultController} controller - TransportStreamController.
*/
async decodeFunction(encodedFrame, controller) {
const data = new Uint8Array(encodedFrame.data);
const keyIndex = data[encodedFrame.data.byteLength - 1] & 0xf; // lower four bits.
if (this._cryptoKeyRing[keyIndex]) {
const counterLength = 1 + ((data[encodedFrame.data.byteLength - 1] >> 4) & 0x7);
const frameHeader = new Uint8Array(encodedFrame.data, 0, unencryptedBytes[encodedFrame.type]);
// Extract the truncated authentication tag.
const authTagOffset = encodedFrame.data.byteLength - (digestLength[encodedFrame.type]
+ counterLength + 1);
const authTag = encodedFrame.data.slice(authTagOffset, authTagOffset
+ digestLength[encodedFrame.type]);
// Set authentication tag bytes to 0.
const zeros = new Uint8Array(digestLength[encodedFrame.type]);
data.set(zeros, encodedFrame.data.byteLength - (digestLength[encodedFrame.type] + counterLength + 1));
// Do truncated hash comparison. If the hash does not match we might have to advance the
// ratchet a limited number of times. See (even though the description there is odd)
// https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1
let { authenticationKey, material } = this._cryptoKeyRing[keyIndex];
let valid = false;
let newKeys = null;
for (let distance = 0; distance < ratchetWindow; distance++) {
const calculatedTag = await crypto.subtle.sign(authenticationTagOptions,
authenticationKey, encodedFrame.data);
if (isArrayEqual(new Uint8Array(authTag),
new Uint8Array(calculatedTag.slice(0, digestLength[encodedFrame.type])))) {
valid = true;
if (distance > 0) {
this._setKeys(newKeys);
}
break;
}
// Attempt to ratchet and generate the next set of keys.
material = await importKey(await ratchet(material));
newKeys = await deriveKeys(material);
authenticationKey = newKeys.authenticationKey;
}
// Check whether we found a valid signature.
if (!valid) {
// TODO: return an error to the app.
console.error('Authentication tag mismatch');
return;
}
// Extract the counter.
const counter = new Uint8Array(16);
counter.set(data.slice(encodedFrame.data.byteLength - (counterLength + 1),
encodedFrame.data.byteLength - 1), 16 - counterLength);
const counterView = new DataView(counter.buffer);
// XOR the counter with the saltKey to construct the AES CTR.
const saltKey = new DataView(this._cryptoKeyRing[keyIndex].saltKey);
for (let i = 0; i < counter.byteLength; i++) {
counterView.setUint8(i,
counterView.getUint8(i) ^ saltKey.getUint8(i));
}
return crypto.subtle.decrypt({
name: 'AES-CTR',
counter,
length: 64
}, this._cryptoKeyRing[keyIndex].encryptionKey, new Uint8Array(encodedFrame.data,
unencryptedBytes[encodedFrame.type],
encodedFrame.data.byteLength - (unencryptedBytes[encodedFrame.type]
+ digestLength[encodedFrame.type] + counterLength + 1))
).then(plainText => {
const newData = new ArrayBuffer(unencryptedBytes[encodedFrame.type] + plainText.byteLength);
const newUint8 = new Uint8Array(newData);
newUint8.set(frameHeader);
newUint8.set(new Uint8Array(plainText), unencryptedBytes[encodedFrame.type]);
encodedFrame.data = newData;
return controller.enqueue(encodedFrame);
}, e => {
console.error(e);
// TODO: notify the application about error status.
// TODO: For video we need a better strategy since we do not want to based any
// non-error frames on a garbage keyframe.
if (encodedFrame.type === undefined) { // audio, replace with silence.
const newData = new ArrayBuffer(3);
const newUint8 = new Uint8Array(newData);
newUint8.set([ 0xd8, 0xff, 0xfe ]); // opus silence frame.
encodedFrame.data = newData;
controller.enqueue(encodedFrame);
}
});
} else if (keyIndex >= this._cryptoKeyRing.length && this._cryptoKeyRing[this._currentKeyIndex]) {
// If we are encrypting but don't have a key for the remote drop the frame.
// This is a heuristic since we don't know whether a packet is encrypted,
// do not have a checksum and do not have signaling for whether a remote participant does
// encrypt or not.
return;
}
// TODO: this just passes through to the decoder. Is that ok? If we don't know the key yet
// we might want to buffer a bit but it is still unclear how to do that (and for how long etc).
controller.enqueue(encodedFrame);
}
}
// CONCATENATED MODULE: ./modules/e2ee/Worker.js
/* global TransformStream */
/* eslint-disable no-bitwise */
// Worker for E2EE/Insertable streams.
//
const contexts = new Map(); // Map participant id => context
onmessage = async event => {
const { operation } = event.data;
if (operation === 'encode') {
const { readableStream, writableStream, participantId } = event.data;
if (!contexts.has(participantId)) {
contexts.set(participantId, new Context_Context(participantId));
}
const context = contexts.get(participantId);
const transformStream = new TransformStream({
transform: context.encodeFunction.bind(context)
});
readableStream
.pipeThrough(new TransformStream({
transform: polyFillEncodedFrameMetadata // M83 polyfill.
}))
.pipeThrough(transformStream)
.pipeTo(writableStream);
} else if (operation === 'decode') {
const { readableStream, writableStream, participantId } = event.data;
if (!contexts.has(participantId)) {
contexts.set(participantId, new Context_Context(participantId));
}
const context = contexts.get(participantId);
const transformStream = new TransformStream({
transform: context.decodeFunction.bind(context)
});
readableStream
.pipeThrough(new TransformStream({
transform: polyFillEncodedFrameMetadata // M83 polyfill.
}))
.pipeThrough(transformStream)
.pipeTo(writableStream);
} else if (operation === 'setKey') {
const { participantId, key, keyIndex } = event.data;
if (!contexts.has(participantId)) {
contexts.set(participantId, new Context_Context(participantId));
}
const context = contexts.get(participantId);
if (key) {
context.setKey(key, keyIndex);
} else {
context.setKey(false, keyIndex);
}
} else if (operation === 'cleanup') {
const { participantId } = event.data;
contexts.delete(participantId);
} else {
console.error('e2ee worker', operation);
}
};
/***/ })
/******/ ]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long