mn-video-player 1.0.4 → 1.0.6

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.
@@ -0,0 +1,998 @@
1
+ globalThis.Module = {
2
+ onRuntimeInitialized: function () {
3
+ console.log("wasm加载完成");
4
+ onWasmLoaded();
5
+ },
6
+ };
7
+
8
+ import { initWasm } from "./mn-media.js";
9
+ import * as Common from "./mn-common.js";
10
+ initWasm(Module);
11
+ // import wasmUrl from './mn-media.wasm?url'
12
+ // const res = fetch(wasmUrl)
13
+ /**
14
+ * @description: web worker用于流媒体获取(websocket)及编(音频)、解码(音视频)、对讲
15
+ * @author: lantf
16
+ * @date: 2020/10/18 15:28
17
+ */
18
+
19
+ // self.importScripts("mn-common.js");
20
+ // self.importScripts("mn-media.js"); // 加载wasm视频解码器 直接作为JS运行
21
+
22
+ function MnWorker() {
23
+ this.ws = "ws";
24
+ this.host = "";
25
+ this.port = 0;
26
+ this.deviceType = "";
27
+ this.phone = "";
28
+ this.channel = 0;
29
+ this.messageId = 0;
30
+ this.mediaType = 0;
31
+ this.streamType = 0;
32
+ this.beginTime = "";
33
+ this.playbackMode = 0;
34
+ this.netSpeedTimer = null;
35
+ this.realPlayTimer = null;
36
+ this.playBackTimer = null;
37
+ this.lang = null;
38
+ this.playAudio = false;
39
+ this.coreLogLevel = 0;
40
+ this.cacheReqQue = [];
41
+ this.cacheDataQue = [];
42
+ this.recorderLen = 0;
43
+ this.decoding = true;
44
+ this.wasmLoaded = false;
45
+ this.hasConnected = false;
46
+ this.hasInited = false;
47
+ this.hasCached = false;
48
+ this.decoderInited = false;
49
+ this.chunkSize = 1024 * 1024;
50
+ this.websocket = null;
51
+ this.cacheBuffer = null;
52
+ this.speakBuffer = null;
53
+ this.readyCallback = null;
54
+ this.videoCallback = null;
55
+ this.audioCallback = null;
56
+ this.preTimeStamp = 0;
57
+ this.shouldPlayNextTime = 0;
58
+ this.delayTime = 1000;
59
+ this.keyFrame = null;
60
+ this.netSpeedTime = 3;
61
+ this.netSpeedSize = 0;
62
+ this.heartBeatTime = 0;
63
+ this.fastForward = false;
64
+ this.voiceScheduling = false; // 语音调度
65
+ this.prefix = "/mndp/stream";
66
+ this.logger = new Common.Logger("MnWorker");
67
+ this.userId = null;
68
+ this.tenantId = null;
69
+ this.token = null;
70
+ // * 倍速 (0-无效,1-1倍,2-2倍,3-4倍,4-8倍,5-16倍)
71
+ this.speed = 0;
72
+ this.isPause = false;
73
+ this.speedValue = 1;
74
+ }
75
+
76
+ /**
77
+ * 打开解码器
78
+ **/
79
+ MnWorker.prototype.baseInit = function () {
80
+ this.cacheBuffer = Module._malloc(this.chunkSize);
81
+ this.speakBuffer = Module._malloc(1024);
82
+ let phoneBuffer = Module._malloc(16);
83
+ Module.HEAPU8.set(this.toInt8Array(this.phone), phoneBuffer);
84
+ let ret = Module._baseInit(
85
+ phoneBuffer,
86
+ this.channel,
87
+ this.readyCallback,
88
+ this.videoCallback,
89
+ this.audioCallback,
90
+ this.speakCallback,
91
+ this.coreLogLevel
92
+ );
93
+ if (phoneBuffer) {
94
+ Module._free(phoneBuffer);
95
+ }
96
+
97
+ let objData = {
98
+ t: Common.kOpenDecoderRsp,
99
+ e: ret,
100
+ };
101
+ self.postMessage(objData);
102
+ };
103
+
104
+ /**
105
+ * 关闭解码器
106
+ **/
107
+ MnWorker.prototype.baseUnInit = function () {
108
+ let ret = 0;
109
+ Module._videoUnInit();
110
+ Module._baseUnInit();
111
+
112
+ if (this.cacheBuffer != null) {
113
+ Module._free(this.cacheBuffer);
114
+ this.cacheBuffer = null;
115
+ }
116
+ if (this.speakBuffer != null) {
117
+ Module._free(this.speakBuffer);
118
+ this.speakBuffer = null;
119
+ }
120
+
121
+ this.decoderInited = false;
122
+
123
+ let objData = {
124
+ t: Common.kCloseDecoderRsp,
125
+ e: ret,
126
+ };
127
+ self.postMessage(objData);
128
+ };
129
+
130
+ /**
131
+ * 统计流量
132
+ **/
133
+ MnWorker.prototype.calNetSpeed = function () {
134
+ const speed = this.netSpeedSize / this.netSpeedTime;
135
+ const len = this.cacheDataQue.length;
136
+ this.netSpeedSize = 0;
137
+ let objData = {
138
+ t: Common.kNetSpeedFrame,
139
+ d: speed,
140
+ len,
141
+ };
142
+ self.postMessage(objData);
143
+
144
+ // 心跳(this.netSpeedTime的10倍秒数)
145
+ this.heartBeatTime += this.netSpeedTime;
146
+ if (this.websocket != null && this.hasConnected) {
147
+ if (this.heartBeatTime % (this.netSpeedTime * 10) == 0) {
148
+ let req = {};
149
+ req.phone = this.phone;
150
+ req.channel = this.channel;
151
+ req.messageId = 0x0000;
152
+ let json = JSON.stringify(req);
153
+ this.websocket.send(json);
154
+ }
155
+ }
156
+ };
157
+
158
+ /**
159
+ * 打开websocket码流链路
160
+ **/
161
+ MnWorker.prototype.openStream = function () {
162
+ let url =
163
+ this.ws +
164
+ "://" +
165
+ this.host +
166
+ ":" +
167
+ this.port +
168
+ this.prefix +
169
+ "/" +
170
+ this.phone +
171
+ "-" +
172
+ this.channel +
173
+ `?user_id=${this.userId}&tenant_id=${this.tenantId}&access_token=${this.token}`;
174
+ console.log("mn-worker", url);
175
+ this.websocket = new WebSocket(url);
176
+ this.websocket.binaryType = "arraybuffer";
177
+
178
+ this.websocket.onopen = function () {
179
+ self.decoder.onOpen();
180
+ };
181
+
182
+ this.websocket.onmessage = function (ev) {
183
+ if (typeof ev.data === "string") {
184
+ let objData = {
185
+ t: Common.kStringFrame,
186
+ d: ev.data,
187
+ };
188
+ self.postMessage(objData);
189
+ } else {
190
+ self.decoder.sendData(ev.data);
191
+ }
192
+ };
193
+
194
+ this.websocket.onerror = function () {
195
+ let message = "1连接视频服务器失败";
196
+ switch (self.decoder.lang) {
197
+ case "zh-CN,zh;":
198
+ message = "1连接视频服务器失败";
199
+ break;
200
+ case "zh-TW,zh;":
201
+ message = "1連接視訊伺服器失敗";
202
+ break;
203
+ case "en-US,en;":
204
+ message = "1Failed to connect to video server";
205
+ break;
206
+ case "ru-RU,ru;":
207
+ message = "1Ошибка подключения к видео серверу";
208
+ break;
209
+ case "kk-KZ,kk;":
210
+ message = "1Видео серверіне қосылу жаңылысы";
211
+ break;
212
+ }
213
+ let objData = {
214
+ t: Common.kSocketErrorRsp,
215
+ d: message,
216
+ };
217
+ self.postMessage(objData);
218
+ console.log("websocketErr");
219
+ this.websocket.close();
220
+ this.websocket = null;
221
+ };
222
+
223
+ this.websocket.onclose = function () {
224
+ let message = "1网络连接断开,请稍候再试";
225
+ console.log("websocketErr222");
226
+
227
+ switch (self.decoder.lang) {
228
+ case "zh-CN,zh;":
229
+ message = "1网络连接断开,请稍候再试";
230
+ break;
231
+ case "zh-TW,zh;":
232
+ message = "1網絡連接斷開,請稍候再試";
233
+ break;
234
+ case "en-US,en;":
235
+ message = "1Disconnected, please try again later";
236
+ break;
237
+ case "ru-RU,ru;":
238
+ message = "1Соединение прервано, повторите попытку позже.";
239
+ break;
240
+ case "kk-KZ,kk;":
241
+ message = "1Байланыс ажыратылды, әрекетті кейінірек қайталаңыз.";
242
+ break;
243
+ case "th-TH,th;":
244
+ message = "การเชื่อมต่อถูกตัดการเชื่อมต่อ โปรดลองอีกครั้งในภายหลัง";
245
+ break;
246
+ }
247
+ let objData = {
248
+ t: Common.kSocketCloseRsp,
249
+ d: message,
250
+ };
251
+ self.postMessage(objData);
252
+ this.websocket = null;
253
+ };
254
+ };
255
+
256
+ /**
257
+ * 切换码流
258
+ **/
259
+ MnWorker.prototype.switchStream = function (mediaType, streamType) {
260
+ if (this.websocket != null && this.hasConnected) {
261
+ let req = {};
262
+ req.phone = this.phone;
263
+ req.channel = this.channel;
264
+ req.messageId = 0x9102;
265
+ req.command = 1;
266
+ req.closeType = 0;
267
+
268
+ if (mediaType != null) {
269
+ req.mediaType = mediaType;
270
+ this.mediaType = mediaType;
271
+ }
272
+
273
+ if (streamType != null) {
274
+ req.streamType = streamType;
275
+ this.streamType = streamType;
276
+ }
277
+
278
+ let json = JSON.stringify(req);
279
+ this.websocket.send(json);
280
+ } else {
281
+ this.logger.logInfo("Web socket is null or not connected.");
282
+ }
283
+ };
284
+
285
+ /**
286
+ * 发送制命令
287
+ **/
288
+ MnWorker.prototype.play = function () {
289
+ if (this.websocket == null) {
290
+ this.logger.logInfo("Web socket is null");
291
+ return;
292
+ }
293
+ let req = {};
294
+ req.phone = this.phone;
295
+ req.channel = this.channel;
296
+ req.messageId = this.messageId;
297
+ req.mediaType = this.mediaType;
298
+ req.streamType = this.streamType;
299
+ req.beginTime = this.beginTime;
300
+ req.acceptLanguage = this.lang;
301
+ req.ws = this.ws;
302
+ req.host = this.host;
303
+ req.port = this.port;
304
+ req.deviceType = this.deviceType;
305
+ let json = JSON.stringify(req);
306
+ this.websocket.send(json);
307
+ };
308
+
309
+ /**
310
+ * ws建立后初始化
311
+ **/
312
+ MnWorker.prototype.onOpen = function () {
313
+ this.baseInit();
314
+ this.resume();
315
+ this.play();
316
+ this.hasConnected = true;
317
+ if (this.mediaType !== Common.kMediaSpeak) {
318
+ this.netSpeedTimer = setInterval(
319
+ this.calNetSpeed.bind(this),
320
+ this.netSpeedTime * 1000
321
+ );
322
+ }
323
+ };
324
+
325
+ /**
326
+ * 恢复解码
327
+ **/
328
+ MnWorker.prototype.resume = function () {
329
+ if (this.mediaType !== Common.kMediaSpeak) {
330
+ if (this.beginTime == null) {
331
+ if (this.realPlayTimer == null) {
332
+ this.realPlayTimer = setTimeout(this.realPlay.bind(this), 5);
333
+ }
334
+ } else {
335
+ if (this.playBackTimer == null) {
336
+ this.playBackTimer = setTimeout(this.playBack.bind(this), 5);
337
+ }
338
+ }
339
+ }
340
+ this.decoding = true;
341
+ };
342
+
343
+ /**
344
+ * 暂停解码
345
+ **/
346
+ MnWorker.prototype.pause = function () {
347
+ this.decoding = false;
348
+ clearTimeout(this.realPlayTimer);
349
+ this.realPlayTimer = null;
350
+ this.cacheReqQue.length = 0;
351
+ this.hasInited = false;
352
+ this.hasCached = false;
353
+ };
354
+
355
+ /**
356
+ * 实时视频解码
357
+ **/
358
+ MnWorker.prototype.realPlay = async function () {
359
+ do {
360
+ let queLen = this.cacheDataQue.length;
361
+ if (queLen == 0) {
362
+ break;
363
+ }
364
+
365
+ // 首先渲染出I帧
366
+ if (this.hasInited == false) {
367
+ let u8a = this.cacheDataQue.shift();
368
+
369
+ // 取出帧尾16个字符
370
+ let dataString = "";
371
+ let dataLen = u8a.length - 16;
372
+ for (let i = 0; i < 16; i++) {
373
+ dataString += String.fromCharCode(u8a[dataLen + i]);
374
+ }
375
+
376
+ // 获取时间戳
377
+ this.preTimeStamp = Number(dataString.substr(0, 13));
378
+
379
+ // 获取编码类型,初始化视频解码器
380
+ if (!this.decoderInited) {
381
+ let decoderType = Common.kDecoderH264;
382
+ if (dataString.substr(14) == 99) {
383
+ decoderType = Common.kDecoderH265;
384
+ }
385
+ Module._videoInit(decoderType);
386
+ this.decoderInited = true;
387
+ }
388
+
389
+ // 获取帧类型,若是I帧,则立即渲染
390
+ let frameType = Number(dataString.substr(13, 1));
391
+ if (this.decoding && frameType == 0) {
392
+ Module.HEAPU8.set(u8a, this.cacheBuffer);
393
+ Module._decodeVideo(this.cacheBuffer, dataLen);
394
+ this.hasInited = true;
395
+ }
396
+ break;
397
+ }
398
+
399
+ // 缓存N秒(delayTime)
400
+ if (!this.hasCached) {
401
+ let timeStamp = this.timeStamp(this.cacheDataQue[queLen - 1]);
402
+ if (timeStamp - this.preTimeStamp < this.delayTime) {
403
+ break;
404
+ }
405
+ this.hasCached = true;
406
+ }
407
+
408
+ let u8aHead = this.cacheDataQue[0];
409
+ let headTimeStamp = this.timeStamp(u8aHead);
410
+
411
+ let u8aTail = this.cacheDataQue[queLen - 1];
412
+ let tailTimeStamp = this.timeStamp(u8aTail);
413
+
414
+ // 计算延时
415
+ let delay = headTimeStamp - this.preTimeStamp;
416
+ if (delay < 0 || delay > 2000) {
417
+ delay = 0;
418
+ }
419
+
420
+ // 计算播放策略
421
+ let span = tailTimeStamp - headTimeStamp;
422
+ if (span < this.delayTime) {
423
+ delay = delay * 1.5;
424
+ } else if (span > this.delayTime * 3) {
425
+ // 滞后严重,需要清空缓存
426
+ if (span > this.delayTime * 10) {
427
+ let index = this.frameIndex();
428
+ if (index < 1) {
429
+ // 没有I整,或者缓存中只有第一帧是I帧,则全部清除,仅留I帧(可能会破图,因为后面的帧跟这个I帧不是连续的)
430
+ let frame = this.cacheDataQue.shift();
431
+ this.preTimeStamp = this.timeStamp(
432
+ this.cacheDataQue[this.cacheDataQue.length - 1]
433
+ );
434
+ this.cacheDataQue.length = 0;
435
+ this.cacheDataQue.push(frame);
436
+ } else {
437
+ // 取I帧前一帧的时间戳
438
+ this.preTimeStamp = this.timeStamp(this.cacheDataQue[index - 1]);
439
+ // 只留I帧及后面的帧
440
+ this.cacheDataQue = this.cacheDataQue.slice(
441
+ index,
442
+ this.cacheDataQue.length
443
+ );
444
+ }
445
+ break;
446
+ }
447
+ delay = delay / 1.5;
448
+ }
449
+
450
+ // 主子码流统一再缩小一个系数
451
+ if (this.streamType == 1) {
452
+ delay = delay / 1.5;
453
+ } else {
454
+ delay = delay / 2.5;
455
+ }
456
+
457
+ // 延时
458
+ await this.sleep(delay);
459
+
460
+ if (this.decoding) {
461
+ Module.HEAPU8.set(u8aHead, this.cacheBuffer);
462
+ Module._decodeVideo(this.cacheBuffer, u8aHead.length - 16);
463
+ }
464
+
465
+ this.cacheDataQue.shift();
466
+ this.preTimeStamp = headTimeStamp;
467
+ } while (false);
468
+
469
+ this.realPlayTimer = setTimeout(this.realPlay.bind(this), 0);
470
+ };
471
+
472
+ /**
473
+ * 回放视频解码
474
+ **/
475
+ MnWorker.prototype.playBack = async function () {
476
+ do {
477
+ let shouldDecode = true;
478
+ let min = 30;
479
+ let max = 120;
480
+ if (this.playbackMode == 6) {
481
+ console.log("帧播放", min, max);
482
+ // 帧播放
483
+ min = 10;
484
+ max = 60;
485
+ }
486
+ let queLen = this.cacheDataQue.length;
487
+ if (queLen == 0) {
488
+ break;
489
+ } else if (queLen < min * 25) {
490
+ // 恢复终端码流传输
491
+ this.playBackCtrl(false);
492
+ } else if (queLen > max * 25) {
493
+ // console.log("暂停终端码流传输");
494
+ // 暂停终端码流传输
495
+ this.playBackCtrl(true);
496
+ }
497
+
498
+ // 首先渲染出I帧
499
+ if (this.hasInited === false) {
500
+ if (this.isPause) break;
501
+ let u8a = this.cacheDataQue.shift();
502
+
503
+ // 取出帧尾16个字符
504
+ let dataString = "";
505
+ let dataLen = u8a.length - 16;
506
+ for (let i = 0; i < 16; i++) {
507
+ dataString += String.fromCharCode(u8a[dataLen + i]);
508
+ }
509
+
510
+ // 获取时间戳
511
+ this.preTimeStamp = Number(dataString.substr(0, 13));
512
+ // 获取编码类型,初始化视频解码器
513
+ if (!this.decoderInited) {
514
+ let decoderType = Common.kDecoderH264;
515
+ if (dataString.substr(14) == 99) {
516
+ decoderType = Common.kDecoderH265;
517
+ }
518
+ Module._videoInit(decoderType);
519
+ this.decoderInited = true;
520
+ }
521
+
522
+ // 获取帧类型,若是I帧,则立即渲染
523
+ let frameType = Number(dataString.substr(13, 1));
524
+ if (this.decoding && frameType == 0) {
525
+ Module.HEAPU8.set(u8a, this.cacheBuffer);
526
+ Module._decodeVideo(this.cacheBuffer, dataLen);
527
+ this.hasInited = true;
528
+ this.shouldPlayNextTime = new Date().getTime();
529
+ }
530
+ break;
531
+ }
532
+
533
+ let u8aHead = this.cacheDataQue.shift();
534
+ let headTimeStamp = this.timeStamp(u8aHead);
535
+ let delay = headTimeStamp - this.preTimeStamp;
536
+ if (this.speed) {
537
+ delay = delay / this.speed;
538
+ }
539
+ /* 该帧应播时间 */
540
+ this.shouldPlayNextTime = this.shouldPlayNextTime + delay;
541
+ let now = new Date().getTime();
542
+ let d = this.shouldPlayNextTime - now;
543
+ if (now >= this.shouldPlayNextTime) {
544
+ if (d < -2000) {
545
+ console.log("播放太慢了 跳帧", d, delay);
546
+ while (true) {
547
+ if (this.cacheDataQue.length > 0) {
548
+ /* 寻找时间最接近的I帧 */
549
+ let dataString = "";
550
+ let dataLen = u8aHead.length - 16;
551
+ for (let i = 0; i < 16; i++) {
552
+ dataString += String.fromCharCode(u8aHead[dataLen + i]);
553
+ }
554
+ let frameType = Number(dataString.substr(13, 1));
555
+ if (frameType == 0) {
556
+ /* 是I帧 */
557
+ /* 帧时间 */
558
+ let headTimeStamp = this.timeStamp(u8aHead);
559
+ /* 当前帧与之前帧的时间差 */
560
+ let delay = headTimeStamp - this.preTimeStamp;
561
+ if (this.speed) {
562
+ delay = delay / this.speed;
563
+ }
564
+ console.log("I帧播放");
565
+ this.shouldPlayNextTime = this.shouldPlayNextTime + delay;
566
+ break;
567
+ } else {
568
+ // console.log('寻找下一帧');
569
+ if (this.cacheDataQue.length > 0) {
570
+ u8aHead = this.cacheDataQue.shift();
571
+ } else {
572
+ console.log("没有帧了等待");
573
+ this.hasInited = false;
574
+ shouldDecode = false;
575
+ this.shouldPlayNextTime = new Date().getTime();
576
+ break;
577
+ }
578
+ }
579
+ } else {
580
+ this.hasInited = false;
581
+ shouldDecode = false;
582
+ this.shouldPlayNextTime = new Date().getTime();
583
+ break;
584
+ }
585
+ }
586
+ }
587
+ } else {
588
+ // console.log('解码太快 等待', delay, now, this.shouldPlayNextTime, d, queLen);
589
+ // console.log('speed',this.speed)
590
+ /* 如果出现跨段 等待时间超过10秒以上 则为跨段直接进入下一帧 */
591
+ if (d > 10000) {
592
+ // 跨段等待3秒
593
+ d = 3000;
594
+ this.shouldPlayNextTime = new Date().getTime() + 3000;
595
+ }
596
+ await this.sleep(d);
597
+ }
598
+ if (shouldDecode) {
599
+ if (this.decoding) {
600
+ Module.HEAPU8.set(u8aHead, this.cacheBuffer);
601
+ Module._decodeVideo(this.cacheBuffer, u8aHead.length - 16);
602
+ }
603
+ }
604
+ this.preTimeStamp = headTimeStamp;
605
+ } while (false);
606
+ this.playBackTimer = setTimeout(this.playBack.bind(this), 5);
607
+ };
608
+
609
+ MnWorker.prototype.dateFormat = function (fmt, date) {
610
+ let ret;
611
+ const opt = {
612
+ "Y+": date.getFullYear().toString(), // 年
613
+ "m+": (date.getMonth() + 1).toString(), // 月
614
+ "d+": date.getDate().toString(), // 日
615
+ "H+": date.getHours().toString(), // 时
616
+ "M+": date.getMinutes().toString(), // 分
617
+ "S+": date.getSeconds().toString(), // 秒
618
+ // 有其他格式化字符需求可以继续添加,必须转化成字符串
619
+ };
620
+ for (let k in opt) {
621
+ ret = new RegExp("(" + k + ")").exec(fmt);
622
+ if (ret) {
623
+ fmt = fmt.replace(
624
+ ret[1],
625
+ ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
626
+ );
627
+ }
628
+ }
629
+ return fmt;
630
+ };
631
+
632
+ /**
633
+ * 历史回放控制
634
+ **/
635
+ MnWorker.prototype.playBackCtrl = function (pause) {
636
+ if (pause) {
637
+ if (this.playbackMode == 1) {
638
+ return;
639
+ }
640
+ this.playbackMode = 1;
641
+ } else {
642
+ if (this.playbackMode == 0) {
643
+ return;
644
+ }
645
+ this.playbackMode = 0;
646
+ }
647
+
648
+ let req = {};
649
+ req.phone = this.phone;
650
+ req.channel = this.channel;
651
+ req.messageId = 0x9202;
652
+ req.mediaType = this.mediaType;
653
+ req.streamType = this.streamType;
654
+ req.beginTime = this.beginTime;
655
+ req.acceptLanguage = this.lang;
656
+ req.playbackMode = this.playbackMode;
657
+ let json = JSON.stringify(req);
658
+
659
+ if (this.websocket != null) {
660
+ if (this.websocket.readyState === this.websocket.OPEN) {
661
+ this.websocket.send(json);
662
+ } else {
663
+ if (this.playBackTimer !== null) {
664
+ clearTimeout(this.playBackTimer);
665
+ this.playBackTimer = null;
666
+ }
667
+ this.websocket = null;
668
+ }
669
+ }
670
+ };
671
+
672
+ /**
673
+ * 获取时间戳
674
+ **/
675
+ MnWorker.prototype.timeStamp = function (u8a) {
676
+ let dataString = "";
677
+ let dataLen = u8a.length - 16;
678
+
679
+ for (let i = 0; i < 16; i++) {
680
+ dataString += String.fromCharCode(u8a[dataLen + i]);
681
+ }
682
+
683
+ return Number(dataString.substr(0, 13));
684
+ };
685
+
686
+ /**
687
+ * 获取数组中最后一个I帧的索引
688
+ **/
689
+ MnWorker.prototype.frameIndex = function () {
690
+ let index = -1;
691
+ for (let i = 0; i < this.cacheDataQue.length; i++) {
692
+ let u8a = this.cacheDataQue[i];
693
+ let dataLen = u8a.length - 16;
694
+ if (u8a[dataLen + 13] == 48) {
695
+ // 48 <=> '0'
696
+ index = i;
697
+ }
698
+ }
699
+ return index;
700
+ };
701
+
702
+ /**
703
+ * 延时
704
+ **/
705
+ MnWorker.prototype.sleep = function (ms) {
706
+ return new Promise((resolve) => setTimeout(resolve, ms));
707
+ };
708
+
709
+ /**
710
+ * 往码流链路发音频控制命令
711
+ **/
712
+ MnWorker.prototype.enableAudio = function (d) {
713
+ this.playAudio = d;
714
+ /* if (this.websocket != null) {
715
+ let req = {};
716
+ req.phone = this.phone;
717
+ req.channel = this.channel;
718
+ if (d) {
719
+ req.messageId = 0x9601;
720
+ } else {
721
+ req.messageId = 0x9600;
722
+ }
723
+ let json = JSON.stringify(req);
724
+ this.websocket.send(json);
725
+ } else {
726
+ this.logger.logInfo("Web socket is null");
727
+ }*/
728
+ };
729
+
730
+ /**
731
+ * 关闭websocket码流链路
732
+ **/
733
+ MnWorker.prototype.closeStream = function () {
734
+ this.pause();
735
+
736
+ if (this.websocket != null) {
737
+ this.websocket.close();
738
+ this.websocket = null;
739
+ }
740
+
741
+ if (this.netSpeedTimer != null) {
742
+ clearInterval(this.netSpeedTimer);
743
+ this.netSpeedTimer = null;
744
+ }
745
+
746
+ this.baseUnInit();
747
+ };
748
+
749
+ /**
750
+ * 码流信息解码
751
+ **/
752
+ MnWorker.prototype.sendData = function (data) {
753
+ if (!this.decoding) {
754
+ return;
755
+ }
756
+
757
+ let u8a = new Uint8Array(data);
758
+ this.netSpeedSize += u8a.length;
759
+ //u8a = u8a.subarray(26, 114); // 去掉rtp头,可用于对讲测试
760
+ if (u8a[0] == 0 && u8a[1] == 0 && u8a[2] == 0 && u8a[3] == 1) {
761
+ this.cacheDataQue.push(u8a);
762
+ } else {
763
+ if (this.mediaType !== Common.kMediaSpeak && !this.playAudio) {
764
+ // 监听时,按声音开关控制播放音频
765
+ return;
766
+ }
767
+ if (!this.voiceScheduling) {
768
+ // 语音调度不播放上行音频
769
+ // 最后一字节为音频编码类型
770
+ Module.HEAPU8.set(u8a, this.cacheBuffer);
771
+ Module._decodeAudio(
772
+ this.cacheBuffer,
773
+ u8a.length - 1,
774
+ u8a[u8a.length - 1]
775
+ );
776
+ }
777
+ }
778
+ };
779
+
780
+ /**
781
+ * 处理来自MnPlayer的命令
782
+ **/
783
+ MnWorker.prototype.processReq = function (req) {
784
+ switch (req.t) {
785
+ case Common.kOpenDecoderReq:
786
+ this.ws = req.ws;
787
+ this.host = req.host;
788
+ this.port = req.port;
789
+ this.phone = req.phone;
790
+ (this.userId = req.userId),
791
+ (this.tenantId = req.tenantId),
792
+ (this.token = req.token),
793
+ (this.channel = req.channel);
794
+ this.lang = req.lang;
795
+ this.deviceType = req.deviceType;
796
+ break;
797
+ case Common.kResumeDecodingReq:
798
+ this.resume();
799
+ break;
800
+ case Common.kPauseDecodingReq:
801
+ this.pause();
802
+ break;
803
+ case Common.kOpenStreamReq:
804
+ this.messageId = req.messageId;
805
+ this.mediaType = req.mediaType;
806
+ this.streamType = req.streamType;
807
+ this.beginTime = req.beginTime;
808
+ this.lang = req.lang;
809
+ this.openStream();
810
+ break;
811
+ case Common.kEnableAudioReq:
812
+ this.enableAudio(req.d);
813
+ break;
814
+ case Common.kSwitchStreamReq:
815
+ this.switchStream(req.m, req.s);
816
+ break;
817
+ case Common.kRecorderSpeakReq:
818
+ this.voiceScheduling = true;
819
+ this.encodeAudio(req.d);
820
+ break;
821
+ case Common.kRecorderTalkReq:
822
+ this.voiceScheduling = false;
823
+ this.encodeAudio(req.d);
824
+ break;
825
+ case Common.kCloseStreamReq:
826
+ this.closeStream();
827
+ break;
828
+ case Common.kFastForward:
829
+ this.fastForward = req.d;
830
+ break;
831
+ case Common.kReplayReq:
832
+ this.play();
833
+ break;
834
+ case Common.kReplaySpeed:
835
+ let beginTime = this.beginTime;
836
+ this.playbackMode = req.playbackMode;
837
+ if (req.time) {
838
+ beginTime = req.time;
839
+ } else if (this.preTimeStamp) {
840
+ let t = new Date(this.preTimeStamp);
841
+ beginTime = `${t.getUTCFullYear()}-${(t.getUTCMonth() + 1)
842
+ .toString()
843
+ .padStart(2, "0")}-${t.getUTCDate().toString().padStart(2, "0")} ${t
844
+ .getUTCHours()
845
+ .toString()
846
+ .padStart(2, "0")}:${t
847
+ .getUTCMinutes()
848
+ .toString()
849
+ .padStart(2, "0")}:${t.getUTCSeconds().toString().padStart(2, "0")}`;
850
+ }
851
+
852
+ this.websocket.send(
853
+ JSON.stringify({
854
+ playbackSpeed: req.d,
855
+ messageId: 0x9202,
856
+ channel: this.channel,
857
+ phone: this.phone,
858
+ deviceType: this.deviceType,
859
+ streamType: this.streamType,
860
+ mediaType: this.mediaType,
861
+ host: this.host,
862
+ hostUuid: this.host_uuid,
863
+ port: this.port,
864
+ ws: this.ws,
865
+ beginTime,
866
+ acceptLanguage: this.lang,
867
+ playbackMode: this.playbackMode,
868
+ })
869
+ );
870
+ this.speed = req.v;
871
+ this.speedValue = req.d;
872
+ break;
873
+ default:
874
+ this.logger.logError("Unsupport messsage " + req.t);
875
+ }
876
+ };
877
+
878
+ /**
879
+ * 录音编码(对讲)
880
+ **/
881
+ //判断设备 6 等于QM003 26 = 磁北
882
+ MnWorker.prototype.encodeAudio = function (data) {
883
+ if (this.websocket != null) {
884
+ Module.HEAP8.set(data, this.speakBuffer);
885
+ Module._encodeAudio(
886
+ this.speakBuffer,
887
+ data.length,
888
+ this.deviceType == "6" ? 6 : 26
889
+ );
890
+ }
891
+ };
892
+
893
+ /**
894
+ * 字符串转int8Array
895
+ **/
896
+ MnWorker.prototype.toInt8Array = function (str) {
897
+ let i8a = [];
898
+ for (let i = 0; i < str.length; i++) {
899
+ i8a.push(str.charCodeAt(i));
900
+ }
901
+ return new Int8Array(i8a);
902
+ };
903
+
904
+ /**
905
+ * 缓存来自MnPlayer的命令
906
+ **/
907
+ MnWorker.prototype.cacheReq = function (req) {
908
+ if (req) {
909
+ this.cacheReqQue.push(req);
910
+ }
911
+ };
912
+
913
+ /**
914
+ * WASM初始化
915
+ **/
916
+ MnWorker.prototype.onWasmLoaded = function () {
917
+ this.wasmLoaded = true;
918
+ this.readyCallback = Module.addFunction(function (width, height) {
919
+ let objData = {
920
+ t: Common.kVideoReady,
921
+ w: width,
922
+ h: height,
923
+ };
924
+ self.postMessage(objData);
925
+ }, "viid");
926
+ let that = this;
927
+ this.videoCallback = Module.addFunction(function (buff, size) {
928
+ let outArray = Module.HEAPU8.subarray(buff, buff + size);
929
+ let data = new Uint8Array(outArray);
930
+ let objData = {
931
+ t: Common.kVideoFrame,
932
+ d: data,
933
+ ti: that.preTimeStamp,
934
+ };
935
+ self.postMessage(objData, [objData.d.buffer]);
936
+ }, "viid");
937
+ this.audioCallback = Module.addFunction(function (buff, size) {
938
+ let outArray = Module.HEAPU8.subarray(buff, buff + size);
939
+ let data = new Uint8Array(outArray);
940
+ let objData = {
941
+ t: Common.kAudioFrame,
942
+ d: data,
943
+ };
944
+ self.postMessage(objData, [objData.d.buffer]);
945
+ }, "viid");
946
+
947
+ this.speakCallback = Module.addFunction(function (buff, size) {
948
+ let outArray = Module.HEAPU8.subarray(buff, buff + size);
949
+ let data = new Int8Array(outArray);
950
+ if (
951
+ self.decoder.websocket !== null &&
952
+ self.decoder.websocket.readyState === self.decoder.websocket.OPEN
953
+ ) {
954
+ self.decoder.websocket.send(data);
955
+ }
956
+ }, "viid");
957
+
958
+ // 执行缓存中的命令
959
+ while (this.cacheReqQue.length > 0) {
960
+ let req = this.cacheReqQue.shift();
961
+ this.processReq(req);
962
+ }
963
+ };
964
+
965
+ self.decoder = new MnWorker();
966
+
967
+ /**
968
+ * 接收主线程消息
969
+ **/
970
+ self.onmessage = function (evt) {
971
+ console.log("接收主线程消息", evt, self.decoder);
972
+ if (!self.decoder) {
973
+ console.log("[ER] MnWorker not initialized!");
974
+ return;
975
+ }
976
+
977
+ let req = evt.data;
978
+ if (!self.decoder.wasmLoaded) {
979
+ // 若尚未初始化,则缓存该命令
980
+ console.log("缓存命令", req);
981
+ self.decoder.cacheReq(req);
982
+ return;
983
+ }
984
+ console.log("接收主线程消息", req);
985
+ self.decoder.processReq(req);
986
+ };
987
+
988
+ /**
989
+ * 初始化
990
+ **/
991
+ function onWasmLoaded() {
992
+ if (self.decoder) {
993
+ console.log("WASM加载完成");
994
+ self.decoder.onWasmLoaded();
995
+ } else {
996
+ console.log("[ER] No decoder!");
997
+ }
998
+ }