@wireapp/avs 0.0.3168 → 0.0.4297
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avs-worker.js +441 -0
- package/dist/avs_core.d.ts +2 -0
- package/dist/avs_core.js +2996 -4821
- package/dist/avs_pc.d.ts +9 -4
- package/dist/avs_pc.js +839 -169
- package/dist/avs_wcall.d.ts +117 -70
- package/dist/avs_wcall.js +248 -174
- package/package.json +15 -8
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
var coders = {};
|
|
4
|
+
|
|
5
|
+
const HDR_VERSION = 0;
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
Header
|
|
9
|
+
|
|
10
|
+
1 1 1 1 1 1
|
|
11
|
+
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
12
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
13
|
+
| V=0 |E| RES |S|LEN |1|KLEN |
|
|
14
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
15
|
+
| KID... (length=KLEN) |
|
|
16
|
+
+-------------------------------+
|
|
17
|
+
| CTR... (length=LEN) |
|
|
18
|
+
+-------------------------------+
|
|
19
|
+
|
|
20
|
+
Extensions
|
|
21
|
+
|
|
22
|
+
0 1 2 3 4 5 6 7
|
|
23
|
+
+-+-+-+-+-+-+-+-+---------------------------+
|
|
24
|
+
|M| ELN | EID | VAL... (length=ELEN) |
|
|
25
|
+
+-+-+-+-+-+-+-+-+---------------------------+
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
function calcLen(val) {
|
|
29
|
+
let l = 0;
|
|
30
|
+
|
|
31
|
+
if (val == 0)
|
|
32
|
+
return 1;
|
|
33
|
+
|
|
34
|
+
while (val != 0) {
|
|
35
|
+
l++;
|
|
36
|
+
val = val >>> 8;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return l;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function writeBytes(buf, p, len, val)
|
|
43
|
+
{
|
|
44
|
+
switch (len) {
|
|
45
|
+
case 8:
|
|
46
|
+
buf[p] = (val >>> 56) & 0xff;
|
|
47
|
+
p++;
|
|
48
|
+
|
|
49
|
+
case 7:
|
|
50
|
+
buf[p] = (val >>> 48) & 0xff;
|
|
51
|
+
p++;
|
|
52
|
+
|
|
53
|
+
case 6:
|
|
54
|
+
buf[p] = (val >>> 40) & 0xff;
|
|
55
|
+
p++;
|
|
56
|
+
|
|
57
|
+
case 5:
|
|
58
|
+
buf[p] = (val >>> 32) & 0xff;
|
|
59
|
+
p++;
|
|
60
|
+
|
|
61
|
+
case 4:
|
|
62
|
+
buf[p] = (val >>> 24) & 0xff;
|
|
63
|
+
p++;
|
|
64
|
+
|
|
65
|
+
case 3:
|
|
66
|
+
buf[p] = (val >>> 16) & 0xff;
|
|
67
|
+
p++;
|
|
68
|
+
|
|
69
|
+
case 2:
|
|
70
|
+
buf[p] = (val >>> 8) & 0xff;
|
|
71
|
+
p++;
|
|
72
|
+
|
|
73
|
+
case 1:
|
|
74
|
+
buf[p] = val & 0xff;
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
default:
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return len;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function readBytes(buf, p, len) {
|
|
85
|
+
let l = len;
|
|
86
|
+
let v = 0;
|
|
87
|
+
|
|
88
|
+
while (l > 0) {
|
|
89
|
+
v = v * 256;
|
|
90
|
+
v = v + buf[p];
|
|
91
|
+
p++;
|
|
92
|
+
l--;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const lv = {
|
|
96
|
+
len: len,
|
|
97
|
+
val: v
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return lv;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function frameHeaderDecode(buf) {
|
|
104
|
+
|
|
105
|
+
let ext = buf[0] & 0x10;
|
|
106
|
+
const flen = ((buf[1] >>> 4) & 0x7) + 1;
|
|
107
|
+
const x = (buf[1] >>> 3) & 0x1;
|
|
108
|
+
const klen = buf[1] & 0x7;
|
|
109
|
+
let p = 2;
|
|
110
|
+
let kid = -1;
|
|
111
|
+
let fid = -1;
|
|
112
|
+
|
|
113
|
+
if (x) {
|
|
114
|
+
const lv = readBytes(buf, p, klen + 1);
|
|
115
|
+
|
|
116
|
+
p += lv.len;
|
|
117
|
+
kid = lv.val;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
kid = klen;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const lv = readBytes(buf, p, flen);
|
|
124
|
+
p += lv.len;
|
|
125
|
+
fid = lv.val;
|
|
126
|
+
|
|
127
|
+
// Code below handles extensions, we don't use them for now...
|
|
128
|
+
/*
|
|
129
|
+
while(ext) {
|
|
130
|
+
const b = buf[p];
|
|
131
|
+
ext = b & 0x80;
|
|
132
|
+
const elen = ((b >>> 4) & 0x7) + 2;
|
|
133
|
+
p += elen;
|
|
134
|
+
}
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
if (kid < 0 || fid < 0) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const frameHdr = {
|
|
142
|
+
len: p,
|
|
143
|
+
frameId: fid,
|
|
144
|
+
keyId: kid
|
|
145
|
+
}
|
|
146
|
+
return frameHdr;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function frameHeaderEncode(fid, kid)
|
|
151
|
+
{
|
|
152
|
+
const ab = new ArrayBuffer(32);
|
|
153
|
+
const buf = new Uint8Array(ab);
|
|
154
|
+
let sig = 0;
|
|
155
|
+
let x = 0;
|
|
156
|
+
let klen = 0;
|
|
157
|
+
let flen = 0;
|
|
158
|
+
let p = 2;
|
|
159
|
+
let ext = 0;
|
|
160
|
+
|
|
161
|
+
if (kid > 7) {
|
|
162
|
+
x = 1;
|
|
163
|
+
klen = calcLen(kid) - 1;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
klen = kid;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
flen = calcLen(fid);
|
|
170
|
+
|
|
171
|
+
buf[0] = (HDR_VERSION << 5) + (ext << 4);
|
|
172
|
+
buf[1] = (sig << 7) + ((flen - 1) << 4) + (x << 3) + klen;
|
|
173
|
+
if (x) {
|
|
174
|
+
p += writeBytes(buf, p, klen + 1, kid);
|
|
175
|
+
}
|
|
176
|
+
p += writeBytes(buf, p, flen, fid);
|
|
177
|
+
|
|
178
|
+
return buf.subarray(0, p);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function xor_iv(iv, fid, kid) {
|
|
182
|
+
var oiv = new Uint8Array(iv);
|
|
183
|
+
|
|
184
|
+
oiv[0] = oiv[0] ^ ((fid >> 24) & 0xFF);
|
|
185
|
+
oiv[1] = oiv[1] ^ ((fid >> 16) & 0xFF);
|
|
186
|
+
oiv[2] = oiv[2] ^ ((fid >> 8) & 0xFF);
|
|
187
|
+
oiv[3] = oiv[3] ^ ((fid ) & 0xFF);
|
|
188
|
+
oiv[4] = oiv[4] ^ ((kid >> 24) & 0xFF);
|
|
189
|
+
oiv[5] = oiv[5] ^ ((kid >> 16) & 0xFF);
|
|
190
|
+
oiv[6] = oiv[6] ^ ((kid >> 8) & 0xFF);
|
|
191
|
+
oiv[7] = oiv[7] ^ ((kid ) & 0xFF);
|
|
192
|
+
|
|
193
|
+
return oiv;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function getMediaKey(self, index) {
|
|
197
|
+
postMessage({
|
|
198
|
+
op: "getMediaKey",
|
|
199
|
+
self: self,
|
|
200
|
+
index: index
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getCurrentMediaKey(self, index) {
|
|
205
|
+
postMessage({
|
|
206
|
+
op: "getCurrentMediaKey",
|
|
207
|
+
self: self
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function encryptFrame(coder, rtcFrame, controller) {
|
|
212
|
+
const dataBuf = rtcFrame.data;
|
|
213
|
+
const data = new Uint8Array(dataBuf);
|
|
214
|
+
const isVideo = false;
|
|
215
|
+
|
|
216
|
+
//console.log("encryptFrame=", rtcFrame);
|
|
217
|
+
const t = Object.prototype.toString.call(rtcFrame);
|
|
218
|
+
if (t === '[object RTCEncodedVideoFrame]')
|
|
219
|
+
isVideo = true;
|
|
220
|
+
|
|
221
|
+
if (coder.currentKey == null) {
|
|
222
|
+
getCurrentMediaKey(coder.self);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const baseiv = isVideo ? coder.video.iv : coder.audio.iv;
|
|
227
|
+
const iv = xor_iv(baseiv, coder.frameId, coder.currentKey.id);
|
|
228
|
+
|
|
229
|
+
const hdr = frameHeaderEncode(coder.frameId, coder.currentKey.id);
|
|
230
|
+
crypto.subtle.encrypt(
|
|
231
|
+
{
|
|
232
|
+
name: "AES-GCM",
|
|
233
|
+
iv: iv,
|
|
234
|
+
tagLength: 128,
|
|
235
|
+
additionalData: hdr
|
|
236
|
+
},
|
|
237
|
+
coder.currentKey.key,
|
|
238
|
+
data
|
|
239
|
+
)
|
|
240
|
+
.then(function(encdata){
|
|
241
|
+
const enc8 = new Uint8Array(encdata);
|
|
242
|
+
const enc = new Uint8Array(hdr.length + enc8.length);
|
|
243
|
+
|
|
244
|
+
enc.set(hdr);
|
|
245
|
+
enc.set(enc8, hdr.length);
|
|
246
|
+
|
|
247
|
+
rtcFrame.data = enc.buffer;
|
|
248
|
+
controller.enqueue(rtcFrame);
|
|
249
|
+
|
|
250
|
+
coder.frameId++;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function decryptFrame(coder, rtcFrame, controller) {
|
|
255
|
+
const dataBuf = rtcFrame.data;
|
|
256
|
+
const data = new Uint8Array(dataBuf);
|
|
257
|
+
const ssrc = rtcFrame.synchronizationSource.toString();
|
|
258
|
+
|
|
259
|
+
let uinfo = coder.audio.users[ssrc];
|
|
260
|
+
let isVideo = false;
|
|
261
|
+
|
|
262
|
+
if (!uinfo) {
|
|
263
|
+
uinfo = coder.video.users[ssrc];
|
|
264
|
+
isVideo = true;
|
|
265
|
+
}
|
|
266
|
+
if (!uinfo) {
|
|
267
|
+
console.log('decryptFrame: no userinfo for ssrc: ' + ssrc);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const frameHdr = frameHeaderDecode(data);
|
|
272
|
+
|
|
273
|
+
if (frameHdr == null) {
|
|
274
|
+
console.log('decryptFrame: failed to decode frame header');
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
console.log("coder[" + coder.self + "]"
|
|
280
|
+
+ " len=" + frameHdr.len
|
|
281
|
+
+ " kid="+ frameHdr.keyId
|
|
282
|
+
+ " fid=" + frameHdr.frameId);
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
var kinfo = null;
|
|
286
|
+
for (i = 0; i < coder.keys.length; i++) {
|
|
287
|
+
if (coder.keys[i].id == frameHdr.keyId) {
|
|
288
|
+
kinfo = coder.keys[i];
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!kinfo) {
|
|
294
|
+
getMediaKey(coder.self, frameHdr.keyId);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (isVideo) {
|
|
299
|
+
iv = xor_iv(uinfo.ivv, frameHdr.frameId, frameHdr.keyId);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
iv = xor_iv(uinfo.iva, frameHdr.frameId, frameHdr.keyId);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const hdr = data.subarray(0, frameHdr.len);
|
|
306
|
+
const dec = data.subarray(frameHdr.len);
|
|
307
|
+
crypto.subtle.decrypt(
|
|
308
|
+
{
|
|
309
|
+
name: "AES-GCM",
|
|
310
|
+
iv: iv,
|
|
311
|
+
tagLength: 128,
|
|
312
|
+
additionalData: hdr
|
|
313
|
+
},
|
|
314
|
+
kinfo.key,
|
|
315
|
+
dec
|
|
316
|
+
)
|
|
317
|
+
.then(function(decdata){
|
|
318
|
+
rtcFrame.data = decdata;
|
|
319
|
+
controller.enqueue(rtcFrame);
|
|
320
|
+
})
|
|
321
|
+
.catch(err => {
|
|
322
|
+
console.log(err);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
onmessage = async (event) => {
|
|
327
|
+
const {op, self} = event.data;
|
|
328
|
+
console.log('AVS worker op=' + op + ' self=' + self);
|
|
329
|
+
|
|
330
|
+
let coder = coders[self];
|
|
331
|
+
if (!coder) {
|
|
332
|
+
console.log('AVS worker: no coder for self=' + self);
|
|
333
|
+
}
|
|
334
|
+
/*
|
|
335
|
+
const {opinfo, readableStream, writableStream} = event.data;
|
|
336
|
+
const mval = opinfo.mtype === 'video' ? 1 : 0; // corresponds to enum frame_media_type
|
|
337
|
+
*/
|
|
338
|
+
if (op === 'create') {
|
|
339
|
+
const { iva, ivv } = event.data;
|
|
340
|
+
if (!coder) {
|
|
341
|
+
coder = {
|
|
342
|
+
self: self,
|
|
343
|
+
frameId: Math.random() * 4294967296,
|
|
344
|
+
currentKey: null,
|
|
345
|
+
keys: [],
|
|
346
|
+
audio: {
|
|
347
|
+
iv: iva,
|
|
348
|
+
users: [],
|
|
349
|
+
},
|
|
350
|
+
video: {
|
|
351
|
+
iv: ivv,
|
|
352
|
+
users: [],
|
|
353
|
+
},
|
|
354
|
+
}
|
|
355
|
+
coders[self] = coder;
|
|
356
|
+
|
|
357
|
+
console.log('AVS worker: adding coder for self=' + self);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
else if (op === 'destroy') {
|
|
361
|
+
coders[self] = null;
|
|
362
|
+
}
|
|
363
|
+
else if (op === 'addUser') {
|
|
364
|
+
if (!coder) {
|
|
365
|
+
console.log('addUser: no coder');
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const {userInfo} = event.data;
|
|
369
|
+
|
|
370
|
+
console.log('addUser: adding audio ssrc: '+userInfo.ssrca);
|
|
371
|
+
coder.audio.users[userInfo.ssrca] = userInfo;
|
|
372
|
+
if (userInfo.ssrcv != 0) {
|
|
373
|
+
coder.video.users[userInfo.ssrcv] = userInfo;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else if (op === 'setupSender') {
|
|
377
|
+
const {readableStream, writableStream} = event.data;
|
|
378
|
+
|
|
379
|
+
const transformStream = new TransformStream({
|
|
380
|
+
transform: (frame, controller) => {encryptFrame(coder, frame, controller);},
|
|
381
|
+
});
|
|
382
|
+
readableStream
|
|
383
|
+
.pipeThrough(transformStream)
|
|
384
|
+
.pipeTo(writableStream);
|
|
385
|
+
|
|
386
|
+
console.log('AVS worker sender setup successfully');
|
|
387
|
+
|
|
388
|
+
}
|
|
389
|
+
else if (op === 'setupReceiver') {
|
|
390
|
+
const {readableStream, writableStream} = event.data;
|
|
391
|
+
|
|
392
|
+
if (!coder) {
|
|
393
|
+
console.log('setupSender: no coder for self=' + self);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const transformStream = new TransformStream({
|
|
398
|
+
transform: (frame, controller) => {decryptFrame(coder, frame, controller);},
|
|
399
|
+
});
|
|
400
|
+
readableStream
|
|
401
|
+
.pipeThrough(transformStream)
|
|
402
|
+
.pipeTo(writableStream);
|
|
403
|
+
|
|
404
|
+
console.log('AVS worker receiver setup successfully');
|
|
405
|
+
}
|
|
406
|
+
else if (op === 'setMediaKey') {
|
|
407
|
+
const {index, current, key} = event.data;
|
|
408
|
+
|
|
409
|
+
console.log("setMediaKey: got key for: " + self
|
|
410
|
+
+ " index=" + index + " current=" + current
|
|
411
|
+
+ " keyLen=" + key.length);
|
|
412
|
+
|
|
413
|
+
crypto.subtle.importKey(
|
|
414
|
+
"raw",
|
|
415
|
+
key,
|
|
416
|
+
"AES-GCM",
|
|
417
|
+
false,
|
|
418
|
+
["encrypt", "decrypt"]
|
|
419
|
+
)
|
|
420
|
+
.then(function(k){
|
|
421
|
+
var kinfo = null;
|
|
422
|
+
for (i = 0; i < coder.keys.length; i++) {
|
|
423
|
+
if (coder.keys[i].id == index) {
|
|
424
|
+
coder.keys[i].key = k;
|
|
425
|
+
kinfo = coder.keys[i];
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (!kinfo) {
|
|
430
|
+
kinfo = {id:index, key:k};
|
|
431
|
+
coder.keys.push(kinfo);
|
|
432
|
+
}
|
|
433
|
+
if (index == current)
|
|
434
|
+
coder.currentKey = kinfo;
|
|
435
|
+
|
|
436
|
+
while (coder.keys.length > 4) {
|
|
437
|
+
coder.keys.shift();
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
};
|