Browse Source

feat(e2ee) add support for WebRTC Encoded Transform

An alternative to Insertable Streams, currently implemented in Safarii / WebKit.

https://w3c.github.io/webrtc-encoded-transform/

Fixes: https://github.com/jitsi/jitsi-meet/issues/9585
dev1
Saúl Ibarra Corretgé 4 years ago
parent
commit
61c977f70a

+ 13
- 0
modules/browser/BrowserCapabilities.js View File

@@ -235,6 +235,19 @@ export default class BrowserCapabilities extends BrowserDetection {
235 235
                     !== 'undefined');
236 236
     }
237 237
 
238
+    /**
239
+     * Checks if the browser supports WebRTC Encoded Transform, an alternative
240
+     * to insertable streams.
241
+     *
242
+     * NOTE: At the time of this writing the only browser supporting this is
243
+     * Safari / WebKit, behind a flag.
244
+     *
245
+     * @returns {boolean} {@code true} if the browser supports it.
246
+     */
247
+    supportsEncodedTransform() {
248
+        return Boolean(window.RTCRtpScriptTransform);
249
+    }
250
+
238 251
     /**
239 252
      * Checks if the browser supports insertable streams, needed for E2EE.
240 253
      * @returns {boolean} {@code true} if the browser supports insertable streams.

+ 35
- 17
modules/e2ee/E2EEContext.js View File

@@ -1,4 +1,4 @@
1
-/* global __filename */
1
+/* global __filename, RTCRtpScriptTransform */
2 2
 
3 3
 import { getLogger } from 'jitsi-meet-logger';
4 4
 
@@ -74,14 +74,23 @@ export default class E2EEcontext {
74 74
         }
75 75
         receiver[kJitsiE2EE] = true;
76 76
 
77
-        const receiverStreams = receiver.createEncodedStreams();
78
-
79
-        this._worker.postMessage({
80
-            operation: 'decode',
81
-            readableStream: receiverStreams.readable,
82
-            writableStream: receiverStreams.writable,
83
-            participantId
84
-        }, [ receiverStreams.readable, receiverStreams.writable ]);
77
+        if (window.RTCRtpScriptTransform) {
78
+            const options = {
79
+                operation: 'decode',
80
+                participantId
81
+            };
82
+
83
+            receiver.transform = new RTCRtpScriptTransform(this._worker, options);
84
+        } else {
85
+            const receiverStreams = receiver.createEncodedStreams();
86
+
87
+            this._worker.postMessage({
88
+                operation: 'decode',
89
+                readableStream: receiverStreams.readable,
90
+                writableStream: receiverStreams.writable,
91
+                participantId
92
+            }, [ receiverStreams.readable, receiverStreams.writable ]);
93
+        }
85 94
     }
86 95
 
87 96
     /**
@@ -98,14 +107,23 @@ export default class E2EEcontext {
98 107
         }
99 108
         sender[kJitsiE2EE] = true;
100 109
 
101
-        const senderStreams = sender.createEncodedStreams();
102
-
103
-        this._worker.postMessage({
104
-            operation: 'encode',
105
-            readableStream: senderStreams.readable,
106
-            writableStream: senderStreams.writable,
107
-            participantId
108
-        }, [ senderStreams.readable, senderStreams.writable ]);
110
+        if (window.RTCRtpScriptTransform) {
111
+            const options = {
112
+                operation: 'encode',
113
+                participantId
114
+            };
115
+
116
+            sender.transform = new RTCRtpScriptTransform(this._worker, options);
117
+        } else {
118
+            const senderStreams = sender.createEncodedStreams();
119
+
120
+            this._worker.postMessage({
121
+                operation: 'encode',
122
+                readableStream: senderStreams.readable,
123
+                writableStream: senderStreams.writable,
124
+                participantId
125
+            }, [ senderStreams.readable, senderStreams.writable ]);
126
+        }
109 127
     }
110 128
 
111 129
     /**

+ 4
- 3
modules/e2ee/E2EEncryption.js View File

@@ -96,9 +96,10 @@ export class E2EEncryption {
96 96
      * @returns {boolean}
97 97
      */
98 98
     static isSupported(config) {
99
-        return browser.supportsInsertableStreams()
100
-            && OlmAdapter.isSupported()
101
-            && !(config.testing && config.testing.disableE2EE);
99
+        return !(config.testing && config.testing.disableE2EE)
100
+            && (browser.supportsInsertableStreams()
101
+                || (config.enableEncodedTransformSupport && browser.supportsEncodedTransform()))
102
+            && OlmAdapter.isSupported();
102 103
     }
103 104
 
104 105
     /**

+ 47
- 26
modules/e2ee/Worker.js View File

@@ -7,44 +7,54 @@ import { Context } from './Context';
7 7
 
8 8
 const contexts = new Map(); // Map participant id => context
9 9
 
10
-onmessage = async event => {
11
-    const { operation } = event.data;
10
+/**
11
+ * Retrieves the participant {@code Context}, creating it if necessary.
12
+ *
13
+ * @param {string} participantId - The participant whose context we need.
14
+ * @returns {Object} The context.
15
+ */
16
+function getParticipantContext(participantId) {
17
+    if (!contexts.has(participantId)) {
18
+        contexts.set(participantId, new Context(participantId));
19
+    }
12 20
 
13
-    if (operation === 'encode') {
14
-        const { readableStream, writableStream, participantId } = event.data;
21
+    return contexts.get(participantId);
22
+}
15 23
 
16
-        if (!contexts.has(participantId)) {
17
-            contexts.set(participantId, new Context(participantId));
18
-        }
19
-        const context = contexts.get(participantId);
24
+/**
25
+ * Sets an encode / decode transform.
26
+ *
27
+ * @param {Object} context - The participant context where the transform will be applied.
28
+ * @param {string} operation - Encode / decode.
29
+ * @param {Object} readableStream - Readable stream part.
30
+ * @param {Object} writableStream - Writable stream part.
31
+ */
32
+function handleTransform(context, operation, readableStream, writableStream) {
33
+    if (operation === 'encode' || operation === 'decode') {
34
+        const transformFn = operation === 'encode' ? context.encodeFunction : context.decodeFunction;
20 35
         const transformStream = new TransformStream({
21
-            transform: context.encodeFunction.bind(context)
36
+            transform: transformFn.bind(context)
22 37
         });
23 38
 
24 39
         readableStream
25 40
             .pipeThrough(transformStream)
26 41
             .pipeTo(writableStream);
27
-    } else if (operation === 'decode') {
28
-        const { readableStream, writableStream, participantId } = event.data;
42
+    } else {
43
+        console.error(`Invalid operation: ${operation}`);
44
+    }
45
+}
29 46
 
30
-        if (!contexts.has(participantId)) {
31
-            contexts.set(participantId, new Context(participantId));
32
-        }
33
-        const context = contexts.get(participantId);
34
-        const transformStream = new TransformStream({
35
-            transform: context.decodeFunction.bind(context)
36
-        });
47
+onmessage = async event => {
48
+    const { operation } = event.data;
37 49
 
38
-        readableStream
39
-            .pipeThrough(transformStream)
40
-            .pipeTo(writableStream);
50
+    if (operation === 'encode' || operation === 'decode') {
51
+        const { readableStream, writableStream, participantId } = event.data;
52
+        const context = getParticipantContext(participantId);
53
+
54
+        handleTransform(context, operation, readableStream, writableStream);
41 55
     } else if (operation === 'setKey') {
42 56
         const { participantId, key, keyIndex } = event.data;
43
-
44
-        if (!contexts.has(participantId)) {
45
-            contexts.set(participantId, new Context(participantId));
46
-        }
47
-        const context = contexts.get(participantId);
57
+        const context = getParticipantContext(participantId);
48 58
 
49 59
         if (key) {
50 60
             context.setKey(key, keyIndex);
@@ -59,3 +69,14 @@ onmessage = async event => {
59 69
         console.error('e2ee worker', operation);
60 70
     }
61 71
 };
72
+
73
+// Operations using RTCRtpScriptTransform.
74
+if (self.RTCTransformEvent) {
75
+    self.onrtctransform = event => {
76
+        const transformer = event.transformer;
77
+        const { operation, participantId } = transformer.options;
78
+        const context = getParticipantContext(participantId);
79
+
80
+        handleTransform(context, operation, transformer.readable, transformer.writable);
81
+    };
82
+}

Loading…
Cancel
Save