@ylink-sdk/mobile-web 0.1.6 → 0.1.9-beta.1
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/e30037b8/VERSION +6 -0
- package/dist/e30037b8/libipvp-simd.d.ts +474 -0
- package/dist/e30037b8/libipvp-simd.js +3267 -0
- package/dist/e30037b8/libipvp-simd.prod.d.ts +474 -0
- package/dist/e30037b8/libipvp-simd.prod.js +184 -0
- package/dist/e30037b8/libipvp-simd.prod.wasm +0 -0
- package/dist/e30037b8/libipvp-simd.wasm +0 -0
- package/dist/e30037b8/libipvp.d.ts +474 -0
- package/dist/e30037b8/libipvp.js +3267 -0
- package/dist/e30037b8/libipvp.prod.d.ts +474 -0
- package/dist/e30037b8/libipvp.prod.js +184 -0
- package/dist/e30037b8/libipvp.prod.wasm +0 -0
- package/dist/e30037b8/libipvp.wasm +0 -0
- package/dist/e30037b8/librtc.d.ts +438 -0
- package/dist/e30037b8/librtc.global.js +5863 -0
- package/dist/e30037b8/librtc.global.prod.js +15 -0
- package/dist/e30037b8/libsfu-simd.d.ts +767 -0
- package/dist/e30037b8/libsfu-simd.js +4822 -0
- package/dist/e30037b8/libsfu-simd.prod.d.ts +767 -0
- package/dist/e30037b8/libsfu-simd.prod.js +209 -0
- package/dist/e30037b8/libsfu-simd.prod.wasm +0 -0
- package/dist/e30037b8/libsfu-simd.wasm +0 -0
- package/dist/e30037b8/libsfu.d.ts +767 -0
- package/dist/e30037b8/libsfu.js +4822 -0
- package/dist/e30037b8/libsfu.prod.d.ts +767 -0
- package/dist/e30037b8/libsfu.prod.js +209 -0
- package/dist/e30037b8/libsfu.prod.wasm +0 -0
- package/dist/e30037b8/libsfu.wasm +0 -0
- package/dist/e30037b8/libsvc-simd-m1.d.ts +767 -0
- package/dist/e30037b8/libsvc-simd-m1.js +4921 -0
- package/dist/e30037b8/libsvc-simd-m1.prod.d.ts +767 -0
- package/dist/e30037b8/libsvc-simd-m1.prod.js +209 -0
- package/dist/e30037b8/libsvc-simd-m1.prod.wasm +0 -0
- package/dist/e30037b8/libsvc-simd-m1.wasm +0 -0
- package/dist/e30037b8/libsvc-simd.d.ts +767 -0
- package/dist/e30037b8/libsvc-simd.js +4857 -0
- package/dist/e30037b8/libsvc-simd.prod.d.ts +767 -0
- package/dist/e30037b8/libsvc-simd.prod.js +209 -0
- package/dist/e30037b8/libsvc-simd.prod.wasm +0 -0
- package/dist/e30037b8/libsvc-simd.wasm +0 -0
- package/dist/e30037b8/libsvc.d.ts +767 -0
- package/dist/e30037b8/libsvc.js +4857 -0
- package/dist/e30037b8/libsvc.prod.d.ts +767 -0
- package/dist/e30037b8/libsvc.prod.js +209 -0
- package/dist/e30037b8/libsvc.prod.wasm +0 -0
- package/dist/e30037b8/libsvc.wasm +0 -0
- package/dist/e30037b8/native-audio-worker.global.js +1583 -0
- package/dist/e30037b8/native-audio-worker.global.prod.js +15 -0
- package/dist/e30037b8/native-video-worker.global.js +1960 -0
- package/dist/e30037b8/native-video-worker.global.prod.js +15 -0
- package/dist/e30037b8/worklet-audio-worker.global.js +900 -0
- package/dist/e30037b8/worklet-audio-worker.global.prod.js +15 -0
- package/dist/style.css +1 -1
- package/dist/ylink-sdk-mobile.umd.js +70 -70
- package/package.json +1 -1
|
@@ -0,0 +1,1583 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*! *****************************************************************************
|
|
5
|
+
Copyright (c) Microsoft Corporation.
|
|
6
|
+
|
|
7
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
purpose with or without fee is hereby granted.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
11
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
12
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
13
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
14
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
15
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
16
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
17
|
+
***************************************************************************** */
|
|
18
|
+
|
|
19
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
20
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
21
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
22
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
23
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
24
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
25
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function pipeline(middleware) {
|
|
30
|
+
return (context, next) => {
|
|
31
|
+
// last called middleware
|
|
32
|
+
let index = -1;
|
|
33
|
+
const run = (i) => __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
if (i <= index) {
|
|
35
|
+
console.warn('next() called multiple times');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
index = i;
|
|
39
|
+
let callback = middleware[i];
|
|
40
|
+
if (i === middleware.length)
|
|
41
|
+
callback = next;
|
|
42
|
+
if (!callback)
|
|
43
|
+
return;
|
|
44
|
+
if (callback.length > 1) {
|
|
45
|
+
return callback(context, dispatch);
|
|
46
|
+
}
|
|
47
|
+
yield callback(context, dispatch);
|
|
48
|
+
return dispatch();
|
|
49
|
+
});
|
|
50
|
+
const dispatch = () => run(index + 1);
|
|
51
|
+
return dispatch();
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class MojoReceiver {
|
|
56
|
+
constructor(channel, pipe) {
|
|
57
|
+
this.middleware = [];
|
|
58
|
+
this.pipe = pipe || Math.random().toString(36).slice(3);
|
|
59
|
+
this.channel = channel;
|
|
60
|
+
}
|
|
61
|
+
get valid() {
|
|
62
|
+
return this.channel.has(this.pipe);
|
|
63
|
+
}
|
|
64
|
+
implement(key, invoke) {
|
|
65
|
+
const m = (ctx, next) => __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
const { method, params, status, required, attached = {} } = ctx.data;
|
|
67
|
+
if (key !== method) {
|
|
68
|
+
return next();
|
|
69
|
+
}
|
|
70
|
+
let handled = false;
|
|
71
|
+
const transfer = [];
|
|
72
|
+
const reAttached = {};
|
|
73
|
+
const done = (payload) => {
|
|
74
|
+
if (handled) {
|
|
75
|
+
console.warn(`receiver[${this}] - ${method} reply multiple times`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
handled = true;
|
|
79
|
+
this.channel.send(Object.assign(Object.assign(Object.assign({}, ctx.data), {
|
|
80
|
+
// override params as it may contains un-cloned object
|
|
81
|
+
params: undefined,
|
|
82
|
+
// override attached as it may contains un-cloned object
|
|
83
|
+
attached: reAttached }), payload), transfer);
|
|
84
|
+
};
|
|
85
|
+
const reply = {
|
|
86
|
+
transfer: (...args) => {
|
|
87
|
+
if (required) {
|
|
88
|
+
transfer.push(...args);
|
|
89
|
+
}
|
|
90
|
+
return reply;
|
|
91
|
+
},
|
|
92
|
+
resolve: (value) => {
|
|
93
|
+
if (!required) {
|
|
94
|
+
handled = true;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
done({ resolve: value, status: status + 1 });
|
|
98
|
+
},
|
|
99
|
+
reject: (reason) => {
|
|
100
|
+
if (!required) {
|
|
101
|
+
handled = true;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
done({ status: status - 1, reject: reason });
|
|
105
|
+
},
|
|
106
|
+
// attached
|
|
107
|
+
set: (key, val) => {
|
|
108
|
+
if (required) {
|
|
109
|
+
reAttached[key] = val;
|
|
110
|
+
}
|
|
111
|
+
return reply;
|
|
112
|
+
},
|
|
113
|
+
get: (key) => attached[key],
|
|
114
|
+
};
|
|
115
|
+
try {
|
|
116
|
+
const timestamp = Date.now();
|
|
117
|
+
yield invoke(reply, params, ctx.ports);
|
|
118
|
+
if (true) {
|
|
119
|
+
const delta = Date.now() - timestamp;
|
|
120
|
+
if (delta > 100) {
|
|
121
|
+
console.log(`receiver[${this}] - ${method}() took ${delta}ms`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.warn(error);
|
|
127
|
+
if (!handled) {
|
|
128
|
+
reply.reject(`${error}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
ctx.data.touched = true;
|
|
132
|
+
if (!handled) {
|
|
133
|
+
yield next();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
this.middleware.unshift(m);
|
|
137
|
+
const unload = () => {
|
|
138
|
+
const index = this.middleware.findIndex((v) => v === m);
|
|
139
|
+
if (~index) {
|
|
140
|
+
this.middleware.splice(index, 1);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
return unload;
|
|
144
|
+
}
|
|
145
|
+
reject(msg = '') {
|
|
146
|
+
this.channel.reject(this, msg);
|
|
147
|
+
}
|
|
148
|
+
as() {
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
callback() {
|
|
152
|
+
return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
const { pipe, status } = ctx.data;
|
|
154
|
+
if (pipe !== this.pipe) {
|
|
155
|
+
return next();
|
|
156
|
+
}
|
|
157
|
+
if (status) {
|
|
158
|
+
return next();
|
|
159
|
+
}
|
|
160
|
+
return pipeline(this.middleware)(ctx, next);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
toString() {
|
|
164
|
+
return `${this.pipe}`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class MojoRemote {
|
|
169
|
+
constructor(channel, pipe) {
|
|
170
|
+
this.sequence = 0;
|
|
171
|
+
this.pendings = new Map();
|
|
172
|
+
this.pipe = pipe || Math.random().toString(36).slice(3);
|
|
173
|
+
this.channel = channel;
|
|
174
|
+
this.accepted = defer();
|
|
175
|
+
}
|
|
176
|
+
get ready() {
|
|
177
|
+
return this.accepted.promise;
|
|
178
|
+
}
|
|
179
|
+
get valid() {
|
|
180
|
+
return this.channel.has(this.pipe);
|
|
181
|
+
}
|
|
182
|
+
make(key) {
|
|
183
|
+
const transfer = [];
|
|
184
|
+
const attached = {};
|
|
185
|
+
const ready = (params) => __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
try {
|
|
187
|
+
yield this.ready;
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.warn(`remote[${this}] - ${key}(${params}) failed with '${error}'`);
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const send = (params, required) => {
|
|
195
|
+
this.channel.send({
|
|
196
|
+
pipe: this.pipe,
|
|
197
|
+
sequence: ++this.sequence,
|
|
198
|
+
timestamp: Date.now(),
|
|
199
|
+
method: key,
|
|
200
|
+
status: 0,
|
|
201
|
+
params,
|
|
202
|
+
required,
|
|
203
|
+
attached,
|
|
204
|
+
}, transfer);
|
|
205
|
+
};
|
|
206
|
+
const invoke = {
|
|
207
|
+
transfer: (...args) => {
|
|
208
|
+
transfer.push(...args);
|
|
209
|
+
return invoke;
|
|
210
|
+
},
|
|
211
|
+
post: (...params) => __awaiter(this, void 0, void 0, function* () {
|
|
212
|
+
yield ready(params);
|
|
213
|
+
send(params);
|
|
214
|
+
}),
|
|
215
|
+
invoke: (...params) => __awaiter(this, void 0, void 0, function* () {
|
|
216
|
+
yield ready(params);
|
|
217
|
+
send(params, true);
|
|
218
|
+
const d = defer();
|
|
219
|
+
this.pendings.set(this.sequence, d);
|
|
220
|
+
return d.promise;
|
|
221
|
+
}),
|
|
222
|
+
// attached
|
|
223
|
+
set: (key, val) => {
|
|
224
|
+
attached[key] = val;
|
|
225
|
+
return invoke;
|
|
226
|
+
},
|
|
227
|
+
get: (key) => attached[key],
|
|
228
|
+
};
|
|
229
|
+
return invoke;
|
|
230
|
+
}
|
|
231
|
+
break(msg = '') {
|
|
232
|
+
this.channel.break(this, msg);
|
|
233
|
+
this.pendings.forEach((p) => p.reject('broken'));
|
|
234
|
+
this.pendings.clear();
|
|
235
|
+
this.accepted.reject('broken');
|
|
236
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
237
|
+
this.accepted.promise = Promise.reject('broken');
|
|
238
|
+
// handle uncaught promise
|
|
239
|
+
this.accepted.promise.catch(NOOP);
|
|
240
|
+
}
|
|
241
|
+
callback() {
|
|
242
|
+
return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
|
|
243
|
+
const { pipe, sequence, timestamp, method, status, reject, resolve } = ctx.data;
|
|
244
|
+
if (pipe !== this.pipe) {
|
|
245
|
+
return next();
|
|
246
|
+
}
|
|
247
|
+
if (!status) {
|
|
248
|
+
return next();
|
|
249
|
+
}
|
|
250
|
+
const d = this.pendings.get(sequence);
|
|
251
|
+
if (!d) {
|
|
252
|
+
console.log(`remote[${this}] - receive unknown ${method} reply`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (status < 0) {
|
|
256
|
+
d.reject(reject);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
d.resolve(resolve);
|
|
260
|
+
}
|
|
261
|
+
this.pendings.delete(sequence);
|
|
262
|
+
{
|
|
263
|
+
const delta = Date.now() - timestamp;
|
|
264
|
+
if (delta > 100) {
|
|
265
|
+
console.log(`remote[${this}] - ${method}() took ${delta}ms`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
toString() {
|
|
271
|
+
return `${this.pipe}`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function defer() {
|
|
275
|
+
const until = {};
|
|
276
|
+
until.promise = new Promise((resolve, reject) => {
|
|
277
|
+
until.resolve = resolve;
|
|
278
|
+
until.reject = reject;
|
|
279
|
+
});
|
|
280
|
+
// handle uncaught promise
|
|
281
|
+
until.promise.catch(NOOP);
|
|
282
|
+
if ( typeof setTimeout !== 'undefined') {
|
|
283
|
+
setTimeout(() => until.reject('timeout'), 10000);
|
|
284
|
+
}
|
|
285
|
+
return until;
|
|
286
|
+
}
|
|
287
|
+
const NOOP = () => {
|
|
288
|
+
// nothing
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
class MojoChannel {
|
|
292
|
+
// fix me
|
|
293
|
+
// incompatible onmessage ev type
|
|
294
|
+
constructor(transport) {
|
|
295
|
+
this.remotes = new Map();
|
|
296
|
+
this.receivers = new Map();
|
|
297
|
+
this.pendingReceivers = new Set();
|
|
298
|
+
const previous = transport.onmessage;
|
|
299
|
+
this.transport = transport;
|
|
300
|
+
this.transport.onmessage = (ev) => {
|
|
301
|
+
const { data, ports = [] } = ev;
|
|
302
|
+
if (isString(data)) {
|
|
303
|
+
if (/^mojo/.test(data)) {
|
|
304
|
+
const [, pipe] = data.split(':');
|
|
305
|
+
const receiver = this.receivers.get(`${pipe}`);
|
|
306
|
+
if (receiver) {
|
|
307
|
+
this.send(`accept:${receiver}`);
|
|
308
|
+
this.oninvitation && this.oninvitation(pipe, receiver);
|
|
309
|
+
receiver.onready && receiver.onready();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.pendingReceivers.add(pipe);
|
|
313
|
+
this.oninvitation && this.oninvitation(pipe);
|
|
314
|
+
if (this.pendingReceivers.has(pipe)) {
|
|
315
|
+
previous && previous.call(this.transport, ev);
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (/^accept/.test(data)) {
|
|
320
|
+
const [, pipe] = data.split(':');
|
|
321
|
+
const remote = this.remotes.get(pipe);
|
|
322
|
+
if (!remote) {
|
|
323
|
+
console.log(`unknown accept:\n${data}`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
remote.accepted.resolve();
|
|
327
|
+
remote.onaccept && remote.onaccept();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (/^reject/.test(data)) {
|
|
331
|
+
const [, pipe, msg = 'rejected'] = data.split(':');
|
|
332
|
+
const remote = this.remotes.get(pipe);
|
|
333
|
+
if (!remote) {
|
|
334
|
+
console.log(`unknown reject:\n${data}`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
this.remotes.delete(pipe);
|
|
338
|
+
remote.accepted.reject(msg);
|
|
339
|
+
remote.onreject && remote.onreject(msg);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (/^break/.test(data)) {
|
|
343
|
+
const [, pipe, msg = 'breaked'] = data.split(':');
|
|
344
|
+
if (this.pendingReceivers.has(pipe)) {
|
|
345
|
+
this.pendingReceivers.delete(pipe);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const receiver = this.receivers.get(pipe);
|
|
349
|
+
if (!receiver) {
|
|
350
|
+
console.log(`unknown break:\n${data}`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
this.receivers.delete(pipe);
|
|
354
|
+
receiver.onbreak && receiver.onbreak(msg);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (previous) {
|
|
358
|
+
previous.call(this.transport, ev);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
console.log(`unknown invitation:\n${data}`);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const ctx = { data, ports };
|
|
365
|
+
pipeline([
|
|
366
|
+
...Array.from(this.remotes.values()).map((p) => p.callback()),
|
|
367
|
+
...Array.from(this.receivers.values()).map((p) => p.callback()),
|
|
368
|
+
])(ctx, () => {
|
|
369
|
+
if (previous) {
|
|
370
|
+
previous.call(this.transport, ev);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (data.touched) {
|
|
374
|
+
|
|
375
|
+
console.log(`unhandled method: ${data.pipe}::${data.method}\n` +
|
|
376
|
+
`forget to call resolve() or reject() in method implement?`);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
console.log('unknown message', data);
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
fork() {
|
|
384
|
+
return new MojoChannel(this.transport);
|
|
385
|
+
}
|
|
386
|
+
has(pipe) {
|
|
387
|
+
return this.remotes.has(pipe) || this.receivers.has(pipe);
|
|
388
|
+
}
|
|
389
|
+
close() {
|
|
390
|
+
this.remotes.forEach((v) => v.break());
|
|
391
|
+
this.remotes.clear();
|
|
392
|
+
this.receivers.forEach((v) => v.reject());
|
|
393
|
+
this.receivers.clear();
|
|
394
|
+
this.transport.onmessage = null;
|
|
395
|
+
this.transport = null;
|
|
396
|
+
}
|
|
397
|
+
send(msg, transfer) {
|
|
398
|
+
this.transport.postMessage(msg, transfer);
|
|
399
|
+
}
|
|
400
|
+
reject(receiver, msg = '') {
|
|
401
|
+
this.send(`reject:${receiver}:${msg}`);
|
|
402
|
+
this.receivers.delete(`${receiver}`);
|
|
403
|
+
}
|
|
404
|
+
break(remote, msg = '') {
|
|
405
|
+
if (!this.remotes.has(`${remote}`))
|
|
406
|
+
return;
|
|
407
|
+
this.send(`break:${remote}:${msg}`);
|
|
408
|
+
this.remotes.delete(`${remote}`);
|
|
409
|
+
}
|
|
410
|
+
remote(pipe) {
|
|
411
|
+
const remote = new MojoRemote(this, pipe && `${pipe}`);
|
|
412
|
+
this.remotes.set(`${remote}`, remote);
|
|
413
|
+
this.send(`mojo:${remote}`);
|
|
414
|
+
return remote;
|
|
415
|
+
}
|
|
416
|
+
receiver(pipe) {
|
|
417
|
+
if (pipe && this.receivers.has(`${pipe}`)) {
|
|
418
|
+
console.log(`duplicate pipe: ${pipe}`);
|
|
419
|
+
}
|
|
420
|
+
const receiver = new MojoReceiver(this, pipe && `${pipe}`);
|
|
421
|
+
this.receivers.set(`${receiver}`, receiver);
|
|
422
|
+
if (this.pendingReceivers.has(`${receiver}`)) {
|
|
423
|
+
this.send(`accept:${receiver}`);
|
|
424
|
+
this.pendingReceivers.delete(`${receiver}`);
|
|
425
|
+
receiver.onready && receiver.onready();
|
|
426
|
+
}
|
|
427
|
+
return receiver;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const isString = (val) => typeof val === 'string';
|
|
431
|
+
|
|
432
|
+
class AudioFrameBuffer {
|
|
433
|
+
constructor(builder) {
|
|
434
|
+
const { length, numberOfChannels: channels = 1, sampleRate } = builder;
|
|
435
|
+
let { data } = builder;
|
|
436
|
+
this.length = length;
|
|
437
|
+
this.numberOfChannels = channels;
|
|
438
|
+
this.sampleRate = sampleRate;
|
|
439
|
+
this.duration = length / sampleRate;
|
|
440
|
+
if (!data || !data.length || !data[0].length) {
|
|
441
|
+
// AudioBuffer do not store data as a planar sequence
|
|
442
|
+
data = [...Array(channels)].map(() => new Float32Array(length));
|
|
443
|
+
}
|
|
444
|
+
if (data.length < channels) {
|
|
445
|
+
console.warn(`invalid buffer channels ${channels} vs ${data.length}`);
|
|
446
|
+
}
|
|
447
|
+
if (data[0].length < length) {
|
|
448
|
+
console.warn(`invalid buffer length ${length} vs ${data[0].length}`);
|
|
449
|
+
}
|
|
450
|
+
this.data = data;
|
|
451
|
+
}
|
|
452
|
+
// CopyFrom
|
|
453
|
+
static From(buffer) {
|
|
454
|
+
const { sampleRate, numberOfChannels, length } = buffer;
|
|
455
|
+
const data = [...Array(numberOfChannels).keys()].map((ch) => buffer.getChannelData(ch).slice());
|
|
456
|
+
return new AudioFrameBuffer({ length, numberOfChannels, sampleRate, data });
|
|
457
|
+
}
|
|
458
|
+
static Copy(dst, src) {
|
|
459
|
+
const { numberOfChannels: channels } = dst;
|
|
460
|
+
const { numberOfChannels: available } = src;
|
|
461
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
462
|
+
dst.copyToChannel(src.getChannelData(Math.min(ch, available - 1)), ch);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
static Clip(buffer, begin, end) {
|
|
466
|
+
const { sampleRate, numberOfChannels } = buffer;
|
|
467
|
+
const data = AudioFrameBuffer.Data(buffer, begin, end);
|
|
468
|
+
const { length } = data[0];
|
|
469
|
+
return new AudioFrameBuffer({ length, numberOfChannels, sampleRate, data });
|
|
470
|
+
}
|
|
471
|
+
static Data(buffer, begin, end) {
|
|
472
|
+
return [...Array(buffer.numberOfChannels)].map((_, ch) => buffer.getChannelData(ch).subarray(begin, end));
|
|
473
|
+
}
|
|
474
|
+
static Builder(buffer) {
|
|
475
|
+
const { length, numberOfChannels, sampleRate } = buffer;
|
|
476
|
+
const data = AudioFrameBuffer.Data(buffer);
|
|
477
|
+
return {
|
|
478
|
+
length,
|
|
479
|
+
numberOfChannels,
|
|
480
|
+
sampleRate,
|
|
481
|
+
data,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
copyFromChannel(destination, channel = 0, bufferOffset = 0) {
|
|
485
|
+
if (channel >= this.numberOfChannels) {
|
|
486
|
+
console.warn('invalid channel');
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const data = this.getChannelData(channel);
|
|
490
|
+
if (bufferOffset >= data.length) {
|
|
491
|
+
console.warn('invalid offset');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const source = data.subarray(bufferOffset);
|
|
495
|
+
destination.set(source.subarray(0, Math.min(source.length, destination.length)));
|
|
496
|
+
}
|
|
497
|
+
copyToChannel(source, channel = 0, bufferOffset = 0) {
|
|
498
|
+
if (channel >= this.numberOfChannels) {
|
|
499
|
+
console.warn('invalid channel');
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const data = this.getChannelData(channel);
|
|
503
|
+
if (bufferOffset >= this.length) {
|
|
504
|
+
console.warn('invalid offset');
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const length = Math.min(this.length - bufferOffset, source.length);
|
|
508
|
+
const clipped = source.subarray(0, length);
|
|
509
|
+
data.set(clipped, bufferOffset);
|
|
510
|
+
}
|
|
511
|
+
getChannelData(channel) {
|
|
512
|
+
return this.data[channel];
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// export const enum SpeechType {
|
|
517
|
+
var VADActivity;
|
|
518
|
+
(function (VADActivity) {
|
|
519
|
+
VADActivity[VADActivity["kVadActive"] = 0] = "kVadActive";
|
|
520
|
+
VADActivity[VADActivity["kVadPassive"] = 1] = "kVadPassive";
|
|
521
|
+
VADActivity[VADActivity["kVadUnknown"] = 2] = "kVadUnknown";
|
|
522
|
+
})(VADActivity || (VADActivity = {}));
|
|
523
|
+
// export const enum SpeechType {
|
|
524
|
+
var SpeechType;
|
|
525
|
+
(function (SpeechType) {
|
|
526
|
+
SpeechType[SpeechType["kNormalSpeech"] = 0] = "kNormalSpeech";
|
|
527
|
+
SpeechType[SpeechType["kPLC"] = 1] = "kPLC";
|
|
528
|
+
SpeechType[SpeechType["kCNG"] = 2] = "kCNG";
|
|
529
|
+
SpeechType[SpeechType["kPLCCNG"] = 3] = "kPLCCNG";
|
|
530
|
+
SpeechType[SpeechType["kCodecPLC"] = 4] = "kCodecPLC";
|
|
531
|
+
SpeechType[SpeechType["kUndefined"] = 5] = "kUndefined";
|
|
532
|
+
})(SpeechType || (SpeechType = {}));
|
|
533
|
+
class AudioFrame {
|
|
534
|
+
constructor(builder) {
|
|
535
|
+
this.buffer = builder.buffer;
|
|
536
|
+
this.timestamp = builder.timestamp;
|
|
537
|
+
this.elapsedTimeMs = builder.elapsedTimeMs;
|
|
538
|
+
this.ntpTimeMs = builder.ntpTimeMs;
|
|
539
|
+
this.vad = builder.vad || VADActivity.kVadUnknown;
|
|
540
|
+
this.speech = builder.speech || SpeechType.kUndefined;
|
|
541
|
+
}
|
|
542
|
+
get sampleRate() {
|
|
543
|
+
return this.buffer.sampleRate;
|
|
544
|
+
}
|
|
545
|
+
get samples() {
|
|
546
|
+
return this.buffer.length;
|
|
547
|
+
}
|
|
548
|
+
get channels() {
|
|
549
|
+
return this.buffer.numberOfChannels;
|
|
550
|
+
}
|
|
551
|
+
get duration() {
|
|
552
|
+
return this.buffer.duration;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function CopyAudioFrame(output, outputOffset = 0, input, inputOffset = 0, length = input.length) {
|
|
557
|
+
const { numberOfChannels: channels } = output;
|
|
558
|
+
const { numberOfChannels: available } = input;
|
|
559
|
+
for (let channel = 0; channel < channels; channel++) {
|
|
560
|
+
const source = input
|
|
561
|
+
.getChannelData(Math.min(channel, available - 1))
|
|
562
|
+
.subarray(inputOffset, inputOffset + length);
|
|
563
|
+
output.copyToChannel(source, channel, outputOffset);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function InterleaveT(buffer, factory, map = (val) => val) {
|
|
567
|
+
const channels = buffer.length;
|
|
568
|
+
const length = channels * buffer[0].length;
|
|
569
|
+
const output = factory(length);
|
|
570
|
+
for (let channel = 0; channel < channels; channel++) {
|
|
571
|
+
const data = buffer[channel];
|
|
572
|
+
for (let index = 0; index < data.length; index++) {
|
|
573
|
+
output[index * channels + channel] = map(data[index]);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return output;
|
|
577
|
+
}
|
|
578
|
+
function PlanarT(buffer, channels, factory, map = (val) => val) {
|
|
579
|
+
const output = [];
|
|
580
|
+
const length = buffer.length / channels;
|
|
581
|
+
for (let channel = 0; channel < channels; channel++) {
|
|
582
|
+
output[channel] = factory(length, channel);
|
|
583
|
+
}
|
|
584
|
+
for (let index = 0; index < buffer.length; index++) {
|
|
585
|
+
const channel = index % channels;
|
|
586
|
+
const data = output[channel];
|
|
587
|
+
data[Math.floor(index / channels)] = map(buffer[index]);
|
|
588
|
+
}
|
|
589
|
+
return output;
|
|
590
|
+
}
|
|
591
|
+
const DeInterleaveT = PlanarT;
|
|
592
|
+
function Float32ToInt16(val) {
|
|
593
|
+
return Math.min(Math.max((val * 0x7fff) | 0, -0x8000), 0x7fff);
|
|
594
|
+
}
|
|
595
|
+
function Int16ToFloat32(val) {
|
|
596
|
+
return val >= 0x8000 ? -(0x10000 - val) / 0x8000 : val / 0x7fff;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function makeAudioReshaper(wanted, callback, factory) {
|
|
600
|
+
let pending;
|
|
601
|
+
let offset = 0;
|
|
602
|
+
return (input) => {
|
|
603
|
+
const { samples, channels, sampleRate } = input;
|
|
604
|
+
let current = 0;
|
|
605
|
+
while (current < samples) {
|
|
606
|
+
const needed = wanted - offset;
|
|
607
|
+
const available = samples - current;
|
|
608
|
+
const total = Math.min(available, needed);
|
|
609
|
+
if (!needed) {
|
|
610
|
+
callback(pending);
|
|
611
|
+
pending = null;
|
|
612
|
+
}
|
|
613
|
+
if (!pending) {
|
|
614
|
+
pending = factory
|
|
615
|
+
? factory(wanted)
|
|
616
|
+
: new AudioFrame({
|
|
617
|
+
buffer: new AudioFrameBuffer({
|
|
618
|
+
length: wanted,
|
|
619
|
+
numberOfChannels: channels,
|
|
620
|
+
sampleRate,
|
|
621
|
+
}),
|
|
622
|
+
});
|
|
623
|
+
offset = 0;
|
|
624
|
+
}
|
|
625
|
+
CopyAudioFrame(pending.buffer, offset, input.buffer, current, total);
|
|
626
|
+
current += total;
|
|
627
|
+
offset += total;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
class AudioFrameQueue {
|
|
633
|
+
constructor() {
|
|
634
|
+
this.queue = [];
|
|
635
|
+
// total audio samples
|
|
636
|
+
this.total = 0;
|
|
637
|
+
// audio samples offset
|
|
638
|
+
this.offset = 0;
|
|
639
|
+
}
|
|
640
|
+
get length() {
|
|
641
|
+
return this.total;
|
|
642
|
+
}
|
|
643
|
+
clear() {
|
|
644
|
+
this.queue.length = 0;
|
|
645
|
+
this.total = 0;
|
|
646
|
+
this.offset = 0;
|
|
647
|
+
}
|
|
648
|
+
shift() {
|
|
649
|
+
if (!this.total)
|
|
650
|
+
return;
|
|
651
|
+
const frame = this.queue[0];
|
|
652
|
+
const available = frame.samples - this.offset;
|
|
653
|
+
if (this.offset) {
|
|
654
|
+
console.log('maybe unexpected samples');
|
|
655
|
+
}
|
|
656
|
+
return this.pull(available);
|
|
657
|
+
}
|
|
658
|
+
push(frame) {
|
|
659
|
+
const input = (frame) => {
|
|
660
|
+
this.queue.push(frame);
|
|
661
|
+
this.total += frame.samples;
|
|
662
|
+
};
|
|
663
|
+
if (!this.onpush) {
|
|
664
|
+
input(frame);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const { samples, callback, factory, reshape = false } = this.onpush;
|
|
668
|
+
let { reshaper } = this.onpush;
|
|
669
|
+
if (reshape) {
|
|
670
|
+
if (!reshaper) {
|
|
671
|
+
reshaper = makeAudioReshaper(samples, input, factory);
|
|
672
|
+
this.onpush.reshaper = reshaper;
|
|
673
|
+
}
|
|
674
|
+
// reshape & input
|
|
675
|
+
reshaper(frame);
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
input(frame);
|
|
679
|
+
}
|
|
680
|
+
while (this.total - this.offset >= samples) {
|
|
681
|
+
callback(reshape ? this.shift() : this.pull(samples, factory && factory(samples)));
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
pull(wanted, output) {
|
|
685
|
+
if (wanted <= 0) {
|
|
686
|
+
wanted < 0 && console.warn(`invalid samples: ${wanted}`);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (this.onpull) {
|
|
690
|
+
const { samples, callback, eager } = this.onpull;
|
|
691
|
+
while (this.total < Math.max(samples, wanted) * (eager ? 2 : 1)) {
|
|
692
|
+
const frame = callback(this.total, wanted);
|
|
693
|
+
if (!frame) {
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
this.queue.push(frame);
|
|
697
|
+
this.total += frame.samples;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (this.total < wanted) {
|
|
701
|
+
console.warn('audio frame drained');
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
let current = 0;
|
|
705
|
+
let { offset } = this;
|
|
706
|
+
while (current < wanted) {
|
|
707
|
+
const frame = this.queue.shift();
|
|
708
|
+
const available = frame.samples - offset;
|
|
709
|
+
const needed = wanted - current;
|
|
710
|
+
const length = Math.min(available, needed);
|
|
711
|
+
if (!available) {
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
// clip buffer w/o copy
|
|
715
|
+
if (!output && !current && available >= needed) {
|
|
716
|
+
const buffer = AudioFrameBuffer.Clip(frame.buffer, offset, offset + needed);
|
|
717
|
+
output = new AudioFrame({ buffer });
|
|
718
|
+
if (available > needed) {
|
|
719
|
+
offset += length;
|
|
720
|
+
this.queue.unshift(frame);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
offset = 0;
|
|
724
|
+
}
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
// copy needed or available
|
|
728
|
+
if (!output) {
|
|
729
|
+
const buffer = new AudioFrameBuffer({
|
|
730
|
+
length: wanted,
|
|
731
|
+
numberOfChannels: frame.channels,
|
|
732
|
+
sampleRate: frame.sampleRate,
|
|
733
|
+
});
|
|
734
|
+
output = new AudioFrame({ buffer });
|
|
735
|
+
}
|
|
736
|
+
if (output.samples < wanted) {
|
|
737
|
+
console.warn('invalid frame buffer length');
|
|
738
|
+
}
|
|
739
|
+
CopyAudioFrame(output.buffer, current, frame.buffer, offset, length);
|
|
740
|
+
current += length;
|
|
741
|
+
if (available > needed) {
|
|
742
|
+
offset += length;
|
|
743
|
+
this.queue.unshift(frame);
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
offset = 0;
|
|
747
|
+
if (this.onpull) {
|
|
748
|
+
// optional chaining
|
|
749
|
+
// this.onpull.drain?.(frame);
|
|
750
|
+
const { drain } = this.onpull;
|
|
751
|
+
drain && drain(frame);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
this.total -= wanted;
|
|
755
|
+
this.offset = offset;
|
|
756
|
+
return output;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/// <reference types="emscripten" />
|
|
761
|
+
const DEFAULT_HEAP_TYPE = 'HEAPU8';
|
|
762
|
+
function createMemoryAllocator(native, type) {
|
|
763
|
+
return (length) => {
|
|
764
|
+
const { BYTES_PER_ELEMENT } = native[type || DEFAULT_HEAP_TYPE];
|
|
765
|
+
const bytes = length * BYTES_PER_ELEMENT;
|
|
766
|
+
const pointer = native._malloc(bytes);
|
|
767
|
+
const tuple = () => [pointer, bytes];
|
|
768
|
+
// malloc() might cause wasm memory growth, which will detach 'heap' buffer
|
|
769
|
+
const view = (normalize = true) => {
|
|
770
|
+
const v = native[type || DEFAULT_HEAP_TYPE].subarray(pointer / BYTES_PER_ELEMENT, pointer + length);
|
|
771
|
+
if (normalize && typeof v.constructor !== 'undefined') {
|
|
772
|
+
return new v.constructor(v.buffer, v.byteOffset, length);
|
|
773
|
+
}
|
|
774
|
+
console.log('unnormalized array view has unexpected length');
|
|
775
|
+
return v;
|
|
776
|
+
};
|
|
777
|
+
const free = () => native._free(pointer);
|
|
778
|
+
return {
|
|
779
|
+
BYTES_PER_ELEMENT,
|
|
780
|
+
pointer,
|
|
781
|
+
length,
|
|
782
|
+
bytes,
|
|
783
|
+
tuple,
|
|
784
|
+
view,
|
|
785
|
+
free,
|
|
786
|
+
};
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
function createMemoryViewer(native, type) {
|
|
790
|
+
return (begin, length, normalize = true) => {
|
|
791
|
+
const heap = native[type || DEFAULT_HEAP_TYPE];
|
|
792
|
+
const { BYTES_PER_ELEMENT } = heap;
|
|
793
|
+
const view = heap.subarray(begin / BYTES_PER_ELEMENT, begin + length);
|
|
794
|
+
if (normalize && typeof view.constructor !== 'undefined') {
|
|
795
|
+
return new view.constructor(view.buffer, view.byteOffset, length);
|
|
796
|
+
}
|
|
797
|
+
console.log('unnormalized array view has unexpected length');
|
|
798
|
+
return view;
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function open(name, store) {
|
|
803
|
+
const request = indexedDB.open(name);
|
|
804
|
+
request.onupgradeneeded = () => request.result.createObjectStore(store);
|
|
805
|
+
const promise = make(request);
|
|
806
|
+
return (mode, callback) => promise.then((db) => callback(db.transaction(store, mode).objectStore(store)));
|
|
807
|
+
}
|
|
808
|
+
const LAZY_INSTANCE = (factory) => {
|
|
809
|
+
let instance;
|
|
810
|
+
return (...args) => {
|
|
811
|
+
if (!instance) {
|
|
812
|
+
instance = factory();
|
|
813
|
+
}
|
|
814
|
+
return instance(...args);
|
|
815
|
+
};
|
|
816
|
+
};
|
|
817
|
+
const LAZY_GLOBAL_STORE = LAZY_INSTANCE(() => open('LINE-UI', 'key-value'));
|
|
818
|
+
function get(key, store = LAZY_GLOBAL_STORE) {
|
|
819
|
+
return store('readonly', (store) => make(store.get(key)));
|
|
820
|
+
}
|
|
821
|
+
function clear(store = LAZY_GLOBAL_STORE) {
|
|
822
|
+
return store('readwrite', (store) => {
|
|
823
|
+
store.clear();
|
|
824
|
+
return make(store.transaction);
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
function make(request) {
|
|
828
|
+
return new Promise((resolve, reject) => {
|
|
829
|
+
// @ts-ignore - file size hacks
|
|
830
|
+
request.oncomplete = request.onsuccess = () => resolve(request.result);
|
|
831
|
+
// @ts-ignore - file size hacks
|
|
832
|
+
request.onabort = request.onerror = () => reject(request.error);
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/// <reference types="emscripten" />
|
|
837
|
+
var IPVPEvents;
|
|
838
|
+
(function (IPVPEvents) {
|
|
839
|
+
IPVPEvents[IPVPEvents["kRTPDead"] = 1] = "kRTPDead";
|
|
840
|
+
IPVPEvents[IPVPEvents["kGainCtrl"] = 2] = "kGainCtrl";
|
|
841
|
+
IPVPEvents[IPVPEvents["kCSRCChange"] = 3] = "kCSRCChange";
|
|
842
|
+
IPVPEvents[IPVPEvents["kCCIDChange"] = 4] = "kCCIDChange";
|
|
843
|
+
IPVPEvents[IPVPEvents["kMicFramingDelay"] = 5] = "kMicFramingDelay";
|
|
844
|
+
IPVPEvents[IPVPEvents["kSpkFramengDelay"] = 6] = "kSpkFramengDelay";
|
|
845
|
+
IPVPEvents[IPVPEvents["kRTPViolate"] = 7] = "kRTPViolate";
|
|
846
|
+
IPVPEvents[IPVPEvents["kSignalBreak"] = 8] = "kSignalBreak";
|
|
847
|
+
})(IPVPEvents || (IPVPEvents = {}));
|
|
848
|
+
var IPVPConfigAEC;
|
|
849
|
+
(function (IPVPConfigAEC) {
|
|
850
|
+
IPVPConfigAEC[IPVPConfigAEC["kOff"] = 0] = "kOff";
|
|
851
|
+
IPVPConfigAEC[IPVPConfigAEC["kNormal"] = 1] = "kNormal";
|
|
852
|
+
IPVPConfigAEC[IPVPConfigAEC["kEnhance"] = 2] = "kEnhance";
|
|
853
|
+
})(IPVPConfigAEC || (IPVPConfigAEC = {}));
|
|
854
|
+
var IPVPConfigANS;
|
|
855
|
+
(function (IPVPConfigANS) {
|
|
856
|
+
IPVPConfigANS[IPVPConfigANS["kOff"] = 0] = "kOff";
|
|
857
|
+
IPVPConfigANS[IPVPConfigANS["kLow"] = 1] = "kLow";
|
|
858
|
+
IPVPConfigANS[IPVPConfigANS["kMiddle"] = 2] = "kMiddle";
|
|
859
|
+
IPVPConfigANS[IPVPConfigANS["kHigh"] = 3] = "kHigh";
|
|
860
|
+
})(IPVPConfigANS || (IPVPConfigANS = {}));
|
|
861
|
+
var IPVPConfigAGCMode;
|
|
862
|
+
(function (IPVPConfigAGCMode) {
|
|
863
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kAdaptiveDigital"] = 1] = "kAdaptiveDigital";
|
|
864
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kAdaptiveAnalog"] = 2] = "kAdaptiveAnalog";
|
|
865
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kFixedDigital"] = 4] = "kFixedDigital";
|
|
866
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kUnchanged"] = 8] = "kUnchanged";
|
|
867
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kCustomTable"] = 16] = "kCustomTable";
|
|
868
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kDigitalAgcAdj"] = 32] = "kDigitalAgcAdj";
|
|
869
|
+
IPVPConfigAGCMode[IPVPConfigAGCMode["kVadThresholdAdj"] = 64] = "kVadThresholdAdj";
|
|
870
|
+
})(IPVPConfigAGCMode || (IPVPConfigAGCMode = {}));
|
|
871
|
+
var IPVPDataType;
|
|
872
|
+
(function (IPVPDataType) {
|
|
873
|
+
IPVPDataType[IPVPDataType["kInvalid"] = -1] = "kInvalid";
|
|
874
|
+
IPVPDataType[IPVPDataType["kSpeech"] = 0] = "kSpeech";
|
|
875
|
+
IPVPDataType[IPVPDataType["kPCM"] = 1] = "kPCM";
|
|
876
|
+
IPVPDataType[IPVPDataType["kRTP"] = 2] = "kRTP";
|
|
877
|
+
IPVPDataType[IPVPDataType["kRTCP"] = 3] = "kRTCP";
|
|
878
|
+
IPVPDataType[IPVPDataType["kRFC2833"] = 4] = "kRFC2833";
|
|
879
|
+
IPVPDataType[IPVPDataType["kCNG"] = 5] = "kCNG";
|
|
880
|
+
IPVPDataType[IPVPDataType["kAVS"] = 6] = "kAVS";
|
|
881
|
+
IPVPDataType[IPVPDataType["kRED"] = 7] = "kRED";
|
|
882
|
+
IPVPDataType[IPVPDataType["kVQM"] = 8] = "kVQM";
|
|
883
|
+
IPVPDataType[IPVPDataType["kSDES"] = 9] = "kSDES";
|
|
884
|
+
IPVPDataType[IPVPDataType["kFEC"] = 10] = "kFEC";
|
|
885
|
+
})(IPVPDataType || (IPVPDataType = {}));
|
|
886
|
+
var IPVPTalkFlag;
|
|
887
|
+
(function (IPVPTalkFlag) {
|
|
888
|
+
IPVPTalkFlag[IPVPTalkFlag["kInactive"] = 0] = "kInactive";
|
|
889
|
+
IPVPTalkFlag[IPVPTalkFlag["kRecvOnly"] = 1] = "kRecvOnly";
|
|
890
|
+
IPVPTalkFlag[IPVPTalkFlag["kSendOnly"] = 2] = "kSendOnly";
|
|
891
|
+
IPVPTalkFlag[IPVPTalkFlag["kSendRecv"] = 3] = "kSendRecv";
|
|
892
|
+
IPVPTalkFlag[IPVPTalkFlag["kSendEvnt"] = 4] = "kSendEvnt";
|
|
893
|
+
IPVPTalkFlag[IPVPTalkFlag["kPaging"] = 8] = "kPaging";
|
|
894
|
+
IPVPTalkFlag[IPVPTalkFlag["kMax"] = 15] = "kMax";
|
|
895
|
+
})(IPVPTalkFlag || (IPVPTalkFlag = {}));
|
|
896
|
+
var IPVPSRTPCryptoExtern;
|
|
897
|
+
(function (IPVPSRTPCryptoExtern) {
|
|
898
|
+
IPVPSRTPCryptoExtern[IPVPSRTPCryptoExtern["kUnencryRTP"] = 1] = "kUnencryRTP";
|
|
899
|
+
IPVPSRTPCryptoExtern[IPVPSRTPCryptoExtern["kUnencryRTCP"] = 2] = "kUnencryRTCP";
|
|
900
|
+
IPVPSRTPCryptoExtern[IPVPSRTPCryptoExtern["kUnautoRTP"] = 4] = "kUnautoRTP";
|
|
901
|
+
})(IPVPSRTPCryptoExtern || (IPVPSRTPCryptoExtern = {}));
|
|
902
|
+
var IPVPCodecOpts;
|
|
903
|
+
(function (IPVPCodecOpts) {
|
|
904
|
+
IPVPCodecOpts[IPVPCodecOpts["kAmrWbIO"] = 1] = "kAmrWbIO";
|
|
905
|
+
IPVPCodecOpts[IPVPCodecOpts["kHFOnly"] = 2] = "kHFOnly";
|
|
906
|
+
IPVPCodecOpts[IPVPCodecOpts["kDTXSend"] = 4] = "kDTXSend";
|
|
907
|
+
IPVPCodecOpts[IPVPCodecOpts["kDTXRecv"] = 8] = "kDTXRecv";
|
|
908
|
+
IPVPCodecOpts[IPVPCodecOpts["kStero"] = 16] = "kStero";
|
|
909
|
+
IPVPCodecOpts[IPVPCodecOpts["kInbandFEC"] = 32] = "kInbandFEC";
|
|
910
|
+
IPVPCodecOpts[IPVPCodecOpts["kUseYLRTX"] = 64] = "kUseYLRTX";
|
|
911
|
+
})(IPVPCodecOpts || (IPVPCodecOpts = {}));
|
|
912
|
+
var IPVPFECFormat;
|
|
913
|
+
(function (IPVPFECFormat) {
|
|
914
|
+
IPVPFECFormat[IPVPFECFormat["kURL"] = 0] = "kURL";
|
|
915
|
+
IPVPFECFormat[IPVPFECFormat["kFLEX"] = 1] = "kFLEX";
|
|
916
|
+
})(IPVPFECFormat || (IPVPFECFormat = {}));
|
|
917
|
+
var IPVPGainDir;
|
|
918
|
+
(function (IPVPGainDir) {
|
|
919
|
+
IPVPGainDir[IPVPGainDir["kNone"] = 0] = "kNone";
|
|
920
|
+
IPVPGainDir[IPVPGainDir["kRecv"] = 1] = "kRecv";
|
|
921
|
+
IPVPGainDir[IPVPGainDir["kSend"] = 2] = "kSend";
|
|
922
|
+
})(IPVPGainDir || (IPVPGainDir = {}));
|
|
923
|
+
var IPVPFlowDir;
|
|
924
|
+
(function (IPVPFlowDir) {
|
|
925
|
+
IPVPFlowDir[IPVPFlowDir["kNone"] = 0] = "kNone";
|
|
926
|
+
IPVPFlowDir[IPVPFlowDir["kLocal"] = 1] = "kLocal";
|
|
927
|
+
IPVPFlowDir[IPVPFlowDir["kNet"] = 2] = "kNet";
|
|
928
|
+
IPVPFlowDir[IPVPFlowDir["kMix"] = 4] = "kMix";
|
|
929
|
+
IPVPFlowDir[IPVPFlowDir["kAll"] = 3] = "kAll";
|
|
930
|
+
})(IPVPFlowDir || (IPVPFlowDir = {}));
|
|
931
|
+
|
|
932
|
+
let native;
|
|
933
|
+
let alloc;
|
|
934
|
+
let alloc16;
|
|
935
|
+
let view;
|
|
936
|
+
let view32;
|
|
937
|
+
let loglevel = 1;
|
|
938
|
+
let callid; // aka encoder id
|
|
939
|
+
const decoders = [];
|
|
940
|
+
const decoderGains = [-1, -1];
|
|
941
|
+
function hasDecoder(ccid) {
|
|
942
|
+
return decoders.findIndex((v) => v && v.has(ccid));
|
|
943
|
+
}
|
|
944
|
+
function addDecoder(ccid, track) {
|
|
945
|
+
if (!decoders[track]) {
|
|
946
|
+
decoders[track] = new Set();
|
|
947
|
+
}
|
|
948
|
+
decoders[track].add(ccid);
|
|
949
|
+
}
|
|
950
|
+
function removeDecoder(ccid) {
|
|
951
|
+
let channel = -1;
|
|
952
|
+
decoders.forEach((v, i) => {
|
|
953
|
+
if (!v)
|
|
954
|
+
return;
|
|
955
|
+
if (v.has(ccid)) {
|
|
956
|
+
channel = i;
|
|
957
|
+
}
|
|
958
|
+
v.delete(ccid);
|
|
959
|
+
});
|
|
960
|
+
return channel;
|
|
961
|
+
}
|
|
962
|
+
function moveDecoder(ccid, track) {
|
|
963
|
+
removeDecoder(ccid);
|
|
964
|
+
addDecoder(ccid, track);
|
|
965
|
+
}
|
|
966
|
+
function decoderList(from = 0, to) {
|
|
967
|
+
return decoders.slice(from, to).reduce((p, v) => p.concat(Array.from(v)), []);
|
|
968
|
+
}
|
|
969
|
+
function isMainTrack(track) {
|
|
970
|
+
return track < 11;
|
|
971
|
+
}
|
|
972
|
+
function trackType(track) {
|
|
973
|
+
return isMainTrack(track) ? 0 /* kMain */ : 1 /* kOther */;
|
|
974
|
+
}
|
|
975
|
+
let eventR;
|
|
976
|
+
let memory;
|
|
977
|
+
let inputBuffer;
|
|
978
|
+
let outputBuffer;
|
|
979
|
+
let refBuffer;
|
|
980
|
+
let push;
|
|
981
|
+
let push_ref;
|
|
982
|
+
let pull;
|
|
983
|
+
let drop;
|
|
984
|
+
let vtuner_reply;
|
|
985
|
+
const isWechatWorker = typeof worker !== 'undefined';
|
|
986
|
+
if (isWechatWorker) {
|
|
987
|
+
worker.onMessage((data) => {
|
|
988
|
+
worker.onmessage && worker.onmessage({ data });
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
const cleanup = [];
|
|
992
|
+
// eslint-disable-next-line no-restricted-globals
|
|
993
|
+
const channel = new MojoChannel(isWechatWorker ? worker : self);
|
|
994
|
+
const engine = channel.receiver('audio-engine-host');
|
|
995
|
+
engine.implement('init', (reply, [url, transportPR, eventPR, verbose = false]) => __awaiter(void 0, void 0, void 0, function* () {
|
|
996
|
+
let factory;
|
|
997
|
+
let wasmUrl;
|
|
998
|
+
if (isWechatWorker) {
|
|
999
|
+
const trailingSlash = (v) => (!v || v.endsWith('/') ? v : `${v}/`);
|
|
1000
|
+
const filename = (path) => path.substring(path.lastIndexOf('/') + 1);
|
|
1001
|
+
const [scriptUrl, wasmDir = ''] = url.split('?');
|
|
1002
|
+
const scriptName = filename(scriptUrl);
|
|
1003
|
+
factory = require(scriptUrl);
|
|
1004
|
+
wasmUrl = trailingSlash(wasmDir) + scriptName.replace(/\.js$/, '.wasm');
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
importScripts(url);
|
|
1008
|
+
factory = LIBIPVP;
|
|
1009
|
+
wasmUrl = url.replace(/\.js$/, '.wasm');
|
|
1010
|
+
}
|
|
1011
|
+
const wasmBinary = isWechatWorker
|
|
1012
|
+
? wasmUrl
|
|
1013
|
+
: yield get(wasmUrl).catch(() => null);
|
|
1014
|
+
// should clear cache if init failed
|
|
1015
|
+
native = yield factory({ wasmBinary }).catch((error) => {
|
|
1016
|
+
clear();
|
|
1017
|
+
throw error;
|
|
1018
|
+
});
|
|
1019
|
+
alloc = createMemoryAllocator(native);
|
|
1020
|
+
alloc16 = createMemoryAllocator(native, 'HEAPU16');
|
|
1021
|
+
view = createMemoryViewer(native);
|
|
1022
|
+
view32 = createMemoryViewer(native, 'HEAPU32');
|
|
1023
|
+
console.log(`IPVP: ${native.ipvp_get_version()} - ${native.ipvp_get_build()}`);
|
|
1024
|
+
eventR = channel.remote(eventPR);
|
|
1025
|
+
const transportR = channel.remote(transportPR);
|
|
1026
|
+
cleanup.unshift(() => {
|
|
1027
|
+
transportR.break();
|
|
1028
|
+
eventR.break();
|
|
1029
|
+
eventR = null;
|
|
1030
|
+
});
|
|
1031
|
+
native.ipvp_init((level, msg) => {
|
|
1032
|
+
if (verbose || (level <= loglevel && true)) {
|
|
1033
|
+
console.log(msg);
|
|
1034
|
+
}
|
|
1035
|
+
}, (event, wparam, lparam, data, length) => {
|
|
1036
|
+
if (event === IPVPEvents.kRTPDead) {
|
|
1037
|
+
if (hasDecoder(wparam)) {
|
|
1038
|
+
native.ipvp_decode_stop(wparam);
|
|
1039
|
+
const channel = removeDecoder(wparam);
|
|
1040
|
+
eventR && eventR.make('decodestop').post(wparam, channel);
|
|
1041
|
+
}
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
if (event === IPVPEvents.kGainCtrl) {
|
|
1045
|
+
console.log(`gain control: ${lparam}db`);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
if (event === IPVPEvents.kCSRCChange) {
|
|
1049
|
+
const speaking = [];
|
|
1050
|
+
if (data && lparam) {
|
|
1051
|
+
// [int32_t, int32_t][]
|
|
1052
|
+
speaking.push(...view32(data, lparam).filter((_, index) => index % 2 === 0));
|
|
1053
|
+
}
|
|
1054
|
+
eventR.make('speaking').post(callid, speaking);
|
|
1055
|
+
maybeUpdateDecodeGain(speaking);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
if (event === IPVPEvents.kCCIDChange) {
|
|
1059
|
+
const speaking = [];
|
|
1060
|
+
if (data && lparam) {
|
|
1061
|
+
speaking.push(...view32(data, lparam));
|
|
1062
|
+
}
|
|
1063
|
+
eventR.make('focusing').post(callid, speaking);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
console.log(`ipvp event: ${IPVPEvents[event]}`);
|
|
1067
|
+
}, (callid, data, length) => {
|
|
1068
|
+
const memory = new Uint8Array(view(data, length));
|
|
1069
|
+
transportR.make('send').transfer(memory.buffer).post(callid, memory);
|
|
1070
|
+
}, (data, length) => {
|
|
1071
|
+
vtuner_reply && vtuner_reply(data, length);
|
|
1072
|
+
});
|
|
1073
|
+
// native.ipvp_config_jitter(1, 0, 0, 60);
|
|
1074
|
+
reply.resolve();
|
|
1075
|
+
}));
|
|
1076
|
+
engine.implement('exit', (reply) => {
|
|
1077
|
+
cleanup.forEach((v) => v());
|
|
1078
|
+
cleanup.length = 0;
|
|
1079
|
+
native.ipvp_exit();
|
|
1080
|
+
reply.resolve();
|
|
1081
|
+
});
|
|
1082
|
+
engine.implement('debug', (reply, [level]) => {
|
|
1083
|
+
loglevel = level;
|
|
1084
|
+
reply.resolve();
|
|
1085
|
+
});
|
|
1086
|
+
engine.implement('mos', (reply, [lossrate, rtt, realtime = false]) => {
|
|
1087
|
+
// range[0, 100] to range[0, 10]
|
|
1088
|
+
reply.resolve(native.ipvp_mos(lossrate, rtt, realtime) / 10);
|
|
1089
|
+
});
|
|
1090
|
+
engine.implement('aec', (reply, [level]) => {
|
|
1091
|
+
console.log(`IPVP AEC: ${level}`);
|
|
1092
|
+
native.ipvp_config_aec(level);
|
|
1093
|
+
reply.resolve();
|
|
1094
|
+
});
|
|
1095
|
+
engine.implement('agc', (reply, [enable]) => {
|
|
1096
|
+
console.log(`IPVP AGC: ${enable}`);
|
|
1097
|
+
native.ipvp_config_agc(enable);
|
|
1098
|
+
reply.resolve();
|
|
1099
|
+
});
|
|
1100
|
+
engine.implement('ans', (reply, [ans, tns = ans]) => {
|
|
1101
|
+
console.log(`IPVP ANS: ${ans}, ${tns}`);
|
|
1102
|
+
native.ipvp_config_ans(tns, ans);
|
|
1103
|
+
reply.resolve();
|
|
1104
|
+
});
|
|
1105
|
+
engine.implement('mute', (reply, [enable]) => {
|
|
1106
|
+
console.log(`IPVP mute: ${enable}`);
|
|
1107
|
+
native.ipvp_set_mute(Number(enable));
|
|
1108
|
+
reply.resolve();
|
|
1109
|
+
});
|
|
1110
|
+
engine.implement('bypass', (reply, [enable]) => {
|
|
1111
|
+
if (enable) {
|
|
1112
|
+
native.ipvp_config_aec(IPVPConfigAEC.kOff);
|
|
1113
|
+
native.ipvp_config_ans(IPVPConfigANS.kOff, IPVPConfigANS.kOff);
|
|
1114
|
+
native.ipvp_set_bypass(1);
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
native.ipvp_config_aec(IPVPConfigAEC.kNormal);
|
|
1118
|
+
native.ipvp_config_ans(IPVPConfigANS.kLow, IPVPConfigANS.kLow);
|
|
1119
|
+
native.ipvp_set_bypass(0);
|
|
1120
|
+
}
|
|
1121
|
+
reply.resolve();
|
|
1122
|
+
});
|
|
1123
|
+
engine.implement('dtmf', (reply, [tones]) => {
|
|
1124
|
+
native.ipvp_dtmf_send(callid, tones, IPVPFlowDir.kNet);
|
|
1125
|
+
reply.resolve();
|
|
1126
|
+
});
|
|
1127
|
+
engine.implement('sendstats', (reply) => {
|
|
1128
|
+
reply.resolve(native.ipvp_get_astats_tx(callid));
|
|
1129
|
+
});
|
|
1130
|
+
engine.implement('recvstats', (reply, [ccid]) => {
|
|
1131
|
+
if (ccid !== -1) {
|
|
1132
|
+
reply.resolve(native.ipvp_get_astats_rx(ccid));
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
reply.resolve(decoderList().map((id) => native.ipvp_get_astats_rx(id)));
|
|
1136
|
+
// const counts = decoders.size;
|
|
1137
|
+
// const stats = Array.from(decoders)
|
|
1138
|
+
// .map((id) => native.ipvp_get_astats_rx(id))
|
|
1139
|
+
// .reduce((stats, val, index) => {
|
|
1140
|
+
// if (!index) {
|
|
1141
|
+
// val.mos_conver /= counts;
|
|
1142
|
+
// val.mos_listen /= counts;
|
|
1143
|
+
// val.mos_network /= counts;
|
|
1144
|
+
// return val;
|
|
1145
|
+
// }
|
|
1146
|
+
// stats.mos_conver += val.mos_conver / counts;
|
|
1147
|
+
// stats.mos_listen += val.mos_listen / counts;
|
|
1148
|
+
// stats.mos_network += val.mos_network / counts;
|
|
1149
|
+
// return stats;
|
|
1150
|
+
// }, {} as IPVPAudioStatsRX);
|
|
1151
|
+
// reply.resolve(stats);
|
|
1152
|
+
});
|
|
1153
|
+
engine.implement('bandwidth', (reply) => {
|
|
1154
|
+
reply.resolve(native.ipvp_get_bandwidth());
|
|
1155
|
+
});
|
|
1156
|
+
engine.implement('jitter', (reply) => {
|
|
1157
|
+
reply.resolve(native.ipvp_get_jitter(callid));
|
|
1158
|
+
});
|
|
1159
|
+
engine.implement('sendstatsadv', (reply) => {
|
|
1160
|
+
reply.resolve(native.ipvp_get_astats_tx_advance(callid));
|
|
1161
|
+
});
|
|
1162
|
+
engine.implement('recvstatsadv', (reply, [ccid]) => {
|
|
1163
|
+
if (ccid !== -1) {
|
|
1164
|
+
reply.resolve(native.ipvp_get_astats_rx_advance(ccid));
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
reply.resolve(decoderList().map((id) => native.ipvp_get_astats_rx_advance(id)));
|
|
1168
|
+
});
|
|
1169
|
+
engine.implement('report', (reply) => {
|
|
1170
|
+
reply.resolve(native.ipvp_get_astats_report());
|
|
1171
|
+
});
|
|
1172
|
+
engine.implement('micschedule', (reply) => {
|
|
1173
|
+
reply.resolve(native.ipvp_get_mic_schedule());
|
|
1174
|
+
});
|
|
1175
|
+
engine.implement('spkschedule', (reply) => {
|
|
1176
|
+
reply.resolve(native.ipvp_get_spk_schedule());
|
|
1177
|
+
});
|
|
1178
|
+
engine.implement('process', (reply, [builder]) => {
|
|
1179
|
+
if (!push) {
|
|
1180
|
+
console.log('unexpected - process before setup');
|
|
1181
|
+
reply.reject();
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
push(builder);
|
|
1185
|
+
const output = pull(builder.length);
|
|
1186
|
+
if (!output) {
|
|
1187
|
+
reply.resolve();
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
|
|
1191
|
+
});
|
|
1192
|
+
engine.implement('processdelay', (reply, [samples]) => {
|
|
1193
|
+
if (!drop) {
|
|
1194
|
+
console.log('unexpected - processdelay before setup');
|
|
1195
|
+
reply.reject();
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
drop(samples);
|
|
1199
|
+
reply.resolve();
|
|
1200
|
+
});
|
|
1201
|
+
engine.implement('push', (reply, [builder]) => {
|
|
1202
|
+
if (!push) {
|
|
1203
|
+
console.log('unexpected - process before setup');
|
|
1204
|
+
reply.reject();
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
push(builder);
|
|
1208
|
+
reply.resolve();
|
|
1209
|
+
});
|
|
1210
|
+
engine.implement('pushref', (reply, [builder]) => {
|
|
1211
|
+
if (!push_ref) {
|
|
1212
|
+
console.log('unexpected - process before setup');
|
|
1213
|
+
reply.reject();
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
push_ref(builder);
|
|
1217
|
+
reply.resolve();
|
|
1218
|
+
});
|
|
1219
|
+
engine.implement('pull', (reply, [samples]) => {
|
|
1220
|
+
const output = pull(samples);
|
|
1221
|
+
if (!output) {
|
|
1222
|
+
reply.resolve();
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
|
|
1226
|
+
});
|
|
1227
|
+
engine.implement('pulldelay', (reply, [samples]) => {
|
|
1228
|
+
if (!drop) {
|
|
1229
|
+
console.log('unexpected - processdelay before setup');
|
|
1230
|
+
reply.reject();
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
drop(samples);
|
|
1234
|
+
reply.resolve();
|
|
1235
|
+
});
|
|
1236
|
+
engine.implement('receive', (reply, [ccid, data, track = 0]) => {
|
|
1237
|
+
if (hasDecoder(ccid) === -1) {
|
|
1238
|
+
// CHECK RTP EXT - M bit
|
|
1239
|
+
// if (!(data[17] & 0x40)) {
|
|
1240
|
+
// }
|
|
1241
|
+
// CHECK RTP EXT - energy
|
|
1242
|
+
if (!data[18] && !data[19]) {
|
|
1243
|
+
reply.resolve();
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
const result = native.ipvp_decode_start(ccid, ccid);
|
|
1247
|
+
if (result !== 0) {
|
|
1248
|
+
console.log(`audio decoder failed: ${result}`);
|
|
1249
|
+
}
|
|
1250
|
+
if (result > 0) {
|
|
1251
|
+
console.log(`trying to release audio decoder: ${result}`);
|
|
1252
|
+
const track = removeDecoder(result);
|
|
1253
|
+
native.ipvp_decode_stop(result);
|
|
1254
|
+
native.ipvp_decode_start(ccid, ccid);
|
|
1255
|
+
eventR && eventR.make('decodestop').post(result, track);
|
|
1256
|
+
}
|
|
1257
|
+
addDecoder(ccid, track);
|
|
1258
|
+
eventR && eventR.make('decodestart').post(ccid, track);
|
|
1259
|
+
// gain override
|
|
1260
|
+
const gain = decoderGains[trackType(track)];
|
|
1261
|
+
if (gain >= 0) {
|
|
1262
|
+
native.ipvp_decode_gain([[ccid, gain]]);
|
|
1263
|
+
eventR && eventR.make('gainchange').post(gain, track);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
else if (!decoders[track] || !decoders[track].has(ccid)) {
|
|
1267
|
+
moveDecoder(ccid, track);
|
|
1268
|
+
// gain override
|
|
1269
|
+
const gain = decoderGains[trackType(track)];
|
|
1270
|
+
if (gain >= 0) {
|
|
1271
|
+
native.ipvp_decode_gain([[ccid, gain]]);
|
|
1272
|
+
eventR && eventR.make('gainchange').post(gain, track);
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
const gain = isMainTrack(track) && CURRENT_SUPPRESSION ? 20 : 100;
|
|
1276
|
+
native.ipvp_decode_gain([[ccid, gain]]);
|
|
1277
|
+
eventR && eventR.make('gainchange').post(gain, track);
|
|
1278
|
+
}
|
|
1279
|
+
maybeUpdateDecodeGain();
|
|
1280
|
+
}
|
|
1281
|
+
const memory = alloc(data.length);
|
|
1282
|
+
memory.view().set(data);
|
|
1283
|
+
native.ipvp_net_data(ccid, memory.pointer, memory.bytes, IPVPDataType.kRTP);
|
|
1284
|
+
memory.free();
|
|
1285
|
+
reply.resolve();
|
|
1286
|
+
});
|
|
1287
|
+
engine.implement('setup', (reply, [ccid, samplerate, type = 3 /* kSendRecv */], [port]) => {
|
|
1288
|
+
callid = ccid;
|
|
1289
|
+
if (type & 1 /* kSendOnly */) {
|
|
1290
|
+
native.ipvp_encode_start(ccid, ccid);
|
|
1291
|
+
cleanup.unshift(() => native.ipvp_encode_stop(ccid));
|
|
1292
|
+
}
|
|
1293
|
+
if (type & 2 /* kRecvOnly */) {
|
|
1294
|
+
cleanup.unshift(() => decoderList().forEach((id) => native.ipvp_decode_stop(id)));
|
|
1295
|
+
}
|
|
1296
|
+
const channels = 1;
|
|
1297
|
+
const samples = samplerate / 100;
|
|
1298
|
+
memory = alloc16(samples * channels);
|
|
1299
|
+
inputBuffer = new AudioFrameQueue();
|
|
1300
|
+
outputBuffer = new AudioFrameQueue();
|
|
1301
|
+
refBuffer = new AudioFrameQueue();
|
|
1302
|
+
cleanup.unshift(() => memory.free());
|
|
1303
|
+
console.log(`config - channel: ${channels}, samplerate: ${samplerate}`);
|
|
1304
|
+
native.ipvp_dev_config(samplerate, channels);
|
|
1305
|
+
inputBuffer.onpush = {
|
|
1306
|
+
samples,
|
|
1307
|
+
callback: (frame) => {
|
|
1308
|
+
const { buffer } = frame;
|
|
1309
|
+
// always use first channel
|
|
1310
|
+
const data = [...Array(1)].map((_, ch) => buffer.getChannelData(ch));
|
|
1311
|
+
InterleaveT(data, () => memory.view(), Float32ToInt16);
|
|
1312
|
+
native.ipvp_mic_framing(memory.pointer, memory.bytes);
|
|
1313
|
+
},
|
|
1314
|
+
};
|
|
1315
|
+
refBuffer.onpush = {
|
|
1316
|
+
samples,
|
|
1317
|
+
callback: (frame) => {
|
|
1318
|
+
const { buffer } = frame;
|
|
1319
|
+
// always use first channel
|
|
1320
|
+
const data = [...Array(1)].map((_, ch) => buffer.getChannelData(ch));
|
|
1321
|
+
InterleaveT(data, () => memory.view(), Float32ToInt16);
|
|
1322
|
+
native.ipvp_ref_framing(memory.pointer, memory.bytes);
|
|
1323
|
+
},
|
|
1324
|
+
};
|
|
1325
|
+
outputBuffer.onpull = {
|
|
1326
|
+
samples,
|
|
1327
|
+
callback: () => {
|
|
1328
|
+
native.ipvp_spk_framing(memory.pointer, memory.bytes);
|
|
1329
|
+
const data = DeInterleaveT(memory.view(), channels, (samples) => new Float32Array(samples), Int16ToFloat32);
|
|
1330
|
+
const buffer = new AudioFrameBuffer({
|
|
1331
|
+
length: samples,
|
|
1332
|
+
numberOfChannels: channels,
|
|
1333
|
+
sampleRate: samplerate,
|
|
1334
|
+
data,
|
|
1335
|
+
});
|
|
1336
|
+
const frame = new AudioFrame({ buffer });
|
|
1337
|
+
// ref
|
|
1338
|
+
if (is(3 /* kSendRecv */)) {
|
|
1339
|
+
native.ipvp_ref_framing(memory.pointer, memory.bytes);
|
|
1340
|
+
}
|
|
1341
|
+
return frame;
|
|
1342
|
+
},
|
|
1343
|
+
};
|
|
1344
|
+
const is = (val) => !(type ^ val);
|
|
1345
|
+
push = (builder) => {
|
|
1346
|
+
if (is(2 /* kRecvOnly */)) {
|
|
1347
|
+
console.log('push auido buffer but engine is recv only');
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
const buffer = new AudioFrameBuffer(builder);
|
|
1351
|
+
const frame = new AudioFrame({ buffer });
|
|
1352
|
+
inputBuffer.push(frame);
|
|
1353
|
+
};
|
|
1354
|
+
push_ref = (builder) => {
|
|
1355
|
+
if (is(2 /* kRecvOnly */)) {
|
|
1356
|
+
console.log('push ref auido buffer but engine is recv only');
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
const buffer = new AudioFrameBuffer(builder);
|
|
1360
|
+
const frame = new AudioFrame({ buffer });
|
|
1361
|
+
refBuffer.push(frame);
|
|
1362
|
+
};
|
|
1363
|
+
pull = (wanted) => {
|
|
1364
|
+
if (is(1 /* kSendOnly */)) {
|
|
1365
|
+
console.log('pull auido buffer but engine is send only');
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
// true && checkSamples(wanted, samplerate);
|
|
1369
|
+
const frame = outputBuffer.pull(wanted);
|
|
1370
|
+
if (!frame)
|
|
1371
|
+
return;
|
|
1372
|
+
return AudioFrameBuffer.Builder(frame.buffer);
|
|
1373
|
+
};
|
|
1374
|
+
let dropRequests = 0;
|
|
1375
|
+
drop = () => {
|
|
1376
|
+
if (is(1 /* kSendOnly */)) {
|
|
1377
|
+
console.log('drop audio buffer but engine is send only');
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
dropRequests++;
|
|
1381
|
+
};
|
|
1382
|
+
if (port) {
|
|
1383
|
+
const channel = new MojoChannel(port);
|
|
1384
|
+
// processor api
|
|
1385
|
+
const processor = channel.receiver('worklet-audio-processor-host');
|
|
1386
|
+
processor.implement('process', (reply, [builder]) => {
|
|
1387
|
+
push(builder);
|
|
1388
|
+
let wanted = builder.length;
|
|
1389
|
+
if (dropRequests > 0) {
|
|
1390
|
+
dropRequests--;
|
|
1391
|
+
wanted -= samples;
|
|
1392
|
+
}
|
|
1393
|
+
const output = pull(wanted);
|
|
1394
|
+
if (!output) {
|
|
1395
|
+
reply.resolve();
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
|
|
1399
|
+
});
|
|
1400
|
+
processor.implement('processdelay', (reply, [samples]) => {
|
|
1401
|
+
drop(samples);
|
|
1402
|
+
reply.resolve();
|
|
1403
|
+
});
|
|
1404
|
+
// source|sink api
|
|
1405
|
+
const source = channel.receiver('worklet-audio-source-host');
|
|
1406
|
+
source.implement('onpush', (reply, [builder]) => {
|
|
1407
|
+
push(builder);
|
|
1408
|
+
reply.resolve();
|
|
1409
|
+
});
|
|
1410
|
+
source.implement('onref', (reply, [builder]) => {
|
|
1411
|
+
push_ref(builder);
|
|
1412
|
+
reply.resolve();
|
|
1413
|
+
});
|
|
1414
|
+
const sink = channel.receiver('worklet-audio-sink-host');
|
|
1415
|
+
sink.implement('onpull', (reply, [wanted]) => {
|
|
1416
|
+
const output = pull(wanted);
|
|
1417
|
+
if (!output) {
|
|
1418
|
+
reply.resolve();
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
|
|
1422
|
+
});
|
|
1423
|
+
sink.implement('ondelay', (reply, [samples]) => {
|
|
1424
|
+
drop(samples);
|
|
1425
|
+
reply.resolve();
|
|
1426
|
+
});
|
|
1427
|
+
cleanup.unshift(() => {
|
|
1428
|
+
channel.close();
|
|
1429
|
+
port.close();
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
reply.resolve();
|
|
1433
|
+
});
|
|
1434
|
+
engine.implement('trackgain', (reply, [gain = -1, track = 0]) => {
|
|
1435
|
+
const type = trackType(track);
|
|
1436
|
+
decoderGains[trackType(track)] = gain;
|
|
1437
|
+
if (gain >= 0) {
|
|
1438
|
+
const decoders = type === 0 /* kMain */ ? decoderList(0, 10) : decoderList(11);
|
|
1439
|
+
native.ipvp_decode_gain(decoders.map((ccid) => [ccid, gain]));
|
|
1440
|
+
eventR && eventR.make('gainchange').post(gain, track);
|
|
1441
|
+
if (type === 0 /* kMain */) {
|
|
1442
|
+
debouncedUpdateDecodeGain.cancel();
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
reply.resolve();
|
|
1446
|
+
});
|
|
1447
|
+
engine.implement('fec', (reply, [lossrate]) => {
|
|
1448
|
+
native.ipvp_config_fec(callid, lossrate);
|
|
1449
|
+
reply.resolve();
|
|
1450
|
+
});
|
|
1451
|
+
engine.implement('dumpstats', (reply) => {
|
|
1452
|
+
native.ipvp_debug_dump_stat();
|
|
1453
|
+
reply.resolve();
|
|
1454
|
+
});
|
|
1455
|
+
engine.implement('dumpparam', (reply) => {
|
|
1456
|
+
native.ipvp_debug_dump_param();
|
|
1457
|
+
reply.resolve();
|
|
1458
|
+
});
|
|
1459
|
+
engine.implement('dumptalk', (reply) => {
|
|
1460
|
+
native.ipvp_debug_dump_talk();
|
|
1461
|
+
reply.resolve();
|
|
1462
|
+
});
|
|
1463
|
+
{
|
|
1464
|
+
engine.implement('vtuner', (reply, [host = 'localhost:8090', port = Math.random() * 10000 + 1080]) => {
|
|
1465
|
+
/* eslint-disable no-restricted-globals */
|
|
1466
|
+
const { hostname, protocol } = self.location;
|
|
1467
|
+
const scheme = hostname === 'localhost' ? 'ws:' : protocol.replace('http', 'ws');
|
|
1468
|
+
const ws = new WebSocket(`${scheme}//${host}/?port=${port.toFixed(0)}`);
|
|
1469
|
+
ws.binaryType = 'arraybuffer';
|
|
1470
|
+
console.log(`vtuner: ${port.toFixed(0)}`);
|
|
1471
|
+
let close;
|
|
1472
|
+
ws.onopen = () => {
|
|
1473
|
+
ws.onmessage = ({ data }) => {
|
|
1474
|
+
if (typeof data !== 'string') {
|
|
1475
|
+
memory.view().set(new Uint8Array(data));
|
|
1476
|
+
native.ipvp_debug_vtuner_send(memory.pointer, data.byteLength);
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
console.log(`unknown message:\n\n${data}\n`);
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
native.ipvp_debug_vtuner_enable(1);
|
|
1483
|
+
const memory = alloc(512 * 1024);
|
|
1484
|
+
vtuner_reply = (data, length) => {
|
|
1485
|
+
ws.send(view(data, length));
|
|
1486
|
+
};
|
|
1487
|
+
close = () => {
|
|
1488
|
+
console.log('closing vtuner...');
|
|
1489
|
+
native.ipvp_debug_vtuner_enable(0);
|
|
1490
|
+
memory.free();
|
|
1491
|
+
ws.close();
|
|
1492
|
+
};
|
|
1493
|
+
cleanup.unshift(() => {
|
|
1494
|
+
close && close();
|
|
1495
|
+
close = null;
|
|
1496
|
+
});
|
|
1497
|
+
};
|
|
1498
|
+
ws.onclose = () => {
|
|
1499
|
+
close && close();
|
|
1500
|
+
close = null;
|
|
1501
|
+
};
|
|
1502
|
+
reply.resolve();
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
const DEFAULT_DELAYED_UPDATE_TIME = 5000;
|
|
1506
|
+
let LAST_SPEAKING = [];
|
|
1507
|
+
let CURRENT_SUPPRESSION = false;
|
|
1508
|
+
function maybeUpdateDecodeGain(speaking = LAST_SPEAKING) {
|
|
1509
|
+
LAST_SPEAKING = speaking;
|
|
1510
|
+
let gain = decoderGains[0 /* kMain */];
|
|
1511
|
+
if (gain >= 0)
|
|
1512
|
+
return;
|
|
1513
|
+
const specialDecoders = decoderList(11);
|
|
1514
|
+
const normalDecoders = decoderList(0, 10);
|
|
1515
|
+
const shouldSuppression = specialDecoders.some((v) => speaking.includes(v));
|
|
1516
|
+
// if has channel > 10
|
|
1517
|
+
// all ssrc in channel below 10 set to 20%
|
|
1518
|
+
if (shouldSuppression) {
|
|
1519
|
+
const DEFAULT_SUPPRESSION_GAIN = 20;
|
|
1520
|
+
gain = DEFAULT_SUPPRESSION_GAIN;
|
|
1521
|
+
debouncedUpdateDecodeGain.cancel();
|
|
1522
|
+
updateDecodeGain(normalDecoders, gain);
|
|
1523
|
+
}
|
|
1524
|
+
// if no channel > 10
|
|
1525
|
+
// all ssrc in channel below 10 set to 100%
|
|
1526
|
+
else {
|
|
1527
|
+
const DEFAULT_GAIN = 100;
|
|
1528
|
+
gain = DEFAULT_GAIN;
|
|
1529
|
+
debouncedUpdateDecodeGain(normalDecoders, gain);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function updateDecodeGain(decoders, gain) {
|
|
1533
|
+
native.ipvp_decode_gain(decoders.map((ccid) => [ccid, gain]));
|
|
1534
|
+
eventR && eventR.make('gainchange').post(gain, 0 /* kMain */);
|
|
1535
|
+
CURRENT_SUPPRESSION = gain !== 100;
|
|
1536
|
+
}
|
|
1537
|
+
const debouncedUpdateDecodeGain = debounce(updateDecodeGain, DEFAULT_DELAYED_UPDATE_TIME);
|
|
1538
|
+
function debounce(fn, delay) {
|
|
1539
|
+
let timer;
|
|
1540
|
+
function delegate(...args) {
|
|
1541
|
+
clearTimeout(timer);
|
|
1542
|
+
// @ts-ignore
|
|
1543
|
+
timer = setTimeout(() => fn.call(this, ...args), delay);
|
|
1544
|
+
}
|
|
1545
|
+
delegate.cancel = () => clearTimeout(timer);
|
|
1546
|
+
return delegate;
|
|
1547
|
+
}
|
|
1548
|
+
setupBusyDetector();
|
|
1549
|
+
// simple cpu overload detection
|
|
1550
|
+
function setupBusyDetector() {
|
|
1551
|
+
let last = Date.now();
|
|
1552
|
+
let detectTimer;
|
|
1553
|
+
const detectInterval = 1000;
|
|
1554
|
+
const factor = 1.1;
|
|
1555
|
+
const detect = () => {
|
|
1556
|
+
const now = Date.now();
|
|
1557
|
+
const delta = now - last;
|
|
1558
|
+
const tester = detectInterval * factor;
|
|
1559
|
+
if (delta > tester) {
|
|
1560
|
+
console.log(`busy:\n` +
|
|
1561
|
+
`delta: ${delta}\n` +
|
|
1562
|
+
`factor: ${factor}\n` +
|
|
1563
|
+
`interval: ${detectInterval}\n` +
|
|
1564
|
+
`delay: ${delta - detectInterval}\n`);
|
|
1565
|
+
}
|
|
1566
|
+
last = now;
|
|
1567
|
+
detectTimer = setTimeout(detect, detectInterval);
|
|
1568
|
+
};
|
|
1569
|
+
detect();
|
|
1570
|
+
return () => clearTimeout(detectTimer);
|
|
1571
|
+
}
|
|
1572
|
+
// const checkSamples = (samples: number, sampleRate: number) => {
|
|
1573
|
+
// const period = sampleRate / 100;
|
|
1574
|
+
// const diff = samples % period;
|
|
1575
|
+
// const upside = samples - diff + period;
|
|
1576
|
+
// const downside = samples - diff;
|
|
1577
|
+
// if (diff) {
|
|
1578
|
+
// true &&
|
|
1579
|
+
// console.log(`invalid samples: ${samples} -> ${upside} or ${downside}`);
|
|
1580
|
+
// }
|
|
1581
|
+
// };
|
|
1582
|
+
|
|
1583
|
+
}());
|