@ylink-sdk/mobile-web 0.1.9-beta.1 → 0.1.9-beta.4
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/style.css +1 -1
- package/dist/ylink-sdk-mobile.umd.js +70 -70
- package/package.json +1 -1
- package/dist/e30037b8/VERSION +0 -6
- package/dist/e30037b8/libipvp-simd.d.ts +0 -474
- package/dist/e30037b8/libipvp-simd.js +0 -3267
- package/dist/e30037b8/libipvp-simd.prod.d.ts +0 -474
- package/dist/e30037b8/libipvp-simd.prod.js +0 -184
- package/dist/e30037b8/libipvp-simd.prod.wasm +0 -0
- package/dist/e30037b8/libipvp-simd.wasm +0 -0
- package/dist/e30037b8/libipvp.d.ts +0 -474
- package/dist/e30037b8/libipvp.js +0 -3267
- package/dist/e30037b8/libipvp.prod.d.ts +0 -474
- package/dist/e30037b8/libipvp.prod.js +0 -184
- package/dist/e30037b8/libipvp.prod.wasm +0 -0
- package/dist/e30037b8/libipvp.wasm +0 -0
- package/dist/e30037b8/librtc.d.ts +0 -438
- package/dist/e30037b8/librtc.global.js +0 -5863
- package/dist/e30037b8/librtc.global.prod.js +0 -15
- package/dist/e30037b8/libsfu-simd.d.ts +0 -767
- package/dist/e30037b8/libsfu-simd.js +0 -4822
- package/dist/e30037b8/libsfu-simd.prod.d.ts +0 -767
- package/dist/e30037b8/libsfu-simd.prod.js +0 -209
- package/dist/e30037b8/libsfu-simd.prod.wasm +0 -0
- package/dist/e30037b8/libsfu-simd.wasm +0 -0
- package/dist/e30037b8/libsfu.d.ts +0 -767
- package/dist/e30037b8/libsfu.js +0 -4822
- package/dist/e30037b8/libsfu.prod.d.ts +0 -767
- package/dist/e30037b8/libsfu.prod.js +0 -209
- package/dist/e30037b8/libsfu.prod.wasm +0 -0
- package/dist/e30037b8/libsfu.wasm +0 -0
- package/dist/e30037b8/libsvc-simd-m1.d.ts +0 -767
- package/dist/e30037b8/libsvc-simd-m1.js +0 -4921
- package/dist/e30037b8/libsvc-simd-m1.prod.d.ts +0 -767
- package/dist/e30037b8/libsvc-simd-m1.prod.js +0 -209
- 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 +0 -767
- package/dist/e30037b8/libsvc-simd.js +0 -4857
- package/dist/e30037b8/libsvc-simd.prod.d.ts +0 -767
- package/dist/e30037b8/libsvc-simd.prod.js +0 -209
- package/dist/e30037b8/libsvc-simd.prod.wasm +0 -0
- package/dist/e30037b8/libsvc-simd.wasm +0 -0
- package/dist/e30037b8/libsvc.d.ts +0 -767
- package/dist/e30037b8/libsvc.js +0 -4857
- package/dist/e30037b8/libsvc.prod.d.ts +0 -767
- package/dist/e30037b8/libsvc.prod.js +0 -209
- package/dist/e30037b8/libsvc.prod.wasm +0 -0
- package/dist/e30037b8/libsvc.wasm +0 -0
- package/dist/e30037b8/native-audio-worker.global.js +0 -1583
- package/dist/e30037b8/native-audio-worker.global.prod.js +0 -15
- package/dist/e30037b8/native-video-worker.global.js +0 -1960
- package/dist/e30037b8/native-video-worker.global.prod.js +0 -15
- package/dist/e30037b8/worklet-audio-worker.global.js +0 -900
- package/dist/e30037b8/worklet-audio-worker.global.prod.js +0 -15
|
@@ -1,1960 +0,0 @@
|
|
|
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
|
-
function YUVToRGB(buffer, image, alpha = 255) {
|
|
433
|
-
const { width, height, chromaWidth, chromaHeight, dataY, dataU, dataV, strideY, strideU, strideV, } = buffer;
|
|
434
|
-
const output = image.data;
|
|
435
|
-
const hdec = depower(width / chromaWidth) | 0;
|
|
436
|
-
const vdec = depower(height / chromaHeight) | 0;
|
|
437
|
-
const outStride = width << 2;
|
|
438
|
-
let YPtr = 0;
|
|
439
|
-
let Y0Ptr = 0;
|
|
440
|
-
let Y1Ptr = 0;
|
|
441
|
-
let CbPtr = 0;
|
|
442
|
-
let CrPtr = 0;
|
|
443
|
-
let outPtr = 0;
|
|
444
|
-
let outPtr0 = 0;
|
|
445
|
-
let outPtr1 = 0;
|
|
446
|
-
let colorCb = 0;
|
|
447
|
-
let colorCr = 0;
|
|
448
|
-
let multY = 0;
|
|
449
|
-
let multCrR = 0;
|
|
450
|
-
let multCbCrG = 0;
|
|
451
|
-
let multCbB = 0;
|
|
452
|
-
let x = 0;
|
|
453
|
-
let y = 0;
|
|
454
|
-
let xdec = 0;
|
|
455
|
-
let ydec = 0;
|
|
456
|
-
// eslint-disable-next-line no-constant-condition
|
|
457
|
-
if (hdec === 1 && vdec === 1) {
|
|
458
|
-
// Optimize for 4:2:0, which is most common
|
|
459
|
-
outPtr0 = 0;
|
|
460
|
-
outPtr1 = outStride;
|
|
461
|
-
ydec = 0;
|
|
462
|
-
for (y = 0; y < height; y += 2) {
|
|
463
|
-
Y0Ptr = (y * strideY) | 0;
|
|
464
|
-
Y1Ptr = (Y0Ptr + strideY) | 0;
|
|
465
|
-
CbPtr = (ydec * strideU) | 0;
|
|
466
|
-
CrPtr = (ydec * strideV) | 0;
|
|
467
|
-
for (x = 0; x < width; x += 2) {
|
|
468
|
-
colorCb = dataU[CbPtr++] | 0;
|
|
469
|
-
colorCr = dataV[CrPtr++] | 0;
|
|
470
|
-
// Quickie YUV conversion
|
|
471
|
-
// https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
|
|
472
|
-
// multiplied by 256 for integer-friendliness
|
|
473
|
-
multCrR = (((409 * colorCr) | 0) - 57088) | 0;
|
|
474
|
-
multCbCrG = (((100 * colorCb) | 0) + ((208 * colorCr) | 0) - 34816) | 0;
|
|
475
|
-
multCbB = (((516 * colorCb) | 0) - 70912) | 0;
|
|
476
|
-
multY = (298 * dataY[Y0Ptr++]) | 0;
|
|
477
|
-
output[outPtr0] = (multY + multCrR) >> 8;
|
|
478
|
-
output[outPtr0 + 1] = (multY - multCbCrG) >> 8;
|
|
479
|
-
output[outPtr0 + 2] = (multY + multCbB) >> 8;
|
|
480
|
-
output[outPtr0 + 3] = alpha;
|
|
481
|
-
outPtr0 += 4;
|
|
482
|
-
multY = (298 * dataY[Y0Ptr++]) | 0;
|
|
483
|
-
output[outPtr0] = (multY + multCrR) >> 8;
|
|
484
|
-
output[outPtr0 + 1] = (multY - multCbCrG) >> 8;
|
|
485
|
-
output[outPtr0 + 2] = (multY + multCbB) >> 8;
|
|
486
|
-
output[outPtr0 + 3] = alpha;
|
|
487
|
-
outPtr0 += 4;
|
|
488
|
-
multY = (298 * dataY[Y1Ptr++]) | 0;
|
|
489
|
-
output[outPtr1] = (multY + multCrR) >> 8;
|
|
490
|
-
output[outPtr1 + 1] = (multY - multCbCrG) >> 8;
|
|
491
|
-
output[outPtr1 + 2] = (multY + multCbB) >> 8;
|
|
492
|
-
output[outPtr1 + 3] = alpha;
|
|
493
|
-
outPtr1 += 4;
|
|
494
|
-
multY = (298 * dataY[Y1Ptr++]) | 0;
|
|
495
|
-
output[outPtr1] = (multY + multCrR) >> 8;
|
|
496
|
-
output[outPtr1 + 1] = (multY - multCbCrG) >> 8;
|
|
497
|
-
output[outPtr1 + 2] = (multY + multCbB) >> 8;
|
|
498
|
-
output[outPtr1 + 3] = alpha;
|
|
499
|
-
outPtr1 += 4;
|
|
500
|
-
}
|
|
501
|
-
outPtr0 += outStride;
|
|
502
|
-
outPtr1 += outStride;
|
|
503
|
-
ydec++;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
outPtr = 0;
|
|
508
|
-
for (y = 0; y < height; y++) {
|
|
509
|
-
xdec = 0;
|
|
510
|
-
ydec = y >> vdec;
|
|
511
|
-
YPtr = (y * strideY) | 0;
|
|
512
|
-
CbPtr = (ydec * strideU) | 0;
|
|
513
|
-
CrPtr = (ydec * strideV) | 0;
|
|
514
|
-
for (x = 0; x < width; x++) {
|
|
515
|
-
xdec = x >> hdec;
|
|
516
|
-
colorCb = dataU[CbPtr + xdec] | 0;
|
|
517
|
-
colorCr = dataV[CrPtr + xdec] | 0;
|
|
518
|
-
// Quickie YUV conversion
|
|
519
|
-
// https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
|
|
520
|
-
// multiplied by 256 for integer-friendliness
|
|
521
|
-
multCrR = (((409 * colorCr) | 0) - 57088) | 0;
|
|
522
|
-
multCbCrG = (((100 * colorCb) | 0) + ((208 * colorCr) | 0) - 34816) | 0;
|
|
523
|
-
multCbB = (((516 * colorCb) | 0) - 70912) | 0;
|
|
524
|
-
multY = (298 * dataY[YPtr++]) | 0;
|
|
525
|
-
output[outPtr] = (multY + multCrR) >> 8;
|
|
526
|
-
output[outPtr + 1] = (multY - multCbCrG) >> 8;
|
|
527
|
-
output[outPtr + 2] = (multY + multCbB) >> 8;
|
|
528
|
-
// const Y = dataY[YPtr++];
|
|
529
|
-
// const U = dataU[CbPtr + xdec];
|
|
530
|
-
// const V = dataV[CrPtr + xdec];
|
|
531
|
-
// const [R, G, B] = YUV2RGB([Y, U, V]);
|
|
532
|
-
// const R = YUVToR(Y, U, V);
|
|
533
|
-
// const G = YUVToG(Y, U, V);
|
|
534
|
-
// const B = YUVToB(Y, U, V);
|
|
535
|
-
// output[outPtr] = R;
|
|
536
|
-
// output[outPtr + 1] = G;
|
|
537
|
-
// output[outPtr + 2] = B;
|
|
538
|
-
output[outPtr + 3] = alpha;
|
|
539
|
-
outPtr += 4;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
function depower(ratio) {
|
|
545
|
-
let shiftCount = 0;
|
|
546
|
-
let n = ratio >> 1;
|
|
547
|
-
while (n !== 0) {
|
|
548
|
-
n >>= 1;
|
|
549
|
-
shiftCount++;
|
|
550
|
-
}
|
|
551
|
-
if (ratio !== 1 << shiftCount) {
|
|
552
|
-
throw Error(`chroma plane dimensions must be power of 2 ratio to luma plane dimensions; got ${ratio}`);
|
|
553
|
-
}
|
|
554
|
-
return shiftCount;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// export const enum VideoFrameBufferType {
|
|
558
|
-
var VideoFrameBufferType;
|
|
559
|
-
(function (VideoFrameBufferType) {
|
|
560
|
-
VideoFrameBufferType[VideoFrameBufferType["kNative"] = 0] = "kNative";
|
|
561
|
-
VideoFrameBufferType[VideoFrameBufferType["kRGBA"] = 1] = "kRGBA";
|
|
562
|
-
VideoFrameBufferType[VideoFrameBufferType["kABGR"] = 2] = "kABGR";
|
|
563
|
-
VideoFrameBufferType[VideoFrameBufferType["kI420"] = 3] = "kI420";
|
|
564
|
-
VideoFrameBufferType[VideoFrameBufferType["kI420A"] = 4] = "kI420A";
|
|
565
|
-
VideoFrameBufferType[VideoFrameBufferType["kI444"] = 5] = "kI444";
|
|
566
|
-
})(VideoFrameBufferType || (VideoFrameBufferType = {}));
|
|
567
|
-
function I420DataSize(height, strideY, strideU, strideV) {
|
|
568
|
-
return strideY * height + (strideU + strideV) * ((height + 1) / 2);
|
|
569
|
-
}
|
|
570
|
-
class I420Buffer {
|
|
571
|
-
constructor(builder) {
|
|
572
|
-
this.type = VideoFrameBufferType.kI420;
|
|
573
|
-
this.width = builder.width;
|
|
574
|
-
this.height = builder.height;
|
|
575
|
-
this.strideY = builder.strideY || this.width;
|
|
576
|
-
this.strideU = builder.strideU || this.chromaWidth;
|
|
577
|
-
this.strideV = builder.strideV || this.chromaWidth;
|
|
578
|
-
const hasdata = builder.dataY && builder.dataU && builder.dataV;
|
|
579
|
-
// @ts-ignore
|
|
580
|
-
this.data =
|
|
581
|
-
!hasdata &&
|
|
582
|
-
AlignedMalloc(I420DataSize(this.height, this.strideY, this.strideU, this.strideV));
|
|
583
|
-
this.dataY = builder.dataY || new Uint8ClampedArray(this.data, 0, this.strideY * this.height);
|
|
584
|
-
this.dataU =
|
|
585
|
-
builder.dataU ||
|
|
586
|
-
new Uint8ClampedArray(this.data, this.strideY * this.height, this.strideU * this.chromaHeight);
|
|
587
|
-
this.dataV =
|
|
588
|
-
builder.dataV ||
|
|
589
|
-
new Uint8ClampedArray(this.data, this.strideY * this.height + this.strideU * this.chromaHeight, this.strideV * this.chromaHeight);
|
|
590
|
-
// this.dataY = builder.dataY || AlignedMalloc(this.strideY * this.height);
|
|
591
|
-
// this.dataU =
|
|
592
|
-
// builder.dataU ||
|
|
593
|
-
// AlignedMalloc(this.strideU * this.chromaHeight);
|
|
594
|
-
// this.dataV =
|
|
595
|
-
// builder.dataV ||
|
|
596
|
-
// AlignedMalloc(this.strideV * this.chromaHeight);
|
|
597
|
-
}
|
|
598
|
-
get chromaWidth() {
|
|
599
|
-
return Math.floor((this.width + 1) / 2);
|
|
600
|
-
}
|
|
601
|
-
get chromaHeight() {
|
|
602
|
-
return Math.floor((this.height + 1) / 2);
|
|
603
|
-
}
|
|
604
|
-
toI420() {
|
|
605
|
-
return this;
|
|
606
|
-
}
|
|
607
|
-
getI420() {
|
|
608
|
-
return this;
|
|
609
|
-
}
|
|
610
|
-
getI420A() {
|
|
611
|
-
return undefined;
|
|
612
|
-
}
|
|
613
|
-
getI444() {
|
|
614
|
-
return undefined;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
|
|
618
|
-
const kBufferAlignment = 64;
|
|
619
|
-
function AlignedSize(size, alignment = kBufferAlignment) {
|
|
620
|
-
const remainder = size % alignment;
|
|
621
|
-
const aligned = remainder === 0 ? size : size + alignment - remainder;
|
|
622
|
-
return aligned;
|
|
623
|
-
}
|
|
624
|
-
function AlignedMalloc(size, alignment = kBufferAlignment) {
|
|
625
|
-
return new ArrayBuffer(AlignedSize(size, alignment));
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
class VideoFrame {
|
|
629
|
-
constructor(builder) {
|
|
630
|
-
this.buffer = builder.buffer;
|
|
631
|
-
}
|
|
632
|
-
get width() {
|
|
633
|
-
return this.buffer.width;
|
|
634
|
-
}
|
|
635
|
-
get height() {
|
|
636
|
-
return this.buffer.height;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
let suspended = false;
|
|
641
|
-
if (typeof document !== 'undefined' && typeof __INVISIBLE_SUSPEND__ !== 'undefined') {
|
|
642
|
-
document.addEventListener('visibilitychange', () => {
|
|
643
|
-
suspended = document.visibilityState === 'hidden';
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
class VideoRenderer {
|
|
647
|
-
constructor() {
|
|
648
|
-
this.frameCounts = 0;
|
|
649
|
-
this.droppedFrameCounts = 0;
|
|
650
|
-
this.pending = false;
|
|
651
|
-
}
|
|
652
|
-
clear() {
|
|
653
|
-
console.log('abstract clear()');
|
|
654
|
-
}
|
|
655
|
-
render(frame) {
|
|
656
|
-
console.log('abstract render()', frame);
|
|
657
|
-
}
|
|
658
|
-
onFrame(frame, immediate = false) {
|
|
659
|
-
// fps reduction
|
|
660
|
-
if (!this.pending && !suspended) {
|
|
661
|
-
if (immediate) {
|
|
662
|
-
this.render(frame);
|
|
663
|
-
}
|
|
664
|
-
else {
|
|
665
|
-
requestAnimationFrame(() => this.render(frame));
|
|
666
|
-
}
|
|
667
|
-
requestAnimationFrame(() => (this.pending = false));
|
|
668
|
-
this.pending = true;
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
this.droppedFrameCounts++;
|
|
672
|
-
|
|
673
|
-
!(this.droppedFrameCounts % 600) &&
|
|
674
|
-
console.log(`renderer drop frame due to fps reduction: ${this.droppedFrameCounts}`);
|
|
675
|
-
}
|
|
676
|
-
this.frameCounts++;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
class CanvasVideoRenderer extends VideoRenderer {
|
|
681
|
-
constructor(canvas) {
|
|
682
|
-
super();
|
|
683
|
-
this.canvas = canvas;
|
|
684
|
-
this.width = canvas.width;
|
|
685
|
-
this.height = canvas.height;
|
|
686
|
-
// this.canvas.style.display = 'block';
|
|
687
|
-
this.context = this.canvas.getContext('2d');
|
|
688
|
-
// this.context.setTransform(1, 0, 0, 1, 0, 0);
|
|
689
|
-
}
|
|
690
|
-
setSize(width, height) {
|
|
691
|
-
this.canvas.width = width;
|
|
692
|
-
this.canvas.height = height;
|
|
693
|
-
this.width = width;
|
|
694
|
-
this.height = height;
|
|
695
|
-
}
|
|
696
|
-
clear() {
|
|
697
|
-
this.context.clearRect(0, 0, this.width, this.height);
|
|
698
|
-
}
|
|
699
|
-
render(frame) {
|
|
700
|
-
const { width, height, buffer } = frame;
|
|
701
|
-
if (width !== this.width || height !== this.height) {
|
|
702
|
-
this.setSize(width, height);
|
|
703
|
-
}
|
|
704
|
-
this.clear();
|
|
705
|
-
let image;
|
|
706
|
-
switch (buffer.type) {
|
|
707
|
-
case VideoFrameBufferType.kI420:
|
|
708
|
-
image = this.context.createImageData(width, height);
|
|
709
|
-
YUVToRGB(buffer, image);
|
|
710
|
-
break;
|
|
711
|
-
case VideoFrameBufferType.kABGR:
|
|
712
|
-
image = buffer.toImage(this.context);
|
|
713
|
-
break;
|
|
714
|
-
case VideoFrameBufferType.kNative:
|
|
715
|
-
image = buffer.image;
|
|
716
|
-
break;
|
|
717
|
-
default:
|
|
718
|
-
console.warn(`Frame type: ${buffer.type} is not supported.`);
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
this.context.putImageData(image, 0, 0, 0, 0, width, height);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
const VS = `
|
|
726
|
-
precision mediump float;
|
|
727
|
-
|
|
728
|
-
attribute vec2 aVertexPosition;
|
|
729
|
-
attribute vec2 aTextureCoord;
|
|
730
|
-
|
|
731
|
-
uniform vec2 uResolution;
|
|
732
|
-
|
|
733
|
-
varying vec2 vTextureCoord;
|
|
734
|
-
|
|
735
|
-
void main(void) {
|
|
736
|
-
// convert the rectangle from pixels to 0.0 to 1.0
|
|
737
|
-
vec2 zeroToOne = aVertexPosition / uResolution;
|
|
738
|
-
|
|
739
|
-
// convert from 0 -> 1 to 0 -> 2
|
|
740
|
-
vec2 zeroToTwo = zeroToOne * 2.0;
|
|
741
|
-
|
|
742
|
-
// convert from 0 -> 2 to -1 -> +1 (clipspace)
|
|
743
|
-
vec2 clipSpace = zeroToTwo - 1.0;
|
|
744
|
-
|
|
745
|
-
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
|
|
746
|
-
|
|
747
|
-
vTextureCoord = aTextureCoord;
|
|
748
|
-
}
|
|
749
|
-
`;
|
|
750
|
-
const FS = `
|
|
751
|
-
precision mediump float;
|
|
752
|
-
|
|
753
|
-
uniform sampler2D uTextureY;
|
|
754
|
-
uniform sampler2D uTextureU;
|
|
755
|
-
uniform sampler2D uTextureV;
|
|
756
|
-
|
|
757
|
-
varying vec2 vTextureCoord;
|
|
758
|
-
|
|
759
|
-
const mat4 YUV2RGB = mat4(
|
|
760
|
-
1.1643828125, 0, 1.59602734375, -.87078515625,
|
|
761
|
-
1.1643828125, -.39176171875, -.81296875, .52959375,
|
|
762
|
-
1.1643828125, 2.017234375, 0, -1.081390625,
|
|
763
|
-
0, 0, 0, 1
|
|
764
|
-
);
|
|
765
|
-
|
|
766
|
-
void main(void) {
|
|
767
|
-
gl_FragColor = vec4(
|
|
768
|
-
texture2D(uTextureY, vTextureCoord).x,
|
|
769
|
-
texture2D(uTextureU, vTextureCoord).x,
|
|
770
|
-
texture2D(uTextureV, vTextureCoord).x,
|
|
771
|
-
1
|
|
772
|
-
) * YUV2RGB;
|
|
773
|
-
}
|
|
774
|
-
`;
|
|
775
|
-
function buildProgram(gl) {
|
|
776
|
-
// Create the program and shaders
|
|
777
|
-
const program = gl.createProgram();
|
|
778
|
-
const vertexShader = buildShader(gl, gl.VERTEX_SHADER, VS);
|
|
779
|
-
const fragmentShader = buildShader(gl, gl.FRAGMENT_SHADER, FS);
|
|
780
|
-
if (!program || !vertexShader || !fragmentShader) {
|
|
781
|
-
return null;
|
|
782
|
-
}
|
|
783
|
-
// Attach an link the shader
|
|
784
|
-
gl.attachShader(program, vertexShader);
|
|
785
|
-
gl.attachShader(program, fragmentShader);
|
|
786
|
-
gl.linkProgram(program);
|
|
787
|
-
// Add error handling
|
|
788
|
-
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
789
|
-
const error = gl.getError();
|
|
790
|
-
const status = gl.getProgramParameter(program, gl.VALIDATE_STATUS);
|
|
791
|
-
|
|
792
|
-
console.error(`Could not initialise shader.\n` + `VALIDATE_STATUS: ${status}\n` + `ERROR: ${error}`);
|
|
793
|
-
return null;
|
|
794
|
-
}
|
|
795
|
-
// Delete the shader
|
|
796
|
-
gl.deleteShader(fragmentShader);
|
|
797
|
-
gl.deleteShader(vertexShader);
|
|
798
|
-
// Enable program
|
|
799
|
-
gl.useProgram(program);
|
|
800
|
-
return program;
|
|
801
|
-
}
|
|
802
|
-
function buildShader(gl, type, source) {
|
|
803
|
-
const shader = gl.createShader(type);
|
|
804
|
-
if (!shader) {
|
|
805
|
-
console.error(`WebGL createShader(type: ${type}) failed.`);
|
|
806
|
-
return null;
|
|
807
|
-
}
|
|
808
|
-
// Add shader source
|
|
809
|
-
gl.shaderSource(shader, source);
|
|
810
|
-
// Compile shader
|
|
811
|
-
gl.compileShader(shader);
|
|
812
|
-
// Add error handling
|
|
813
|
-
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
814
|
-
console.error(`WebGL compileShader() failed.\n` + `${gl.getShaderInfoLog(shader)}`);
|
|
815
|
-
return null;
|
|
816
|
-
}
|
|
817
|
-
return shader;
|
|
818
|
-
}
|
|
819
|
-
function buildBuffer(gl, program, type, identifier, size, structure, count) {
|
|
820
|
-
const buffer = {
|
|
821
|
-
buffer: gl.createBuffer(),
|
|
822
|
-
location: -1,
|
|
823
|
-
identifier,
|
|
824
|
-
size,
|
|
825
|
-
structure,
|
|
826
|
-
count,
|
|
827
|
-
};
|
|
828
|
-
// Set the location
|
|
829
|
-
switch (type) {
|
|
830
|
-
case 'attribute':
|
|
831
|
-
buffer.location = gl.getAttribLocation(program, identifier);
|
|
832
|
-
break;
|
|
833
|
-
case 'uniform':
|
|
834
|
-
buffer.location = gl.getUniformLocation(program, identifier);
|
|
835
|
-
break;
|
|
836
|
-
}
|
|
837
|
-
// Create the buffer if count is provided
|
|
838
|
-
if (count) {
|
|
839
|
-
buffer.data = new Float32Array(count * size);
|
|
840
|
-
}
|
|
841
|
-
// Return the buffer
|
|
842
|
-
return buffer;
|
|
843
|
-
}
|
|
844
|
-
function buildTexture(gl, program, type, identifier, textureId, unit) {
|
|
845
|
-
const texture = {
|
|
846
|
-
texture: gl.createTexture(),
|
|
847
|
-
textureId,
|
|
848
|
-
location: gl.getUniformLocation(program, identifier),
|
|
849
|
-
identifier,
|
|
850
|
-
unit,
|
|
851
|
-
};
|
|
852
|
-
// Bind texture
|
|
853
|
-
gl.activeTexture(textureId);
|
|
854
|
-
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
855
|
-
// Set the parameters.
|
|
856
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
857
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
858
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
859
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
860
|
-
gl.uniform1i(texture.location, unit);
|
|
861
|
-
return texture;
|
|
862
|
-
}
|
|
863
|
-
function setBufferData(index, buffer, value) {
|
|
864
|
-
if (isNumber(value)) {
|
|
865
|
-
buffer.data[index * buffer.size] = value;
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
for (let i = value.length - 1; i >= 0; i--) {
|
|
869
|
-
buffer.data[index * buffer.size + i] = value[i];
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
function setTextureData(gl, texture, data, width, height) {
|
|
874
|
-
gl.activeTexture(texture.textureId);
|
|
875
|
-
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
876
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
|
|
877
|
-
const error = gl.getError();
|
|
878
|
-
if (error !== gl.NO_ERROR) {
|
|
879
|
-
console.log('WebGL texImage2D() failed.', error);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
function setAttribBuffer(gl, buffer) {
|
|
883
|
-
gl.enableVertexAttribArray(buffer.location);
|
|
884
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);
|
|
885
|
-
gl.bufferData(gl.ARRAY_BUFFER, buffer.data, gl.STATIC_DRAW);
|
|
886
|
-
gl.vertexAttribPointer(buffer.location, buffer.size, gl.FLOAT, false, 0, 0);
|
|
887
|
-
}
|
|
888
|
-
function setUniformBuffer(gl, buffer) {
|
|
889
|
-
const location = buffer.location;
|
|
890
|
-
const data = buffer.data;
|
|
891
|
-
switch (buffer.structure) {
|
|
892
|
-
case '2f':
|
|
893
|
-
gl.uniform2f(location, data[0], data[1]);
|
|
894
|
-
break;
|
|
895
|
-
case '2fv':
|
|
896
|
-
gl.uniform2fv(location, data);
|
|
897
|
-
break;
|
|
898
|
-
case '3f':
|
|
899
|
-
gl.uniform3f(location, data[0], data[1], data[2]);
|
|
900
|
-
break;
|
|
901
|
-
case '3fv':
|
|
902
|
-
gl.uniform3fv(location, data);
|
|
903
|
-
break;
|
|
904
|
-
case '4f':
|
|
905
|
-
gl.uniform4f(location, data[0], data[1], data[2], data[3]);
|
|
906
|
-
break;
|
|
907
|
-
case '4fv':
|
|
908
|
-
gl.uniform4fv(location, data);
|
|
909
|
-
break;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
function isNumber(value) {
|
|
913
|
-
return !Number.isNaN(parseFloat(value)) && Number.isFinite(value);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
class WebGLVideoRenderer extends VideoRenderer {
|
|
917
|
-
constructor(canvas) {
|
|
918
|
-
super();
|
|
919
|
-
this.attributes = null;
|
|
920
|
-
this.uniforms = null;
|
|
921
|
-
this.textures = null;
|
|
922
|
-
this.parameters = {
|
|
923
|
-
alpha: true,
|
|
924
|
-
antialias: true,
|
|
925
|
-
depth: true,
|
|
926
|
-
desynchronized: true,
|
|
927
|
-
// Don't try to use software GL rendering!
|
|
928
|
-
failIfMajorPerformanceCaveat: true,
|
|
929
|
-
// Don't trigger discrete GPU in multi-GPU systems
|
|
930
|
-
powerPreference: 'low-power',
|
|
931
|
-
premultipliedAlpha: true,
|
|
932
|
-
// In case we need to capture the resulting output.
|
|
933
|
-
preserveDrawingBuffer: false,
|
|
934
|
-
stencil: true,
|
|
935
|
-
};
|
|
936
|
-
// canvas.style.display = 'block';
|
|
937
|
-
this.canvas = canvas;
|
|
938
|
-
this.width = canvas.width;
|
|
939
|
-
this.height = canvas.height;
|
|
940
|
-
this.gl = this.canvas.getContext('webgl');
|
|
941
|
-
// this.context.setTransform(1, 0, 0, 1, 0, 0);
|
|
942
|
-
this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
|
943
|
-
this.gl.enable(this.gl.DEPTH_TEST);
|
|
944
|
-
this.gl.depthFunc(this.gl.LEQUAL);
|
|
945
|
-
}
|
|
946
|
-
setSize(width, height) {
|
|
947
|
-
this.canvas.width = width;
|
|
948
|
-
this.canvas.height = height;
|
|
949
|
-
this.width = width;
|
|
950
|
-
this.height = height;
|
|
951
|
-
this.gl.viewport(0, 0, width, height);
|
|
952
|
-
}
|
|
953
|
-
clear() {
|
|
954
|
-
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
|
955
|
-
}
|
|
956
|
-
grab() {
|
|
957
|
-
if (isOffscreenCanvas(this.canvas)) {
|
|
958
|
-
return this.canvas.transferToImageBitmap();
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
dispose() {
|
|
962
|
-
// eslint-disable-next-line camelcase
|
|
963
|
-
let context;
|
|
964
|
-
if ((context = this.gl.getExtension('WEBGL_lose_context'))) {
|
|
965
|
-
context.loseContext();
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
render(frame) {
|
|
969
|
-
const { width, height,
|
|
970
|
-
// chromaWidth,
|
|
971
|
-
chromaHeight, dataY, dataU, dataV, strideY, strideU, strideV, } = frame.buffer;
|
|
972
|
-
if (this.gl.isContextLost()) {
|
|
973
|
-
this.gl = this.canvas.getContext('webgl');
|
|
974
|
-
}
|
|
975
|
-
if (width !== this.width || height !== this.height) {
|
|
976
|
-
this.setSize(width, height);
|
|
977
|
-
}
|
|
978
|
-
this.clear();
|
|
979
|
-
if (!this.program) {
|
|
980
|
-
this.program = buildProgram(this.gl);
|
|
981
|
-
// Add the program attributes
|
|
982
|
-
this.attributes = {
|
|
983
|
-
position: buildBuffer(this.gl, this.program, 'attribute', 'aVertexPosition', 2, 'v2'),
|
|
984
|
-
texcoord: buildBuffer(this.gl, this.program, 'attribute', 'aTextureCoord', 2, 'v2'),
|
|
985
|
-
};
|
|
986
|
-
// Add the program uniforms
|
|
987
|
-
this.uniforms = {
|
|
988
|
-
resolution: buildBuffer(this.gl, this.program, 'uniform', 'uResolution', 2, '2f', 1),
|
|
989
|
-
};
|
|
990
|
-
// Add the program texture
|
|
991
|
-
this.textures = {
|
|
992
|
-
y: buildTexture(this.gl, this.program, 'uniform', 'uTextureY', this.gl.TEXTURE0, 0),
|
|
993
|
-
u: buildTexture(this.gl, this.program, 'uniform', 'uTextureU', this.gl.TEXTURE1, 1),
|
|
994
|
-
v: buildTexture(this.gl, this.program, 'uniform', 'uTextureV', this.gl.TEXTURE2, 2),
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
let buffer;
|
|
998
|
-
let texture;
|
|
999
|
-
const cropLeft = 0;
|
|
1000
|
-
const cropTop = 0;
|
|
1001
|
-
const cropWidth = width;
|
|
1002
|
-
const cropHeight = height;
|
|
1003
|
-
const textureX0 = cropLeft;
|
|
1004
|
-
const textureX1 = cropLeft + cropWidth;
|
|
1005
|
-
const textureY0 = cropTop + cropHeight;
|
|
1006
|
-
const textureY1 = cropTop;
|
|
1007
|
-
// 4 vertex, 1(x0,y0), 2(x1,y0), 3(x1,y1), 4(x0,y1)
|
|
1008
|
-
// 0: 1,2,4/4,2,3
|
|
1009
|
-
// 90: 2,3,1/1,3,4
|
|
1010
|
-
// 180: 3,4,2/2,4,1
|
|
1011
|
-
// 270: 4,1,3/3,1,2
|
|
1012
|
-
// top-left (0, 0)
|
|
1013
|
-
// top-right (1, 0)
|
|
1014
|
-
// bottom-right (1, 1)
|
|
1015
|
-
// bottom-left (0, 1)
|
|
1016
|
-
const position = new Float32Array([
|
|
1017
|
-
textureX0,
|
|
1018
|
-
textureY0,
|
|
1019
|
-
textureX1,
|
|
1020
|
-
textureY0,
|
|
1021
|
-
textureX0,
|
|
1022
|
-
textureY1,
|
|
1023
|
-
textureX0,
|
|
1024
|
-
textureY1,
|
|
1025
|
-
textureX1,
|
|
1026
|
-
textureY0,
|
|
1027
|
-
textureX1,
|
|
1028
|
-
textureY1,
|
|
1029
|
-
]);
|
|
1030
|
-
// top-left (0, 1)
|
|
1031
|
-
// top-right (1, 1)
|
|
1032
|
-
// bottom-right (1, 0)
|
|
1033
|
-
// bottom-left (0, 0)
|
|
1034
|
-
const textureX = width / strideY;
|
|
1035
|
-
const texcoord = new Float32Array([0, 1, textureX, 1, 0, 0, 0, 0, textureX, 1, textureX, 0]);
|
|
1036
|
-
// Build attribute buffers
|
|
1037
|
-
const attributes = Object.keys(this.attributes);
|
|
1038
|
-
attributes.forEach((attribute) => {
|
|
1039
|
-
buffer = this.attributes[attribute];
|
|
1040
|
-
// Update attribute buffer data
|
|
1041
|
-
switch (attribute) {
|
|
1042
|
-
case 'position':
|
|
1043
|
-
buffer.data = new Float32Array(position.length);
|
|
1044
|
-
setBufferData(0, buffer, position);
|
|
1045
|
-
break;
|
|
1046
|
-
case 'texcoord':
|
|
1047
|
-
buffer.data = new Float32Array(texcoord.length);
|
|
1048
|
-
setBufferData(0, buffer, texcoord);
|
|
1049
|
-
break;
|
|
1050
|
-
default:
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
setAttribBuffer(this.gl, buffer);
|
|
1054
|
-
});
|
|
1055
|
-
// Build uniform buffers
|
|
1056
|
-
setBufferData(0, this.uniforms.resolution, [width, height]);
|
|
1057
|
-
// Update uniforms
|
|
1058
|
-
const uniforms = Object.keys(this.uniforms);
|
|
1059
|
-
uniforms.forEach((uniform) => {
|
|
1060
|
-
buffer = this.uniforms[uniform];
|
|
1061
|
-
// Update uniform buffer data
|
|
1062
|
-
setUniformBuffer(this.gl, buffer);
|
|
1063
|
-
});
|
|
1064
|
-
// Update textures
|
|
1065
|
-
const textures = Object.keys(this.textures);
|
|
1066
|
-
textures.forEach((name) => {
|
|
1067
|
-
texture = this.textures[name];
|
|
1068
|
-
switch (name) {
|
|
1069
|
-
case 'y':
|
|
1070
|
-
setTextureData(this.gl, texture, dataY, strideY, height);
|
|
1071
|
-
break;
|
|
1072
|
-
case 'u':
|
|
1073
|
-
setTextureData(this.gl, texture, dataU, strideU, chromaHeight);
|
|
1074
|
-
break;
|
|
1075
|
-
case 'v':
|
|
1076
|
-
setTextureData(this.gl, texture, dataV, strideV, chromaHeight);
|
|
1077
|
-
break;
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
// Draw those lovely triangles
|
|
1081
|
-
this.gl.drawArrays(this.gl.TRIANGLES, 0, position.length / 2); // in vec2
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
function isOffscreenCanvas(val) {
|
|
1085
|
-
return typeof OffscreenCanvas !== 'undefined' && val instanceof OffscreenCanvas;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
/// <reference types="emscripten" />
|
|
1089
|
-
const DEFAULT_HEAP_TYPE = 'HEAPU8';
|
|
1090
|
-
function createMemoryAllocator(native, type) {
|
|
1091
|
-
return (length) => {
|
|
1092
|
-
const { BYTES_PER_ELEMENT } = native[type || DEFAULT_HEAP_TYPE];
|
|
1093
|
-
const bytes = length * BYTES_PER_ELEMENT;
|
|
1094
|
-
const pointer = native._malloc(bytes);
|
|
1095
|
-
const tuple = () => [pointer, bytes];
|
|
1096
|
-
// malloc() might cause wasm memory growth, which will detach 'heap' buffer
|
|
1097
|
-
const view = (normalize = true) => {
|
|
1098
|
-
const v = native[type || DEFAULT_HEAP_TYPE].subarray(pointer / BYTES_PER_ELEMENT, pointer + length);
|
|
1099
|
-
if (normalize && typeof v.constructor !== 'undefined') {
|
|
1100
|
-
return new v.constructor(v.buffer, v.byteOffset, length);
|
|
1101
|
-
}
|
|
1102
|
-
console.log('unnormalized array view has unexpected length');
|
|
1103
|
-
return v;
|
|
1104
|
-
};
|
|
1105
|
-
const free = () => native._free(pointer);
|
|
1106
|
-
return {
|
|
1107
|
-
BYTES_PER_ELEMENT,
|
|
1108
|
-
pointer,
|
|
1109
|
-
length,
|
|
1110
|
-
bytes,
|
|
1111
|
-
tuple,
|
|
1112
|
-
view,
|
|
1113
|
-
free,
|
|
1114
|
-
};
|
|
1115
|
-
};
|
|
1116
|
-
}
|
|
1117
|
-
function createMemoryViewer(native, type) {
|
|
1118
|
-
return (begin, length, normalize = true) => {
|
|
1119
|
-
const heap = native[type || DEFAULT_HEAP_TYPE];
|
|
1120
|
-
const { BYTES_PER_ELEMENT } = heap;
|
|
1121
|
-
const view = heap.subarray(begin / BYTES_PER_ELEMENT, begin + length);
|
|
1122
|
-
if (normalize && typeof view.constructor !== 'undefined') {
|
|
1123
|
-
return new view.constructor(view.buffer, view.byteOffset, length);
|
|
1124
|
-
}
|
|
1125
|
-
console.log('unnormalized array view has unexpected length');
|
|
1126
|
-
return view;
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
function open(name, store) {
|
|
1131
|
-
const request = indexedDB.open(name);
|
|
1132
|
-
request.onupgradeneeded = () => request.result.createObjectStore(store);
|
|
1133
|
-
const promise = make(request);
|
|
1134
|
-
return (mode, callback) => promise.then((db) => callback(db.transaction(store, mode).objectStore(store)));
|
|
1135
|
-
}
|
|
1136
|
-
const LAZY_INSTANCE = (factory) => {
|
|
1137
|
-
let instance;
|
|
1138
|
-
return (...args) => {
|
|
1139
|
-
if (!instance) {
|
|
1140
|
-
instance = factory();
|
|
1141
|
-
}
|
|
1142
|
-
return instance(...args);
|
|
1143
|
-
};
|
|
1144
|
-
};
|
|
1145
|
-
const LAZY_GLOBAL_STORE = LAZY_INSTANCE(() => open('LINE-UI', 'key-value'));
|
|
1146
|
-
function get(key, store = LAZY_GLOBAL_STORE) {
|
|
1147
|
-
return store('readonly', (store) => make(store.get(key)));
|
|
1148
|
-
}
|
|
1149
|
-
function clear(store = LAZY_GLOBAL_STORE) {
|
|
1150
|
-
return store('readwrite', (store) => {
|
|
1151
|
-
store.clear();
|
|
1152
|
-
return make(store.transaction);
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
function make(request) {
|
|
1156
|
-
return new Promise((resolve, reject) => {
|
|
1157
|
-
// @ts-ignore - file size hacks
|
|
1158
|
-
request.oncomplete = request.onsuccess = () => resolve(request.result);
|
|
1159
|
-
// @ts-ignore - file size hacks
|
|
1160
|
-
request.onabort = request.onerror = () => reject(request.error);
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
/// <reference types="emscripten" />
|
|
1165
|
-
var SFUTransportType;
|
|
1166
|
-
(function (SFUTransportType) {
|
|
1167
|
-
SFUTransportType[SFUTransportType["kAudio"] = 1] = "kAudio";
|
|
1168
|
-
SFUTransportType[SFUTransportType["kVideo"] = 2] = "kVideo";
|
|
1169
|
-
SFUTransportType[SFUTransportType["kData"] = 3] = "kData";
|
|
1170
|
-
SFUTransportType[SFUTransportType["kAnnotation"] = 4] = "kAnnotation";
|
|
1171
|
-
SFUTransportType[SFUTransportType["kMessage"] = 5] = "kMessage";
|
|
1172
|
-
})(SFUTransportType || (SFUTransportType = {}));
|
|
1173
|
-
var SFUTransportOrient;
|
|
1174
|
-
(function (SFUTransportOrient) {
|
|
1175
|
-
SFUTransportOrient[SFUTransportOrient["kSend"] = 1] = "kSend";
|
|
1176
|
-
SFUTransportOrient[SFUTransportOrient["kRecv"] = 2] = "kRecv";
|
|
1177
|
-
SFUTransportOrient[SFUTransportOrient["kBoth"] = 3] = "kBoth";
|
|
1178
|
-
})(SFUTransportOrient || (SFUTransportOrient = {}));
|
|
1179
|
-
var SFUUserRole;
|
|
1180
|
-
(function (SFUUserRole) {
|
|
1181
|
-
SFUUserRole[SFUUserRole["kInteractive"] = 1] = "kInteractive";
|
|
1182
|
-
SFUUserRole[SFUUserRole["kBroadcast"] = 2] = "kBroadcast";
|
|
1183
|
-
})(SFUUserRole || (SFUUserRole = {}));
|
|
1184
|
-
var SFUReason;
|
|
1185
|
-
(function (SFUReason) {
|
|
1186
|
-
SFUReason[SFUReason["kOK"] = 0] = "kOK";
|
|
1187
|
-
SFUReason[SFUReason["kError"] = 320200] = "kError";
|
|
1188
|
-
SFUReason[SFUReason["kExisted"] = 320201] = "kExisted";
|
|
1189
|
-
SFUReason[SFUReason["kNetworkError"] = 320202] = "kNetworkError";
|
|
1190
|
-
SFUReason[SFUReason["kMCFailed"] = 320203] = "kMCFailed";
|
|
1191
|
-
SFUReason[SFUReason["kMSSFailed"] = 320204] = "kMSSFailed";
|
|
1192
|
-
SFUReason[SFUReason["kMSSLost"] = 320205] = "kMSSLost";
|
|
1193
|
-
SFUReason[SFUReason["kAuthFailed"] = 320206] = "kAuthFailed";
|
|
1194
|
-
SFUReason[SFUReason["kLoadFailed"] = 320207] = "kLoadFailed";
|
|
1195
|
-
SFUReason[SFUReason["kNoImplemented"] = 320208] = "kNoImplemented";
|
|
1196
|
-
})(SFUReason || (SFUReason = {}));
|
|
1197
|
-
var SFUTransportStatus;
|
|
1198
|
-
(function (SFUTransportStatus) {
|
|
1199
|
-
SFUTransportStatus[SFUTransportStatus["kReconnecting"] = 320100] = "kReconnecting";
|
|
1200
|
-
SFUTransportStatus[SFUTransportStatus["kReconnectOK"] = 320101] = "kReconnectOK";
|
|
1201
|
-
SFUTransportStatus[SFUTransportStatus["kReconnectNG"] = 320102] = "kReconnectNG";
|
|
1202
|
-
})(SFUTransportStatus || (SFUTransportStatus = {}));
|
|
1203
|
-
var SFUEndpointStatus;
|
|
1204
|
-
(function (SFUEndpointStatus) {
|
|
1205
|
-
SFUEndpointStatus[SFUEndpointStatus["kHandshakeOK"] = 100] = "kHandshakeOK";
|
|
1206
|
-
SFUEndpointStatus[SFUEndpointStatus["kHandshakeNG"] = 101] = "kHandshakeNG";
|
|
1207
|
-
SFUEndpointStatus[SFUEndpointStatus["kConnectOK"] = 102] = "kConnectOK";
|
|
1208
|
-
SFUEndpointStatus[SFUEndpointStatus["kConnectNG"] = 103] = "kConnectNG";
|
|
1209
|
-
})(SFUEndpointStatus || (SFUEndpointStatus = {}));
|
|
1210
|
-
var TransportType;
|
|
1211
|
-
(function (TransportType) {
|
|
1212
|
-
TransportType[TransportType["kSignal"] = 0] = "kSignal";
|
|
1213
|
-
TransportType[TransportType["kData"] = 1] = "kData";
|
|
1214
|
-
})(TransportType || (TransportType = {}));
|
|
1215
|
-
var VideoEncodeMode;
|
|
1216
|
-
(function (VideoEncodeMode) {
|
|
1217
|
-
VideoEncodeMode[VideoEncodeMode["kVeryfast"] = 0] = "kVeryfast";
|
|
1218
|
-
VideoEncodeMode[VideoEncodeMode["kUltrafast"] = 1] = "kUltrafast";
|
|
1219
|
-
VideoEncodeMode[VideoEncodeMode["kUltrafastPlus"] = 2] = "kUltrafastPlus";
|
|
1220
|
-
})(VideoEncodeMode || (VideoEncodeMode = {}));
|
|
1221
|
-
// SFUTransportType
|
|
1222
|
-
// type VideoChannelType = SFUTransportType;
|
|
1223
|
-
var VideoChannelType;
|
|
1224
|
-
(function (VideoChannelType) {
|
|
1225
|
-
// kAudio = 1,
|
|
1226
|
-
VideoChannelType[VideoChannelType["kVideo"] = 2] = "kVideo";
|
|
1227
|
-
VideoChannelType[VideoChannelType["kData"] = 3] = "kData";
|
|
1228
|
-
VideoChannelType[VideoChannelType["kAnnotation"] = 4] = "kAnnotation";
|
|
1229
|
-
VideoChannelType[VideoChannelType["kMessage"] = 5] = "kMessage";
|
|
1230
|
-
})(VideoChannelType || (VideoChannelType = {}));
|
|
1231
|
-
// SFUTransportOrient
|
|
1232
|
-
// type VideoChannelOrient = SFUTransportOrient;
|
|
1233
|
-
var VideoChannelOrient;
|
|
1234
|
-
(function (VideoChannelOrient) {
|
|
1235
|
-
VideoChannelOrient[VideoChannelOrient["kInactive"] = 0] = "kInactive";
|
|
1236
|
-
VideoChannelOrient[VideoChannelOrient["kSend"] = 1] = "kSend";
|
|
1237
|
-
VideoChannelOrient[VideoChannelOrient["kRecv"] = 2] = "kRecv";
|
|
1238
|
-
VideoChannelOrient[VideoChannelOrient["kBoth"] = 3] = "kBoth";
|
|
1239
|
-
})(VideoChannelOrient || (VideoChannelOrient = {}));
|
|
1240
|
-
var VideoChannelMode;
|
|
1241
|
-
(function (VideoChannelMode) {
|
|
1242
|
-
VideoChannelMode[VideoChannelMode["kNormal"] = 0] = "kNormal";
|
|
1243
|
-
VideoChannelMode[VideoChannelMode["kSmooth"] = 1] = "kSmooth";
|
|
1244
|
-
})(VideoChannelMode || (VideoChannelMode = {}));
|
|
1245
|
-
var VideoRotation;
|
|
1246
|
-
(function (VideoRotation) {
|
|
1247
|
-
VideoRotation[VideoRotation["kRotation_0"] = 0] = "kRotation_0";
|
|
1248
|
-
VideoRotation[VideoRotation["kRotation_90"] = 90] = "kRotation_90";
|
|
1249
|
-
VideoRotation[VideoRotation["kRotation_180"] = 180] = "kRotation_180";
|
|
1250
|
-
VideoRotation[VideoRotation["kRotation_270"] = 270] = "kRotation_270";
|
|
1251
|
-
})(VideoRotation || (VideoRotation = {}));
|
|
1252
|
-
var DataType;
|
|
1253
|
-
(function (DataType) {
|
|
1254
|
-
DataType[DataType["kCoopration"] = 1] = "kCoopration";
|
|
1255
|
-
DataType[DataType["kRemoteControl"] = 2] = "kRemoteControl";
|
|
1256
|
-
DataType[DataType["kSubtitleRealtime"] = 3] = "kSubtitleRealtime";
|
|
1257
|
-
DataType[DataType["kSubtitleRecord"] = 4] = "kSubtitleRecord";
|
|
1258
|
-
})(DataType || (DataType = {}));
|
|
1259
|
-
var SubtitleSentenceAction;
|
|
1260
|
-
(function (SubtitleSentenceAction) {
|
|
1261
|
-
SubtitleSentenceAction[SubtitleSentenceAction["kCommon"] = 0] = "kCommon";
|
|
1262
|
-
SubtitleSentenceAction[SubtitleSentenceAction["kShareStart"] = 1] = "kShareStart";
|
|
1263
|
-
SubtitleSentenceAction[SubtitleSentenceAction["kShareEnd"] = 2] = "kShareEnd";
|
|
1264
|
-
SubtitleSentenceAction[SubtitleSentenceAction["kCoopStart"] = 3] = "kCoopStart";
|
|
1265
|
-
SubtitleSentenceAction[SubtitleSentenceAction["kCoopEnd"] = 4] = "kCoopEnd";
|
|
1266
|
-
SubtitleSentenceAction[SubtitleSentenceAction["kServiceOver"] = 5] = "kServiceOver";
|
|
1267
|
-
})(SubtitleSentenceAction || (SubtitleSentenceAction = {}));
|
|
1268
|
-
var SubtitleSentenceStatus;
|
|
1269
|
-
(function (SubtitleSentenceStatus) {
|
|
1270
|
-
SubtitleSentenceStatus[SubtitleSentenceStatus["kStart"] = 0] = "kStart";
|
|
1271
|
-
SubtitleSentenceStatus[SubtitleSentenceStatus["kChanging"] = 1] = "kChanging";
|
|
1272
|
-
SubtitleSentenceStatus[SubtitleSentenceStatus["kEnd"] = 2] = "kEnd";
|
|
1273
|
-
})(SubtitleSentenceStatus || (SubtitleSentenceStatus = {}));
|
|
1274
|
-
|
|
1275
|
-
let native;
|
|
1276
|
-
let alloc;
|
|
1277
|
-
let view;
|
|
1278
|
-
// visibilityState === 'hidden'
|
|
1279
|
-
let suspended$1 = false;
|
|
1280
|
-
let loglevel = 2;
|
|
1281
|
-
let timer_factory;
|
|
1282
|
-
const DEFAULT_FRAME_RATE = 33;
|
|
1283
|
-
// const supportOffscreen = typeof OffscreenCanvas !== 'undefined';
|
|
1284
|
-
const encode_coef = 0.3;
|
|
1285
|
-
let encode_avg = 0;
|
|
1286
|
-
let encode_busy = false;
|
|
1287
|
-
const isWechatWorker = typeof worker !== 'undefined';
|
|
1288
|
-
if (isWechatWorker) {
|
|
1289
|
-
worker.onMessage((data) => {
|
|
1290
|
-
worker.onmessage && worker.onmessage({ data });
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
// eslint-disable-next-line no-restricted-globals
|
|
1294
|
-
const channel = new MojoChannel(isWechatWorker ? worker : self);
|
|
1295
|
-
const engine = channel.receiver('video-engine-host');
|
|
1296
|
-
engine.implement('init', (reply, [url, verbose = false]) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1297
|
-
let factory;
|
|
1298
|
-
let wasmUrl;
|
|
1299
|
-
if (isWechatWorker) {
|
|
1300
|
-
const trailingSlash = (v) => (!v || v.endsWith('/') ? v : `${v}/`);
|
|
1301
|
-
const [scriptUrl, wasmDir = ''] = url.split('?');
|
|
1302
|
-
const scriptName = scriptUrl.substring(scriptUrl.lastIndexOf('/') + 1);
|
|
1303
|
-
factory = require(scriptUrl);
|
|
1304
|
-
wasmUrl = trailingSlash(wasmDir) + scriptName.replace(/\.js$/, '.wasm');
|
|
1305
|
-
}
|
|
1306
|
-
else {
|
|
1307
|
-
importScripts(url);
|
|
1308
|
-
factory = LIBSVC;
|
|
1309
|
-
wasmUrl = url.replace(/\.js$/, '.wasm');
|
|
1310
|
-
}
|
|
1311
|
-
const wasmBinary = isWechatWorker
|
|
1312
|
-
? wasmUrl
|
|
1313
|
-
: yield get(wasmUrl).catch(() => null);
|
|
1314
|
-
// should clear cache if init failed
|
|
1315
|
-
native = yield factory({ wasmBinary }).catch((error) => {
|
|
1316
|
-
clear();
|
|
1317
|
-
throw error;
|
|
1318
|
-
});
|
|
1319
|
-
alloc = createMemoryAllocator(native);
|
|
1320
|
-
view = createMemoryViewer(native);
|
|
1321
|
-
console.log(`LIBSVC: ${native.VideoChannel.Version()}`);
|
|
1322
|
-
timer_factory = new native.TimerFactory({
|
|
1323
|
-
start: (callback, delay) => setInterval(() => native.TimerFactory.Run(callback), delay),
|
|
1324
|
-
stop: (id) => {
|
|
1325
|
-
clearInterval(id);
|
|
1326
|
-
},
|
|
1327
|
-
});
|
|
1328
|
-
native.VideoChannel.Init(timer_factory, (level, msg) => {
|
|
1329
|
-
if (verbose || (level >= loglevel && true)) {
|
|
1330
|
-
console.log(msg);
|
|
1331
|
-
}
|
|
1332
|
-
});
|
|
1333
|
-
reply.resolve();
|
|
1334
|
-
}));
|
|
1335
|
-
engine.implement('exit', (reply) => {
|
|
1336
|
-
native.VideoChannel.Exit();
|
|
1337
|
-
timer_factory.delete();
|
|
1338
|
-
reply.resolve();
|
|
1339
|
-
});
|
|
1340
|
-
engine.implement('debug', (reply, [level]) => {
|
|
1341
|
-
loglevel = level;
|
|
1342
|
-
reply.resolve();
|
|
1343
|
-
});
|
|
1344
|
-
engine.implement('suspend', (reply) => {
|
|
1345
|
-
suspended$1 = true;
|
|
1346
|
-
reply.resolve();
|
|
1347
|
-
});
|
|
1348
|
-
engine.implement('resume', (reply) => {
|
|
1349
|
-
suspended$1 = false;
|
|
1350
|
-
reply.resolve();
|
|
1351
|
-
});
|
|
1352
|
-
engine.implement('setup', (reply, [transportPR, type = VideoChannelType.kVideo]) => {
|
|
1353
|
-
const cleanup = [];
|
|
1354
|
-
const transport_host = channel.remote(transportPR);
|
|
1355
|
-
const video_channel_host = channel.receiver();
|
|
1356
|
-
cleanup.unshift(() => transport_host.break());
|
|
1357
|
-
// setup transport proxy
|
|
1358
|
-
const sfu_transport_proxy = new native.SFUTransportProxy({
|
|
1359
|
-
Connect: (config) => {
|
|
1360
|
-
const [pointer, length] = config.desc;
|
|
1361
|
-
const desc = view(pointer, length).slice();
|
|
1362
|
-
transport_host
|
|
1363
|
-
.make('connect')
|
|
1364
|
-
.transfer(desc.buffer)
|
|
1365
|
-
.post(Object.assign(Object.assign({}, config), { desc }));
|
|
1366
|
-
},
|
|
1367
|
-
// main thread will disconnect the real sfu transport
|
|
1368
|
-
Disconnect: () => {
|
|
1369
|
-
if (!video_channel_host.valid)
|
|
1370
|
-
return;
|
|
1371
|
-
transport_host.make('disconnect').post();
|
|
1372
|
-
},
|
|
1373
|
-
Subscribe: (request, remove) => {
|
|
1374
|
-
if (!video_channel_host.valid)
|
|
1375
|
-
return;
|
|
1376
|
-
const memory = request
|
|
1377
|
-
.map((v) => v.desc)
|
|
1378
|
-
.map(([pointer, length]) => view(pointer, length).slice());
|
|
1379
|
-
transport_host
|
|
1380
|
-
.make('subscribe')
|
|
1381
|
-
.transfer(...memory.map((v) => v.buffer))
|
|
1382
|
-
.post(request.map((v, i) => (Object.assign(Object.assign({}, v), { desc: memory[i] }))), remove.map((v) => v.ccid));
|
|
1383
|
-
},
|
|
1384
|
-
SendData: (data, meta) => {
|
|
1385
|
-
const memory = data.map(([pointer, length]) => view(pointer, length).slice());
|
|
1386
|
-
transport_host
|
|
1387
|
-
.make('senddata')
|
|
1388
|
-
.transfer(...memory.map((v) => v.buffer))
|
|
1389
|
-
.post(memory, meta);
|
|
1390
|
-
},
|
|
1391
|
-
SendSingal: (data, length, ccid) => {
|
|
1392
|
-
const memory = view(data, length).slice();
|
|
1393
|
-
transport_host.make('sendsignal').transfer(memory.buffer).post(memory, ccid);
|
|
1394
|
-
},
|
|
1395
|
-
});
|
|
1396
|
-
// setup transport
|
|
1397
|
-
const video_channel = new native.VideoChannel(type);
|
|
1398
|
-
video_channel.SetTransport(sfu_transport_proxy);
|
|
1399
|
-
// setup source
|
|
1400
|
-
const source = new native.FrameSource();
|
|
1401
|
-
video_channel.SetSource(source);
|
|
1402
|
-
let encoding = false;
|
|
1403
|
-
let fps = DEFAULT_FRAME_RATE;
|
|
1404
|
-
const startFrameSource = () => {
|
|
1405
|
-
const id = setInterval(() => {
|
|
1406
|
-
if (!source_host || !source_host.valid)
|
|
1407
|
-
return;
|
|
1408
|
-
source_host
|
|
1409
|
-
.make('capture')
|
|
1410
|
-
.invoke()
|
|
1411
|
-
.then((buffer) => buffer && encode(buffer));
|
|
1412
|
-
}, 1000 / fps);
|
|
1413
|
-
return () => clearInterval(id);
|
|
1414
|
-
};
|
|
1415
|
-
let stopFrameSource;
|
|
1416
|
-
const onstop = () => {
|
|
1417
|
-
stopFrameSource && stopFrameSource();
|
|
1418
|
-
stopFrameSource = null;
|
|
1419
|
-
};
|
|
1420
|
-
const onstart = () => {
|
|
1421
|
-
fps && (stopFrameSource = startFrameSource());
|
|
1422
|
-
};
|
|
1423
|
-
source.on('onstart', () => {
|
|
1424
|
-
encoding = true;
|
|
1425
|
-
onstop();
|
|
1426
|
-
onstart();
|
|
1427
|
-
source_host && source_host.make('onstart').post();
|
|
1428
|
-
});
|
|
1429
|
-
source.on('onstop', () => {
|
|
1430
|
-
encoding = false;
|
|
1431
|
-
onstop();
|
|
1432
|
-
source_host && source_host.make('onstop').post();
|
|
1433
|
-
});
|
|
1434
|
-
// setup sink
|
|
1435
|
-
const sink = new native.FrameSink();
|
|
1436
|
-
video_channel.SetSink(sink);
|
|
1437
|
-
const toBufferBuilder = (frame, copy = true) => {
|
|
1438
|
-
const { width, height, strideY, strideU, strideV } = frame;
|
|
1439
|
-
const { dataY, dataU, dataV } = frame;
|
|
1440
|
-
const viewY = view(dataY, strideY * height);
|
|
1441
|
-
const viewU = view(dataU, strideU * Math.floor((height + 1) / 2));
|
|
1442
|
-
const viewV = view(dataV, strideV * Math.floor((height + 1) / 2));
|
|
1443
|
-
const builder = {
|
|
1444
|
-
width,
|
|
1445
|
-
height,
|
|
1446
|
-
strideY,
|
|
1447
|
-
strideU,
|
|
1448
|
-
strideV,
|
|
1449
|
-
dataY: copy ? viewY.slice() : viewY,
|
|
1450
|
-
dataU: copy ? viewU.slice() : viewU,
|
|
1451
|
-
dataV: copy ? viewV.slice() : viewV,
|
|
1452
|
-
};
|
|
1453
|
-
return builder;
|
|
1454
|
-
};
|
|
1455
|
-
sink.on('onframe', (ccid, frame) => {
|
|
1456
|
-
if (suspended$1)
|
|
1457
|
-
return;
|
|
1458
|
-
const sink = sink_proxies.get(ccid);
|
|
1459
|
-
if (!sink)
|
|
1460
|
-
return;
|
|
1461
|
-
const { renderer, loadeddata } = sink;
|
|
1462
|
-
if (!loadeddata) {
|
|
1463
|
-
sink.host.make('onloadeddata').post();
|
|
1464
|
-
sink.loadeddata = true;
|
|
1465
|
-
}
|
|
1466
|
-
if (frame.width !== sink.width || frame.height !== sink.height) {
|
|
1467
|
-
sink.width = frame.width;
|
|
1468
|
-
sink.height = frame.height;
|
|
1469
|
-
sink.host.make('onframechanged').post(sink.width, sink.height);
|
|
1470
|
-
}
|
|
1471
|
-
if (renderer) {
|
|
1472
|
-
// YUV format
|
|
1473
|
-
const buffer = new I420Buffer(toBufferBuilder(frame, false));
|
|
1474
|
-
// ABGR format
|
|
1475
|
-
// const { width, height } = frame;
|
|
1476
|
-
// const memory = alloc(width * height * 4);
|
|
1477
|
-
// frame.ToABGR(memory.pointer);
|
|
1478
|
-
// const data = view(memory.pointer, memory.bytes);
|
|
1479
|
-
// const buffer = new RGBBuffer({ width, height, data });
|
|
1480
|
-
// renderer.onFrame(new VideoFrame({ buffer }), true);
|
|
1481
|
-
// memory.free();
|
|
1482
|
-
renderer.onFrame(new VideoFrame({ buffer }), true);
|
|
1483
|
-
return;
|
|
1484
|
-
}
|
|
1485
|
-
// YUV format
|
|
1486
|
-
const builder = toBufferBuilder(frame);
|
|
1487
|
-
sink.host
|
|
1488
|
-
.make('onframe')
|
|
1489
|
-
.transfer(builder.dataY.buffer, builder.dataU.buffer, builder.dataV.buffer)
|
|
1490
|
-
.post(builder);
|
|
1491
|
-
});
|
|
1492
|
-
let source_host;
|
|
1493
|
-
const sink_proxies = new Map();
|
|
1494
|
-
// setup cleanup
|
|
1495
|
-
cleanup.unshift(() => {
|
|
1496
|
-
video_channel.Leave();
|
|
1497
|
-
video_channel.delete();
|
|
1498
|
-
sfu_transport_proxy.delete();
|
|
1499
|
-
source.delete();
|
|
1500
|
-
sink.delete();
|
|
1501
|
-
source_host && source_host.break();
|
|
1502
|
-
source_host = null;
|
|
1503
|
-
sink_proxies.forEach((v) => v.host.break());
|
|
1504
|
-
sink_proxies.clear();
|
|
1505
|
-
});
|
|
1506
|
-
// impl video channel host
|
|
1507
|
-
let channel_config;
|
|
1508
|
-
const pending_actions = [];
|
|
1509
|
-
video_channel_host.implement('mode', (reply, [mode]) => {
|
|
1510
|
-
native.VideoChannel.EncodeMode(mode);
|
|
1511
|
-
reply.resolve();
|
|
1512
|
-
});
|
|
1513
|
-
video_channel_host.implement('open', (reply, [config]) => {
|
|
1514
|
-
channel_config = config;
|
|
1515
|
-
if (channel_config.orient !== VideoChannelOrient.kInactive) {
|
|
1516
|
-
video_channel.Join(config);
|
|
1517
|
-
}
|
|
1518
|
-
reply.resolve();
|
|
1519
|
-
});
|
|
1520
|
-
video_channel_host.implement('subscribe', (reply, [request, remove]) => {
|
|
1521
|
-
if (connecting || channel_config.orient === VideoChannelOrient.kInactive) {
|
|
1522
|
-
pending_actions.push(() => video_channel.Subscribe(request, remove));
|
|
1523
|
-
}
|
|
1524
|
-
else {
|
|
1525
|
-
video_channel.Subscribe(request, remove);
|
|
1526
|
-
}
|
|
1527
|
-
remove.forEach((ccid) => {
|
|
1528
|
-
const sink = sink_proxies.get(ccid);
|
|
1529
|
-
if (!sink)
|
|
1530
|
-
return;
|
|
1531
|
-
sink.renderer && sink.renderer.clear();
|
|
1532
|
-
sink.host.break();
|
|
1533
|
-
sink_proxies.delete(ccid);
|
|
1534
|
-
});
|
|
1535
|
-
reply.resolve();
|
|
1536
|
-
});
|
|
1537
|
-
video_channel_host.implement('sendstats', (reply) => {
|
|
1538
|
-
reply.resolve(encoding ? video_channel.SendStats() : null);
|
|
1539
|
-
});
|
|
1540
|
-
video_channel_host.implement('recvstats', (reply, [ccid]) => {
|
|
1541
|
-
reply.resolve(video_channel.RecvStats(ccid));
|
|
1542
|
-
});
|
|
1543
|
-
video_channel_host.implement('sendreport', (reply) => {
|
|
1544
|
-
reply.resolve(encoding ? video_channel.SendReport() : null);
|
|
1545
|
-
});
|
|
1546
|
-
video_channel_host.implement('recvreport', (reply, [ccid]) => {
|
|
1547
|
-
reply.resolve(video_channel.RecvReport(ccid));
|
|
1548
|
-
});
|
|
1549
|
-
const encode = (buffer) => {
|
|
1550
|
-
const tick = Date.now();
|
|
1551
|
-
const { width, height, data } = buffer;
|
|
1552
|
-
const stride = width * 4;
|
|
1553
|
-
const memory = alloc(stride * height);
|
|
1554
|
-
// copy memory to wasm
|
|
1555
|
-
memory.view().set(data);
|
|
1556
|
-
// convert ABGR to I420 via libyuv
|
|
1557
|
-
const frame = native.I420Buffer.FromABGR(width, height, memory.pointer, stride);
|
|
1558
|
-
// release ABGR heap
|
|
1559
|
-
memory.free();
|
|
1560
|
-
// pass frame to source
|
|
1561
|
-
source.OnFrame(frame);
|
|
1562
|
-
// release frame
|
|
1563
|
-
frame.delete();
|
|
1564
|
-
const since = Date.now() - tick;
|
|
1565
|
-
encode_avg = encode_coef * since + (1 - encode_coef) * encode_avg;
|
|
1566
|
-
if (!encode_busy && encode_avg > 120) {
|
|
1567
|
-
encode_busy = true;
|
|
1568
|
-
source_host && source_host.make('onbusy').post(encode_busy);
|
|
1569
|
-
}
|
|
1570
|
-
if (encode_busy && encode_avg < 60) {
|
|
1571
|
-
encode_busy = false;
|
|
1572
|
-
source_host && source_host.make('onbusy').post(encode_busy);
|
|
1573
|
-
}
|
|
1574
|
-
};
|
|
1575
|
-
// source & sink related
|
|
1576
|
-
video_channel_host.implement('encode', (reply, [buffer]) => {
|
|
1577
|
-
reply.resolve();
|
|
1578
|
-
encode(buffer);
|
|
1579
|
-
});
|
|
1580
|
-
video_channel_host.implement('setsource', (reply, [sourcePR, frameRate]) => {
|
|
1581
|
-
if (source_host) {
|
|
1582
|
-
source_host.break();
|
|
1583
|
-
source_host = null;
|
|
1584
|
-
}
|
|
1585
|
-
if (sourcePR) {
|
|
1586
|
-
source_host = channel.remote(sourcePR);
|
|
1587
|
-
}
|
|
1588
|
-
onframerate(frameRate);
|
|
1589
|
-
reply.resolve(encoding);
|
|
1590
|
-
});
|
|
1591
|
-
video_channel_host.implement('setsink', (reply, [ccid, sinkPR, id]) => {
|
|
1592
|
-
reply.resolve();
|
|
1593
|
-
let sink = sink_proxies.get(ccid);
|
|
1594
|
-
if (sink) {
|
|
1595
|
-
sink.host.break();
|
|
1596
|
-
sink_proxies.delete(ccid);
|
|
1597
|
-
}
|
|
1598
|
-
if (!sinkPR)
|
|
1599
|
-
return;
|
|
1600
|
-
sink = { host: channel.remote(sinkPR) };
|
|
1601
|
-
sink_proxies.set(ccid, sink);
|
|
1602
|
-
if (!id)
|
|
1603
|
-
return;
|
|
1604
|
-
{
|
|
1605
|
-
const duplicate = Array.from(sink_proxies.entries()).filter(([, value]) => value.renderer === renderers[id - 1]);
|
|
1606
|
-
if (duplicate.length) {
|
|
1607
|
-
console.warn(`renderer[${id}] is already binded to ${duplicate.map(([key]) => key)}`);
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
sink.renderer = renderers[id - 1];
|
|
1611
|
-
});
|
|
1612
|
-
const onframerate = (frameRate = DEFAULT_FRAME_RATE) => {
|
|
1613
|
-
if (fps === frameRate)
|
|
1614
|
-
return;
|
|
1615
|
-
fps = frameRate;
|
|
1616
|
-
if (!stopFrameSource)
|
|
1617
|
-
return;
|
|
1618
|
-
onstop();
|
|
1619
|
-
onstart();
|
|
1620
|
-
};
|
|
1621
|
-
video_channel_host.implement('setframerate', (reply, [frameRate]) => {
|
|
1622
|
-
console.log(`framerate: ${frameRate}`);
|
|
1623
|
-
onframerate(frameRate);
|
|
1624
|
-
reply.resolve();
|
|
1625
|
-
});
|
|
1626
|
-
video_channel_host.implement('setorient', (reply, [orient]) => {
|
|
1627
|
-
reply.resolve();
|
|
1628
|
-
console.log(`orient: ${orient}`);
|
|
1629
|
-
// no change
|
|
1630
|
-
if (channel_config.orient === orient)
|
|
1631
|
-
return;
|
|
1632
|
-
const inactive = channel_config.orient === VideoChannelOrient.kInactive;
|
|
1633
|
-
channel_config.orient = orient;
|
|
1634
|
-
if (orient === VideoChannelOrient.kInactive) {
|
|
1635
|
-
video_channel.Leave();
|
|
1636
|
-
}
|
|
1637
|
-
else if (inactive) {
|
|
1638
|
-
video_channel.Join(channel_config);
|
|
1639
|
-
connecting = true;
|
|
1640
|
-
}
|
|
1641
|
-
else if (connecting) {
|
|
1642
|
-
pending_actions.push(() => video_channel.SetOrient(orient));
|
|
1643
|
-
}
|
|
1644
|
-
else {
|
|
1645
|
-
video_channel.SetOrient(orient);
|
|
1646
|
-
}
|
|
1647
|
-
});
|
|
1648
|
-
cleanup.unshift(() => onstop());
|
|
1649
|
-
const renderers = [];
|
|
1650
|
-
video_channel_host.implement('load', (reply, [canvas, webgl = true]) => {
|
|
1651
|
-
const renderer = webgl ? new WebGLVideoRenderer(canvas) : new CanvasVideoRenderer(canvas);
|
|
1652
|
-
reply.resolve(renderers.push(renderer));
|
|
1653
|
-
});
|
|
1654
|
-
video_channel_host.implement('unload', (reply, [id]) => {
|
|
1655
|
-
reply.resolve();
|
|
1656
|
-
const renderer = renderers[--id];
|
|
1657
|
-
if (!renderer)
|
|
1658
|
-
return;
|
|
1659
|
-
if (renderer.gl) {
|
|
1660
|
-
renderer.dispose();
|
|
1661
|
-
}
|
|
1662
|
-
const sinks = [...sink_proxies.entries()].filter(([, v]) => v.renderer === renderer);
|
|
1663
|
-
if (sinks) {
|
|
1664
|
-
|
|
1665
|
-
console.warn(`remove video sink for [${sinks.map(([v]) => v)}] due to renderer[${id}] is unloaded`);
|
|
1666
|
-
sinks.forEach(([v]) => sink_proxies.delete(v));
|
|
1667
|
-
}
|
|
1668
|
-
renderers[id] = null;
|
|
1669
|
-
// shrink renderers
|
|
1670
|
-
if (id === renderers.length - 1) {
|
|
1671
|
-
while (id >= 0 && !renderers[id--]) {
|
|
1672
|
-
renderers.pop();
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
});
|
|
1676
|
-
// sfu transport related
|
|
1677
|
-
let connecting = false;
|
|
1678
|
-
video_channel_host.implement('onjoin', (reply, [reason, data]) => {
|
|
1679
|
-
const memory = alloc(data.length);
|
|
1680
|
-
memory.view().set(data);
|
|
1681
|
-
sfu_transport_proxy.onjoin(reason, memory.pointer, memory.bytes);
|
|
1682
|
-
memory.free();
|
|
1683
|
-
pending_actions.forEach((v) => v());
|
|
1684
|
-
pending_actions.length = 0;
|
|
1685
|
-
connecting = false;
|
|
1686
|
-
reply.resolve();
|
|
1687
|
-
});
|
|
1688
|
-
video_channel_host.implement('onleave', (reply, [reason]) => {
|
|
1689
|
-
sfu_transport_proxy.onleave(reason);
|
|
1690
|
-
reply.resolve();
|
|
1691
|
-
});
|
|
1692
|
-
video_channel_host.implement('onstatus', (reply, [status, reason]) => {
|
|
1693
|
-
sfu_transport_proxy.onstatus(status, reason);
|
|
1694
|
-
if (status === SFUTransportStatus.kReconnectOK) {
|
|
1695
|
-
pending_actions.forEach((v) => v());
|
|
1696
|
-
pending_actions.length = 0;
|
|
1697
|
-
}
|
|
1698
|
-
else if (status === SFUTransportStatus.kReconnectNG) {
|
|
1699
|
-
video_channel.Leave();
|
|
1700
|
-
video_channel.Join(channel_config);
|
|
1701
|
-
}
|
|
1702
|
-
reply.resolve();
|
|
1703
|
-
});
|
|
1704
|
-
video_channel_host.implement('onsubscribe', (reply, [ccid, reason, data]) => {
|
|
1705
|
-
const memory = alloc(data.length);
|
|
1706
|
-
memory.view().set(data);
|
|
1707
|
-
sfu_transport_proxy.onsubscribe(ccid, reason, memory.pointer, memory.bytes);
|
|
1708
|
-
memory.free();
|
|
1709
|
-
reply.resolve();
|
|
1710
|
-
});
|
|
1711
|
-
video_channel_host.implement('onsubscribeupdated', (reply, [requests]) => {
|
|
1712
|
-
sfu_transport_proxy.onsubscribeupdated(requests);
|
|
1713
|
-
reply.resolve();
|
|
1714
|
-
});
|
|
1715
|
-
video_channel_host.implement('onbitrate', (reply, [info]) => {
|
|
1716
|
-
sfu_transport_proxy.onbitrate(info);
|
|
1717
|
-
reply.resolve();
|
|
1718
|
-
});
|
|
1719
|
-
video_channel_host.implement('onsendstats', (reply, [stats]) => {
|
|
1720
|
-
sfu_transport_proxy.onsendstats(stats);
|
|
1721
|
-
reply.resolve();
|
|
1722
|
-
});
|
|
1723
|
-
video_channel_host.implement('onrecvstats', (reply, [stats]) => {
|
|
1724
|
-
sfu_transport_proxy.onrecvstats(stats);
|
|
1725
|
-
reply.resolve();
|
|
1726
|
-
});
|
|
1727
|
-
video_channel_host.implement('onsendreport', (reply, [report]) => {
|
|
1728
|
-
sfu_transport_proxy.onsendreport(report);
|
|
1729
|
-
reply.resolve();
|
|
1730
|
-
});
|
|
1731
|
-
video_channel_host.implement('ontimestamps', (reply) => {
|
|
1732
|
-
sfu_transport_proxy.ontimestamps();
|
|
1733
|
-
reply.resolve();
|
|
1734
|
-
});
|
|
1735
|
-
video_channel_host.implement('onmediasync', (reply, [timestamps]) => {
|
|
1736
|
-
sfu_transport_proxy.onmediasync(timestamps);
|
|
1737
|
-
reply.resolve();
|
|
1738
|
-
});
|
|
1739
|
-
video_channel_host.implement('onsignal', (reply, [data]) => {
|
|
1740
|
-
const memory = alloc(data.length);
|
|
1741
|
-
memory.view().set(data);
|
|
1742
|
-
sfu_transport_proxy.onsignal(memory.pointer, memory.bytes);
|
|
1743
|
-
memory.free();
|
|
1744
|
-
reply.resolve();
|
|
1745
|
-
});
|
|
1746
|
-
video_channel_host.implement('ondata', (reply, [data, ccid, time]) => {
|
|
1747
|
-
const memory = alloc(data.length);
|
|
1748
|
-
memory.view().set(data);
|
|
1749
|
-
sfu_transport_proxy.ondata(memory.pointer, memory.bytes, ccid, time, -1);
|
|
1750
|
-
memory.free();
|
|
1751
|
-
reply.resolve();
|
|
1752
|
-
});
|
|
1753
|
-
// cleanup
|
|
1754
|
-
video_channel_host.onbreak = () => {
|
|
1755
|
-
cleanup.forEach((v) => v());
|
|
1756
|
-
cleanup.length = 0;
|
|
1757
|
-
};
|
|
1758
|
-
reply.resolve(`${video_channel_host}`);
|
|
1759
|
-
});
|
|
1760
|
-
engine.implement('setup2', (reply, [transportPR, type = VideoChannelType.kMessage]) => {
|
|
1761
|
-
const cleanup = [];
|
|
1762
|
-
const transport_host = channel.remote(transportPR);
|
|
1763
|
-
const data_channel_host = channel.receiver();
|
|
1764
|
-
cleanup.unshift(() => transport_host.break());
|
|
1765
|
-
// setup transport proxy
|
|
1766
|
-
const sfu_transport_proxy = new native.SFUTransportProxy({
|
|
1767
|
-
Connect: (config) => {
|
|
1768
|
-
const [pointer, length] = config.desc;
|
|
1769
|
-
const desc = view(pointer, length).slice();
|
|
1770
|
-
transport_host
|
|
1771
|
-
.make('connect')
|
|
1772
|
-
.transfer(desc.buffer)
|
|
1773
|
-
.post(Object.assign(Object.assign({}, config), { desc }));
|
|
1774
|
-
},
|
|
1775
|
-
// main thread will disconnect the real sfu transport
|
|
1776
|
-
Disconnect: () => {
|
|
1777
|
-
if (!data_channel_host.valid)
|
|
1778
|
-
return;
|
|
1779
|
-
transport_host.make('disconnect').post();
|
|
1780
|
-
},
|
|
1781
|
-
Subscribe: (request, remove) => {
|
|
1782
|
-
if (!data_channel_host.valid)
|
|
1783
|
-
return;
|
|
1784
|
-
const memory = request
|
|
1785
|
-
.map((v) => v.desc)
|
|
1786
|
-
.map(([pointer, length]) => view(pointer, length).slice());
|
|
1787
|
-
transport_host
|
|
1788
|
-
.make('subscribe')
|
|
1789
|
-
.transfer(...memory.map((v) => v.buffer))
|
|
1790
|
-
.post(request.map((v, i) => (Object.assign(Object.assign({}, v), { desc: memory[i] }))), remove.map((v) => v.ccid));
|
|
1791
|
-
},
|
|
1792
|
-
SendData: (data, meta) => {
|
|
1793
|
-
const memory = data.map(([pointer, length]) => view(pointer, length).slice());
|
|
1794
|
-
transport_host
|
|
1795
|
-
.make('senddata')
|
|
1796
|
-
.transfer(...memory.map((v) => v.buffer))
|
|
1797
|
-
.post(memory, meta);
|
|
1798
|
-
},
|
|
1799
|
-
SendSingal: (data, length, ccid) => {
|
|
1800
|
-
const memory = view(data, length).slice();
|
|
1801
|
-
transport_host.make('sendsignal').transfer(memory.buffer).post(memory, ccid);
|
|
1802
|
-
},
|
|
1803
|
-
});
|
|
1804
|
-
// setup transport
|
|
1805
|
-
const data_channel = new native.DataChannel(type);
|
|
1806
|
-
data_channel.SetTransport(sfu_transport_proxy);
|
|
1807
|
-
// sfu transport related
|
|
1808
|
-
data_channel_host.implement('onjoin', (reply, [reason, data]) => {
|
|
1809
|
-
const memory = alloc(data.length);
|
|
1810
|
-
memory.view().set(data);
|
|
1811
|
-
sfu_transport_proxy.onjoin(reason, memory.pointer, memory.bytes);
|
|
1812
|
-
memory.free();
|
|
1813
|
-
reply.resolve();
|
|
1814
|
-
});
|
|
1815
|
-
data_channel_host.implement('onleave', (reply, [reason]) => {
|
|
1816
|
-
sfu_transport_proxy.onleave(reason);
|
|
1817
|
-
reply.resolve();
|
|
1818
|
-
});
|
|
1819
|
-
data_channel_host.implement('onstatus', (reply, [status, reason]) => {
|
|
1820
|
-
sfu_transport_proxy.onstatus(status, reason);
|
|
1821
|
-
reply.resolve();
|
|
1822
|
-
});
|
|
1823
|
-
data_channel_host.implement('onsubscribe', (reply, [ccid, reason, data]) => {
|
|
1824
|
-
const memory = alloc(data.length);
|
|
1825
|
-
memory.view().set(data);
|
|
1826
|
-
sfu_transport_proxy.onsubscribe(ccid, reason, memory.pointer, memory.bytes);
|
|
1827
|
-
memory.free();
|
|
1828
|
-
reply.resolve();
|
|
1829
|
-
});
|
|
1830
|
-
data_channel_host.implement('onsubscribeupdated', (reply, [requests]) => {
|
|
1831
|
-
sfu_transport_proxy.onsubscribeupdated(requests);
|
|
1832
|
-
reply.resolve();
|
|
1833
|
-
});
|
|
1834
|
-
data_channel_host.implement('onbitrate', (reply, [info]) => {
|
|
1835
|
-
sfu_transport_proxy.onbitrate(info);
|
|
1836
|
-
reply.resolve();
|
|
1837
|
-
});
|
|
1838
|
-
data_channel_host.implement('onsendstats', (reply, [stats]) => {
|
|
1839
|
-
sfu_transport_proxy.onsendstats(stats);
|
|
1840
|
-
reply.resolve();
|
|
1841
|
-
});
|
|
1842
|
-
data_channel_host.implement('onrecvstats', (reply, [stats]) => {
|
|
1843
|
-
sfu_transport_proxy.onrecvstats(stats);
|
|
1844
|
-
reply.resolve();
|
|
1845
|
-
});
|
|
1846
|
-
data_channel_host.implement('onsendreport', (reply, [report]) => {
|
|
1847
|
-
sfu_transport_proxy.onsendreport(report);
|
|
1848
|
-
reply.resolve();
|
|
1849
|
-
});
|
|
1850
|
-
data_channel_host.implement('ontimestamps', (reply) => {
|
|
1851
|
-
sfu_transport_proxy.ontimestamps();
|
|
1852
|
-
reply.resolve();
|
|
1853
|
-
});
|
|
1854
|
-
data_channel_host.implement('onmediasync', (reply, [timestamps]) => {
|
|
1855
|
-
sfu_transport_proxy.onmediasync(timestamps);
|
|
1856
|
-
reply.resolve();
|
|
1857
|
-
});
|
|
1858
|
-
data_channel_host.implement('onsignal', (reply, [data]) => {
|
|
1859
|
-
const memory = alloc(data.length);
|
|
1860
|
-
memory.view().set(data);
|
|
1861
|
-
sfu_transport_proxy.onsignal(memory.pointer, memory.bytes);
|
|
1862
|
-
memory.free();
|
|
1863
|
-
reply.resolve();
|
|
1864
|
-
});
|
|
1865
|
-
data_channel_host.implement('ondata', (reply, [data, ccid, time]) => {
|
|
1866
|
-
const memory = alloc(data.length);
|
|
1867
|
-
memory.view().set(data);
|
|
1868
|
-
sfu_transport_proxy.ondata(memory.pointer, memory.bytes, ccid, time, -1);
|
|
1869
|
-
memory.free();
|
|
1870
|
-
reply.resolve();
|
|
1871
|
-
});
|
|
1872
|
-
// setup cleanup
|
|
1873
|
-
cleanup.unshift(() => {
|
|
1874
|
-
data_channel.Leave();
|
|
1875
|
-
data_channel.delete();
|
|
1876
|
-
sfu_transport_proxy.delete();
|
|
1877
|
-
});
|
|
1878
|
-
data_channel_host.implement('open', (reply, [config]) => {
|
|
1879
|
-
data_channel.Join(config);
|
|
1880
|
-
reply.resolve();
|
|
1881
|
-
});
|
|
1882
|
-
data_channel_host.implement('datastream', (reply, [type = DataType.kRemoteControl, pendingListener]) => {
|
|
1883
|
-
const listenerHost = channel.remote(pendingListener);
|
|
1884
|
-
const data_stream = data_channel.CreateDataStream(type);
|
|
1885
|
-
data_stream.on('ondata', (pointer, length, from) => {
|
|
1886
|
-
const memory = view(pointer, length).slice();
|
|
1887
|
-
listenerHost.make('ondata').transfer(memory.buffer).post(memory, from);
|
|
1888
|
-
});
|
|
1889
|
-
const data_stream_host = channel.receiver();
|
|
1890
|
-
data_stream_host.implement('write', (reply, [data, dest, traits]) => {
|
|
1891
|
-
const memory = alloc(data.length);
|
|
1892
|
-
memory.view().set(data);
|
|
1893
|
-
data_stream.Write(memory.pointer, memory.bytes, dest, traits);
|
|
1894
|
-
memory.free();
|
|
1895
|
-
reply.resolve();
|
|
1896
|
-
});
|
|
1897
|
-
data_stream_host.implement('subscribe', (reply, [request, remove]) => {
|
|
1898
|
-
data_stream.Subscribe(request, remove);
|
|
1899
|
-
reply.resolve();
|
|
1900
|
-
});
|
|
1901
|
-
data_stream_host.onbreak = () => data_stream.delete();
|
|
1902
|
-
reply.resolve(`${data_stream_host}`);
|
|
1903
|
-
});
|
|
1904
|
-
data_channel_host.implement('subtitlestream', (reply, [type = DataType.kSubtitleRealtime, pendingListener]) => {
|
|
1905
|
-
const subtitle = new native.SubtitleRealtime();
|
|
1906
|
-
const listenerHost = channel.remote(pendingListener);
|
|
1907
|
-
const data_stream = data_channel.CreateDataStream(type);
|
|
1908
|
-
data_stream.on('ondata', (pointer, bytes, from) => {
|
|
1909
|
-
subtitle.decode([pointer, bytes], (info) => {
|
|
1910
|
-
listenerHost.make('ondata').post(info, from);
|
|
1911
|
-
});
|
|
1912
|
-
});
|
|
1913
|
-
const data_stream_host = channel.receiver();
|
|
1914
|
-
data_stream_host.implement('write', (reply, [data, dest]) => {
|
|
1915
|
-
subtitle.encode(data, ([pointer, bytes]) => {
|
|
1916
|
-
const traits = { direct: !!dest.length, merge: false };
|
|
1917
|
-
data_stream.Write(pointer, bytes, dest, traits);
|
|
1918
|
-
});
|
|
1919
|
-
reply.resolve();
|
|
1920
|
-
});
|
|
1921
|
-
data_stream_host.implement('subscribe', (reply, [request, remove]) => {
|
|
1922
|
-
data_stream.Subscribe(request, remove);
|
|
1923
|
-
reply.resolve();
|
|
1924
|
-
});
|
|
1925
|
-
data_stream_host.onbreak = () => data_stream.delete();
|
|
1926
|
-
reply.resolve(`${data_stream_host}`);
|
|
1927
|
-
});
|
|
1928
|
-
// cleanup
|
|
1929
|
-
data_channel_host.onbreak = () => {
|
|
1930
|
-
cleanup.forEach((v) => v());
|
|
1931
|
-
cleanup.length = 0;
|
|
1932
|
-
};
|
|
1933
|
-
reply.resolve(`${data_channel_host}`);
|
|
1934
|
-
});
|
|
1935
|
-
setupBusyDetector();
|
|
1936
|
-
// simple cpu overload detection
|
|
1937
|
-
function setupBusyDetector() {
|
|
1938
|
-
let last = Date.now();
|
|
1939
|
-
let detectTimer;
|
|
1940
|
-
const detectInterval = 1000;
|
|
1941
|
-
const factor = 1.1;
|
|
1942
|
-
const detect = () => {
|
|
1943
|
-
const now = Date.now();
|
|
1944
|
-
const delta = now - last;
|
|
1945
|
-
const tester = detectInterval * factor;
|
|
1946
|
-
if (delta > tester) {
|
|
1947
|
-
console.log(`busy report:\n` +
|
|
1948
|
-
`delta: ${delta}\n` +
|
|
1949
|
-
`factor: ${factor}\n` +
|
|
1950
|
-
`interval: ${detectInterval}\n` +
|
|
1951
|
-
`delay: ${delta - detectInterval}\n`);
|
|
1952
|
-
}
|
|
1953
|
-
last = now;
|
|
1954
|
-
detectTimer = setTimeout(detect, detectInterval);
|
|
1955
|
-
};
|
|
1956
|
-
detect();
|
|
1957
|
-
return () => clearTimeout(detectTimer);
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
}());
|