zbzt-live-sdk 0.0.2

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.
Files changed (80) hide show
  1. package/.babelrc +8 -0
  2. package/.editorconfig +13 -0
  3. package/.eslintrc.js +29 -0
  4. package/CHANGELOG.md +16 -0
  5. package/README.md +32 -0
  6. package/dist/zbzt-live-sdk.cjs.js +51 -0
  7. package/dist/zbzt-live-sdk.esm.js +51 -0
  8. package/dist/zbzt-live-sdk.umd.js +52 -0
  9. package/dist/zbzt-live-sdk.umd.js.map +1 -0
  10. package/example/app.js +133 -0
  11. package/example/bootstrap-3.4.1/css/bootstrap-theme.css +587 -0
  12. package/example/bootstrap-3.4.1/css/bootstrap-theme.css.map +1 -0
  13. package/example/bootstrap-3.4.1/css/bootstrap-theme.min.css +6 -0
  14. package/example/bootstrap-3.4.1/css/bootstrap-theme.min.css.map +1 -0
  15. package/example/bootstrap-3.4.1/css/bootstrap.css +6834 -0
  16. package/example/bootstrap-3.4.1/css/bootstrap.css.map +1 -0
  17. package/example/bootstrap-3.4.1/css/bootstrap.min.css +6 -0
  18. package/example/bootstrap-3.4.1/css/bootstrap.min.css.map +1 -0
  19. package/example/bootstrap-3.4.1/fonts/glyphicons-halflings-regular.eot +0 -0
  20. package/example/bootstrap-3.4.1/fonts/glyphicons-halflings-regular.svg +288 -0
  21. package/example/bootstrap-3.4.1/fonts/glyphicons-halflings-regular.ttf +0 -0
  22. package/example/bootstrap-3.4.1/fonts/glyphicons-halflings-regular.woff +0 -0
  23. package/example/bootstrap-3.4.1/fonts/glyphicons-halflings-regular.woff2 +0 -0
  24. package/example/bootstrap-3.4.1/js/bootstrap.js +2580 -0
  25. package/example/bootstrap-3.4.1/js/bootstrap.min.js +6 -0
  26. package/example/bootstrap-3.4.1/js/npm.js +13 -0
  27. package/example/css/style.css +18 -0
  28. package/example/index.html +59 -0
  29. package/example/js/jquery-3.7.1.min.js +2 -0
  30. package/example/js/utils.js +25 -0
  31. package/example/js/variables.js +14 -0
  32. package/example/js/zegoApi.js +229 -0
  33. package/example/style.css +25 -0
  34. package/example/test.html +100 -0
  35. package/package.json +60 -0
  36. package/release.js +33 -0
  37. package/rollup-plugin-http.js +49 -0
  38. package/src/channel/getSendMsgParams.js +66 -0
  39. package/src/channel/index.js +135 -0
  40. package/src/channel/pomelo/index.js +182 -0
  41. package/src/channel/pomelo/latestQueue.js +150 -0
  42. package/src/channel/pomelo/polemo.js +730 -0
  43. package/src/channel/pomelo/util.js +54 -0
  44. package/src/channel/sdk-cb.js +73 -0
  45. package/src/channel/stream-msg.js +97 -0
  46. package/src/channel/zbzt/index.js +74 -0
  47. package/src/channel/zbzt/interactWithChannel.js +4 -0
  48. package/src/channel/zbzt/interactWithChannelControl.js +1568 -0
  49. package/src/channel/zbzt/interactWithChannelEntry.js +318 -0
  50. package/src/config/config.js +226 -0
  51. package/src/default/base.js +71 -0
  52. package/src/default/extend.js +36 -0
  53. package/src/default/index.js +10 -0
  54. package/src/live/base.js +43 -0
  55. package/src/live/call-method.js +10 -0
  56. package/src/live/extend.js +53 -0
  57. package/src/live/index.js +10 -0
  58. package/src/network/api.js +56 -0
  59. package/src/network/commonFetch.js +66 -0
  60. package/src/network/dataReport.js +448 -0
  61. package/src/notice.js +418 -0
  62. package/src/tool/base.js +74 -0
  63. package/src/tool/call-method.js +10 -0
  64. package/src/tool/extend.js +42 -0
  65. package/src/tool/index.js +10 -0
  66. package/src/util/bridge.js +87 -0
  67. package/src/util/bridge1.js +46 -0
  68. package/src/util/dict.js +51 -0
  69. package/src/util/sessionStorage.js +29 -0
  70. package/src/util/sha256.js +483 -0
  71. package/src/util/util.js +329 -0
  72. package/src/zbzt-av-sdk/default-sdk.js +192 -0
  73. package/src/zbzt-av-sdk/device.js +86 -0
  74. package/src/zbzt-av-sdk/rtc-sdk.js +2854 -0
  75. package/src/zbzt-av-sdk/talrtc-sdk.js +2620 -0
  76. package/src/zbzt-av-sdk/trtc-sdk.js +1802 -0
  77. package/src/zbzt-av-sdk/zbzt-av-sdk.js +2121 -0
  78. package/src/zbzt-av-sdk/zego-sdk.js +1718 -0
  79. package/src/zbzt-av-sdk/zego-sdk.js.bak +3133 -0
  80. package/src/zbzt-live-sdk.js +1484 -0
@@ -0,0 +1,1718 @@
1
+ /**
2
+ * @File: Zego 音视频 SDK
3
+ * @Author: xuyuanrui
4
+ * @Email: raywhbxyr@163.com
5
+ * @Date: 2024-01-26 10:14:41
6
+ **/
7
+ import ZegoExpressEngine from 'zego-express-engine-webrtc';
8
+
9
+ import dataReport from '../network/dataReport.js';
10
+ import defaultApi from '../default';
11
+ import util from '../util/util';
12
+ import NOTICE from '../notice';
13
+ import { deviceListReport } from './device.js';
14
+ import { getZegoGateway } from '../config/config.js';
15
+ import commonFetch from '../network/commonFetch.js';
16
+
17
+ // 留存初始化参数 initParams;
18
+ let initParams = {};
19
+ // 创摄像头流的参数, 维持原相关set约束的调用方式
20
+ let cameraConstraints = {
21
+ camera: {
22
+ audio: {
23
+ // input,
24
+ // ANS, 降噪
25
+ // AGC, 增益
26
+ // AEC, 回声消除
27
+ },
28
+ video: {
29
+ // input, // 采集设备id
30
+ quality: 4,
31
+ width: initParams.encodedResolutionWidth,
32
+ height: initParams.encodedResolutionHeight,
33
+ frameRate: initParams.encodeCaptureFps,
34
+ }
35
+ },
36
+ videoBitrate: initParams.encodeCaptureBitrate/1000,
37
+ audioBitrate: 48,
38
+ captureView: null,
39
+ // autoPlay: true,
40
+ };
41
+ let screenConstraints = {
42
+ videoBitrate: 800,
43
+ captureView: null,
44
+ // autoPlay: true,
45
+ screen: {
46
+ audio: false,
47
+ video: {
48
+ quality: 4,
49
+ frameRate: 15,
50
+ width: 960,
51
+ height: 540
52
+ }
53
+ }
54
+ };
55
+ // enableCamera、enableMic
56
+ let hasCamera = true;
57
+ let hasMicrophone = true;
58
+ /**
59
+ * zegoWebExpressEngine 实例
60
+ * @type { ZegoExpressEngine.ZegoExpressEngine | null }
61
+ */
62
+ let zg = null;
63
+ // 声浪回调周期
64
+ let soundLevelMonitorCycle = 1000;
65
+ // 推流id
66
+ let localStreamId;
67
+ /**
68
+ * @type { import('zego-express-engine-webrtc/sdk/code/zh/ZegoLocalStream.web').default | null }
69
+ */
70
+ let localStream;
71
+ let screenStreamId = '';
72
+ /**
73
+ * @type { import('zego-express-engine-webrtc/sdk/code/zh/ZegoLocalStream.web').default | null }
74
+ */
75
+ let screenStream;
76
+ //记录当前角色
77
+ let currentRole = '';
78
+ //记录当前课态类型:小组课=0/小班课=1
79
+ let currentClassMode = null;
80
+ //记录当前rtmp推流sdk类型
81
+ let currentGroupSdkType = ''; // ???
82
+ // 维护的一份当前正在使用的音频设备的 deviceId 的列表
83
+ let usingAudioDeviceId = {
84
+ speaker: '',
85
+ microphone: ''
86
+ };
87
+ // // 维护的一份拉流的 streamId 与本地预览通道的映射表
88
+ let streamIdToPreviewId = {};
89
+ let previewIdToStreamId = {};
90
+ let streamIdToStream = {}; // 维护一份流id跟播放器的映射关系type: { streamId: stream }
91
+ let streamIdToStreamView = {}; // 维护一份流id跟播放器的映射关系type: { streamid: remoteview }
92
+ // 拉流的播放通道初始化从 0 开始依次递增至44(共45路流),-1 为推流的本地视频预览
93
+ const uiChnIndexs = new Array(48).fill(0).map((a, b) => b).reverse();
94
+
95
+
96
+ // WEB没有获取系统音量,维护麦克风音量、
97
+ let microphoneVolume = 100;
98
+ let speakerVolume = 100;
99
+
100
+ //静音推流时,音量值是否上抛上层标志 false=没有声音不往上层通知/true=有声音有上层通知
101
+ let isNoticeMicVolumeZego = false;
102
+ // 心跳
103
+ let heartBeatDataReportObj = {};
104
+ let heartBeatDataReportTimer = null;
105
+ let isFirstHeartBeatReport = false;
106
+ let streamIdRtcPlayerInfo = {}; // 埋点数据格式
107
+ let streamIdRtcPlayerInfo1 = {}; // 埋点数据格式
108
+ let currStreamIdRtcPlayerInfo;
109
+ const heartBeatRealKeys = ['video_fps', 'video_bitrate', 'audio_fps', 'audio_bitrate'];
110
+
111
+ //记录底层推流回调的值
112
+ let dataZegoCapture = {};
113
+
114
+ let hasLoginChannel = 0;
115
+ // TODO 测试代码,token接入完成后删除
116
+ async function getToken(appId, userId) {
117
+ const querys = {
118
+ appId,
119
+ userId,
120
+ };
121
+ return commonFetch('https://dev-webrtc.weclassroom.com/webrtc/zegotoken', querys, 'GET');
122
+ }
123
+
124
+ async function init(args) {
125
+ // args.appId = 1739272706;
126
+ // args.server = 'wss://webliveroom-test.zego.im/ws';
127
+ // args.appKey = 'eyJ2ZXIiOjEsImhhc2giOiI3ZGRmMjMwZjRiYzYxNTNlNGViN2U2Zjc4M2Q1N2Y0NiIsIm5vbmNlIjoiMjgyMWEyYWE5YWU0NDM3MWY1NGI2NWYzMDk0OWNmYWYiLCJleHBpcmVkIjoxNzA5NDMxODkwfQ==';
128
+ args.server = getZegoGateway();
129
+ args.appKey = await getToken(args.appId, args.userId);
130
+ console.warn('[初始化]', args);
131
+ // NOTE 1. 参数保留
132
+ initParams = Object.assign({}, args);
133
+ localStreamId = initParams.streamId;
134
+ screenStreamId = `${args.institutionId}_${args.roomId}_${args.userId}_${args.live_id}_1`;
135
+ currentRole = initParams.role;
136
+ currentClassMode = initParams.classMode;
137
+ currentGroupSdkType = initParams.sdkTypeGroup;
138
+ // NOTE 2. 加载扩展插件
139
+ // NOTE 此处没用到Web拓展能力
140
+ // NOTE 3. 初始化ZegoWebExpressEngine & 登录房间
141
+ await startEngine(initParams);
142
+ // NOTE 4. 注册回调 注意: 渲染相关的事件在streamView上管理
143
+ addListener();
144
+ // NOTE 5. 实例化zego实例之后的动作
145
+ await afterInitSDK(initParams);
146
+ // NOTE 6. 创流参数保留
147
+ if (initParams.noiseSuppressMode == -2) {
148
+ defaultApi.writeLog(`noiseSuppressMode 关闭噪声消除 ${initParams.noiseSuppressMod}`);
149
+ await setEnableNoiseSuppress(false);
150
+ } else if (initParams.noiseSuppressMode == -1) {
151
+ defaultApi.writeLog(`noiseSuppressMode 由底层进行配置,js层不进行配置 ${initParams.noiseSuppressMode}`);
152
+ } else {
153
+ await setEnableNoiseSuppress(true);
154
+ };
155
+ //aecMode为-1是关闭回音消除,云控下传
156
+ if (initParams.aecMode == -2) {
157
+ defaultApi.writeLog(`aecMode 关闭回音消除 ${initParams.aecMode}`);
158
+ await setEnableAEC(false);
159
+ } else if (initParams.aecMode == -1) {
160
+ defaultApi.writeLog(`aecMode 由底层进行配置,js层不进行配置 ${initParams.aecMode}`);
161
+ } else {
162
+ defaultApi.writeLog(`aecMode 启动回音消除,模式为: ${initParams.aecMode}`);
163
+ await setEnableAEC(true);
164
+ };
165
+ await setVideoBitrate(initParams.encodeCaptureBitrate);
166
+ await setAudioBitrate(initParams.encodeCaptureBitrate);
167
+ await setVideoFPS(initParams.encodeCaptureFps);
168
+ // NOTE WEB没有分开设置
169
+ // await setVideoCaptureResolution(initParams.previewResolutionWidth, initParams.previewResolutionHeight);
170
+ await setVideoEncodeResolution(initParams.encodedResolutionWidth, initParams.encodedResolutionHeight);
171
+ window.current_sdk_type = 'zego';
172
+ defaultApi.writeLog('zego init finished current_sdk_type : zego');
173
+ }
174
+ async function startEngine(args) {
175
+ if(!zg){
176
+ zg = new ZegoExpressEngine.ZegoExpressEngine(args.appId, args.server);
177
+ }
178
+ zg.setDebugVerbose(false);
179
+ zg.setLogConfig({ logLevel: 'disable' });
180
+ // NOTE 开启多房间
181
+ // zg.enableMultiRoom(true); // 开多房间,推流需要指定房间号
182
+ // NOTE web没有onInitSDK, 登录房间放在注册事件之后
183
+ // deviceCheckerInitSDK 区别设备检测
184
+ }
185
+
186
+ async function afterInitSDK(args) {
187
+ // NOTE 1. 设置默认设备
188
+ const usedDevices = window.zbyAVSDK_device_checker_init;
189
+ const { devices } = args;
190
+ const _devices = {
191
+ camera: (devices && devices.camera) || (usedDevices && usedDevices.camera && usedDevices.camera.use) || '',
192
+ microphone: (devices && devices.microphone) || (usedDevices && usedDevices.microphone && usedDevices.microphone.use) || '',
193
+ speaker: (devices && devices.speaker) || (usedDevices && usedDevices.speaker && usedDevices.speaker.use) || ''
194
+ };
195
+ await setDefaultDevice(_devices, 'default');
196
+ // NOTE 2. 登录房间
197
+ const { roomId, appKey, userId, userName } = args;
198
+ await zg.loginRoom(roomId, appKey, { userID: userId, userName }, { userUpdate: true });
199
+ // NOTE 3. 设置质量回调周期 web 无此接口,回调周期写死3s
200
+ // NOTE 4. 设置声浪回调周期;
201
+ setSoundLevelMonitorCycle(1000);
202
+ // NOTE 5. 设置SEI开关
203
+ setMediaSideFlags();
204
+ }
205
+
206
+ function destroyEngine() {
207
+ zg.destroyEngine();
208
+ }
209
+ function setMediaSideFlags() {
210
+ // SEI开关 web跟native模式不一样,此接口需调整
211
+ // NOTE 当前需求不用此功能
212
+ }
213
+ /**
214
+ * @function 设置默认的硬件设备,包括摄像头、麦克风以及扬声器
215
+ * @return Promise
216
+ */
217
+ async function setDefaultDevice(devices, operationType) {
218
+ console.warn('[设置默认设备]', devices, operationType);
219
+ // 设置默认的摄像头
220
+ if (devices && devices.camera) {
221
+ if (zbyAVSDK_device_checker_init.camera.list.length == 0) {
222
+ await getVideoDeviceList();
223
+ }
224
+ await setVideoDevice({ deviceId: devices.camera, operationType: `${operationType}_1` });
225
+ } else {
226
+ const cameraData = await getVideoDeviceList();
227
+ if (cameraData.length) {
228
+ let hasSetCamera = false;
229
+ for (let item of cameraData) {
230
+ if (item.isDefault) {
231
+ await setVideoDevice({ deviceId: item.deviceId, operationType: `${operationType}_2` });
232
+ hasSetCamera = true;
233
+ break;
234
+ }
235
+ }
236
+ if (!hasSetCamera) {
237
+ await setVideoDevice({ deviceId: cameraData[0].deviceId, operationType: `${operationType}_3` });
238
+ }
239
+ }
240
+ }
241
+ // 设置默认的麦克风
242
+ if (devices && devices.microphone) {
243
+ if (zbyAVSDK_device_checker_init.microphone.list.length == 0) {
244
+ await getMicrophoneDeviceList();
245
+ }
246
+ await setMicrophoneDevice({ deviceId: devices.microphone, operationType: `${operationType}_1` });
247
+ } else {
248
+ const microPhoneData = await getMicrophoneDeviceList();
249
+ if (microPhoneData.length) {
250
+ let hasSetMicrophone = false;
251
+ for (let item of microPhoneData) {
252
+ if (item.isDefault) {
253
+ await setMicrophoneDevice({ deviceId: item.deviceId, operationType: `${operationType}_2` });
254
+ hasSetMicrophone = true;
255
+ break;
256
+ }
257
+ }
258
+ if (!hasSetMicrophone) {
259
+ await setMicrophoneDevice({ deviceId: microPhoneData[0].deviceId, operationType: `${operationType}_3` });
260
+ }
261
+ }
262
+ }
263
+ // 设置默认的扬声器
264
+ if (devices && devices.speaker) {
265
+ await setSpeakerDevice({ deviceId: devices.speaker, operationType: `${operationType}_1` });
266
+ } else {
267
+ const speakerData = await getSpeakerDeviceList();
268
+ if (speakerData.length) {
269
+ let hasSetSpeaker = false;
270
+ for (let item of speakerData) {
271
+ if (item.isDefault) {
272
+ await setSpeakerDevice({ deviceId: item.deviceId, operationType: `${operationType}_2` });
273
+ hasSetSpeaker = true;
274
+ break;
275
+ }
276
+ }
277
+ if (!hasSetSpeaker) {
278
+ await setSpeakerDevice({ deviceId: speakerData[0].deviceId, operationType: `${operationType}_3` });
279
+ }
280
+ }
281
+ }
282
+ }
283
+ async function getVideoDeviceList() {
284
+ if(!zg) {
285
+ return Promise.resolve([]);
286
+ }
287
+ let videoListArr = await zg.getCameras();
288
+ let videoList = [];
289
+ for (let i = 0, len = videoListArr.length; i < len; i++) {
290
+ videoList.push({
291
+ deviceId: videoListArr[i].deviceID,
292
+ deviceName: videoListArr[i].deviceName,
293
+ isDefault: i === 0
294
+ });
295
+ deviceListReport.cameraList[videoListArr[i].deviceID] = videoListArr[i].deviceName;
296
+ }
297
+ if (!window.zbyAVSDK_device_checker_init) {
298
+ window.zbyAVSDK_device_checker_init = {};
299
+ }
300
+ if (!window.zbyAVSDK_device_checker_init.camera) {
301
+ window.zbyAVSDK_device_checker_init.camera = {};
302
+ }
303
+ window.zbyAVSDK_device_checker_init.camera.hasTest = true;
304
+ window.zbyAVSDK_device_checker_init.camera.list = videoList;
305
+ return videoList;
306
+ }
307
+
308
+ async function setVideoDevice(args) {
309
+ let { deviceId, idx, operationType, deviceState, code, changedDeviceId } = args;
310
+ let deviceName = '';
311
+ let cameraData;
312
+ // NOTE 1. 没传deviceId, 设置默认设备
313
+ if (!deviceId) {
314
+ cameraData = await getVideoDeviceList();
315
+ if (cameraData.length) {
316
+ let hasGetCamare = false;
317
+ for (let item of cameraData) {
318
+ if (item.isDefault) {
319
+ deviceId = item.deviceId;
320
+ deviceName = item.deviceName;
321
+ hasGetCamare = true;
322
+ break;
323
+ }
324
+ }
325
+ if (!hasGetCamare) {
326
+ deviceId = cameraData[0].deviceId;
327
+ deviceName = cameraData[0].deviceName;
328
+ }
329
+ } else {
330
+ deviceId = '';
331
+ }
332
+ if (deviceId === '') {
333
+ NOTICE.noDevice({
334
+ deviceType: 'camera'
335
+ });
336
+ }
337
+ }
338
+ window.zbyAVSDK_device_checker_init.camera.use = deviceId;
339
+ window.zbyAVSDK_device_checker_init.camera.name = deviceListReport.cameraList[deviceId];
340
+ // NOTE 2. ???
341
+ try {
342
+ dataReport.setDevice({
343
+ device_type: 1,
344
+ device_id: deviceId,
345
+ device_name: deviceListReport.cameraList[deviceId],
346
+ operationType,
347
+ fore_state: operationType == 'hotPlug' ? deviceState + 1 : '-'
348
+ });
349
+ } catch (e) { };
350
+ // NOTE 3. 切摄像头
351
+ if(localStream && deviceId) {
352
+ await zg.useVideoDevice(localStream, deviceId);
353
+ }
354
+ // NOTE 4. 热插拔
355
+ if (operationType == 'hotPlug' || operationType == 'deviceError') {
356
+ if (!cameraData) {
357
+ cameraData = await getVideoDeviceList();
358
+ deviceName = deviceListReport.cameraList[deviceId];
359
+ }
360
+ NOTICE[operationType]({
361
+ deviceType: 'camera',
362
+ useDeviceId: deviceId,
363
+ useDeviceName: deviceName,
364
+ deviceList: cameraData,
365
+ messge: code,
366
+ deviceState,
367
+ changedDeviceId
368
+ });
369
+ }
370
+ try {
371
+ NOTICE.useredCamera({
372
+ deviceId,
373
+ deviceName
374
+ });
375
+ } catch (e) { };
376
+ }
377
+ /**
378
+ * @function 开/关摄像头
379
+ * @param enable:Boolean false -> 关闭,true -> 打开,必选
380
+ * @param idx:Number 推流通道,可选,默认为 0
381
+ * @return Promise | void
382
+ */
383
+ async function enableCamera(enable) {
384
+ defaultApi.writeLog(`avsdk ZEGO::enableCamera ${enable}`);
385
+ // NOTE WEB无预设接口,此接口可能在创流之前调用,所以增加保存字段
386
+ hasCamera = enable;
387
+
388
+ if(!localStream) {
389
+ await startPreview();
390
+ }
391
+
392
+ if(enable) {
393
+ await localStream.startCaptureCamera(cameraConstraints.camera.video);
394
+ await zg.updatePublishingStream(localStream, 0); // 更新类型,0为视频,1为音频,2为音视频
395
+ }else {
396
+ await localStream.stopCaptureVideo();
397
+ }
398
+ }
399
+ async function enableMic(enable) {
400
+ defaultApi.writeLog(`avsdk ZEGO::enableMic ${enable}`);
401
+ // NOTE WEB无预设接口,此接口可能在创流之前调用,所以增加保存字段
402
+ // NOTE 关联地方:创流渲染前
403
+ hasMicrophone = enable;
404
+ isNoticeMicVolumeZego = enable;
405
+
406
+ if(!localStream) {
407
+ await startPreview();
408
+ }
409
+
410
+ if(enable) {
411
+ await localStream.startCaptureMicrophone(cameraConstraints.camera.audio);
412
+ await zg.updatePublishingStream(localStream, 1); // 更新类型,0为视频,1为音频,2为音视频
413
+ }else {
414
+ await localStream.stopCaptureAudio();
415
+ }
416
+ }
417
+ //设置采集声音
418
+ async function setAudioSpeakerCapture(bEnable) {
419
+ // NOTE native接口enableMixSystemPlayout是开启/关闭采集的
420
+ if(!localStream) { return; }
421
+ if(bEnable) {
422
+ localStream.startCaptureMicrophone();
423
+ // NOTE 用于更新处于推流中的ZegoLocalStream实例的音视轨
424
+ await zg.updatePublishingStream(localStream, 1); // 更新类型,0为视频,1为音频,2为音视频
425
+ }else {
426
+ localStream.stopCaptureAudio(); // 停止采集音频
427
+ }
428
+ }
429
+
430
+ /**
431
+ * @function 获取音频设备
432
+ * @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
433
+ * @return Promise | void
434
+ */
435
+ async function getAudioDeviceList(deviceType) {
436
+ if(deviceType) {
437
+ return await getSpeakerDeviceList();
438
+ }else {
439
+ return await getMicrophoneDeviceList();
440
+ }
441
+ }
442
+ /**
443
+ * @function 指定音频设备
444
+ * @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
445
+ * @param deviceId:String 音频设备 id,必选
446
+ * @return Promise | void
447
+ */
448
+ async function setAudioDevice(deviceType, deviceId) {
449
+ if (deviceType == 0) {
450
+ // NOTE 1. 设置麦克风设备
451
+ window.zbyAVSDK_device_checker_init.microphone.use = deviceId;
452
+ zg.useAudioDevice(localStream, deviceId);
453
+ } else {
454
+ // NOTE 2. 设置扬声器设备
455
+ window.zbyAVSDK_device_checker_init.speaker.use = deviceId;
456
+ Object.keys(streamIdToStreamView).forEach(streamId=> {
457
+ streamIdToStreamView[streamId].useAudioOutputDevice(deviceId);
458
+ });
459
+ }
460
+ }
461
+
462
+ async function getSpeakerDeviceList() {
463
+ if(!zg) {
464
+ return Promise.resolve([]);
465
+ }
466
+ let speakerListArr = await zg.getSpeakers();
467
+ let speakerList = [];
468
+ for (let i = 0, len = speakerListArr.length; i < len; i++) {
469
+ speakerList.push({
470
+ deviceId: speakerListArr[i].deviceID,
471
+ deviceName: speakerListArr[i].deviceName,
472
+ isDefault: i === 0
473
+ });
474
+ deviceListReport.speakerList[speakerListArr[i].deviceID] = speakerListArr[i].deviceName;
475
+ }
476
+ if (!window.zbyAVSDK_device_checker_init) {
477
+ window.zbyAVSDK_device_checker_init = {};
478
+ }
479
+ if (!window.zbyAVSDK_device_checker_init.speaker) {
480
+ window.zbyAVSDK_device_checker_init.speaker = {};
481
+ }
482
+ window.zbyAVSDK_device_checker_init.speaker.hasTest = true;
483
+ window.zbyAVSDK_device_checker_init.speaker.list = speakerList;
484
+ return speakerList;
485
+ }
486
+ /**
487
+ * @function 指定扬声器
488
+ * @param deviceId:String 扬声器 id,必选
489
+ * @param operationType:String 操作类型,可选
490
+ * 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
491
+ * 不传即是普通的设置扬声器设备的行为
492
+ * @return Promise | void
493
+ */
494
+ async function setSpeakerDevice(args) {
495
+ // NOTE 1. 没传deviceId则用默认设备
496
+ defaultApi.writeLog(`${JSON.stringify(args)}----setSpeakerDevice-zego----`);
497
+ let { deviceId, operationType, deviceState, code, changedDeviceId } = args;
498
+ let deviceName = '';
499
+ let speakerData;
500
+ if (!deviceId) {
501
+ speakerData = await getSpeakerDeviceList();
502
+ if (speakerData.length) {
503
+ let hasGetSpeaker = false;
504
+ for (let item of speakerData) {
505
+ if (item.isDefault) {
506
+ deviceId = item.deviceId;
507
+ deviceName = item.deviceName;
508
+ hasGetSpeaker = true;
509
+ break;
510
+ }
511
+ }
512
+ if (!hasGetSpeaker) {
513
+ deviceId = speakerData[0].deviceId;
514
+ deviceName = speakerData[0].deviceName;
515
+ }
516
+ } else {
517
+ deviceId = '';
518
+ }
519
+ if (deviceId === '') {
520
+ NOTICE.noDevice({
521
+ deviceType: 'speaker'
522
+ });
523
+ }
524
+ }
525
+ // NOTE 2. ???
526
+ try {
527
+ dataReport.setDevice({
528
+ device_type: 3,
529
+ device_id: deviceId,
530
+ device_name: deviceListReport.speakerList[deviceId],
531
+ operationType,
532
+ fore_state: operationType == 'hotPlug' ? deviceState + 1 : '-'
533
+ });
534
+ window.zbyAVSDK_device_checker_init.speaker.name = deviceListReport.speakerList[deviceId];
535
+ } catch (e) { };
536
+ // NOTE 3. 设置扬声器
537
+ usingAudioDeviceId.speaker = deviceId;
538
+ await setAudioDevice(1, deviceId);
539
+ // NOTE 4. 热插拔处理逻辑
540
+ if (operationType == 'hotPlug' || operationType == 'deviceError') {
541
+ if (!speakerData) {
542
+ speakerData = await getSpeakerDeviceList();
543
+ deviceName = deviceListReport.speakerList[deviceId];
544
+ }
545
+ NOTICE[operationType]({
546
+ deviceType: 'speaker',
547
+ useDeviceId: deviceId,
548
+ useDeviceName: deviceName,
549
+ deviceList: speakerData,
550
+ message: code,
551
+ deviceState,
552
+ changedDeviceId
553
+ });
554
+ }
555
+ }
556
+ /**
557
+ * 获取当前扬声器设备id
558
+ * @return {String} 当前扬声器设备id
559
+ */
560
+ function getCurrentSpeakerDevice() {
561
+ defaultApi.writeLog(`avsdk ZEGO::getSpeakerDevice ${usingAudioDeviceId.speaker}`);
562
+ return usingAudioDeviceId.speaker;
563
+ }
564
+ function getSpeakerDeviceVolume() {
565
+ return speakerVolume;
566
+ }
567
+ function setSpeakerDeviceVolume(volume) {
568
+ speakerVolume = volume;
569
+ // NOTE 1. 遍历所有播放器,全部设置
570
+ Object.keys(streamIdToStreamView).forEach(streamId=> {
571
+ streamIdToStreamView[streamId].setVolume(speakerVolume);
572
+ });
573
+ }
574
+ /**
575
+ * @function 设置扬声器静音
576
+ * @param mute:Boolean 必选
577
+ * @return Promise | void
578
+ */
579
+ function setSpeakerDeviceMute(mute) {
580
+ // NOTE 1. 遍历所有播放器,全部设置
581
+ Object.keys(streamIdToStreamView).forEach(streamId=> {
582
+ const streamView = streamIdToStreamView[streamId];
583
+ streamView.setAudioMuted(mute);
584
+ });
585
+ }
586
+
587
+ /**
588
+ * @function 获取麦克风列表
589
+ * @return Promise | void
590
+ */
591
+ async function getMicrophoneDeviceList() {
592
+ if(!zg) {
593
+ return Promise.resolve([]);
594
+ }
595
+ let microphoneListArr = await zg.getMicrophones();
596
+ let microphoneList = [];
597
+ for (let i = 0, len = microphoneListArr.length; i < len; i++) {
598
+ microphoneList.push({
599
+ deviceId: microphoneListArr[i].deviceID,
600
+ deviceName: microphoneListArr[i].deviceName,
601
+ isDefault: i === 0,
602
+ });
603
+ deviceListReport.micList[microphoneListArr[i].deviceID] = microphoneListArr[i].deviceName;
604
+ }
605
+ if (!window.zbyAVSDK_device_checker_init) {
606
+ window.zbyAVSDK_device_checker_init = {};
607
+ }
608
+ if (!window.zbyAVSDK_device_checker_init.microphone) {
609
+ window.zbyAVSDK_device_checker_init.microphone = {};
610
+ }
611
+ window.zbyAVSDK_device_checker_init.microphone.hasTest = true;
612
+ window.zbyAVSDK_device_checker_init.microphone.list = microphoneList;
613
+ return microphoneList;
614
+ }
615
+ /**
616
+ * @function 指定麦克风
617
+ * @param deviceId:String 麦克风 id,必选
618
+ * @param operationType:String 操作类型,可选
619
+ * 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
620
+ * 不传即是普通的设置麦克风设备的行为
621
+ * @return Promise | void
622
+ */
623
+ async function setMicrophoneDevice(args) {
624
+ defaultApi.writeLog(`${JSON.stringify(args)}----setMicrophoneDevice-zego----`);
625
+ let { deviceId, operationType, deviceState, code, changedDeviceId } = args;
626
+ let deviceName = '';
627
+ let microPhoneData;
628
+ // NOTE 1. 没设备就用默认设备
629
+ if (!deviceId) {
630
+ microPhoneData = await getMicrophoneDeviceList();
631
+ if (microPhoneData.length) {
632
+ let hasGetMicrophone = false;
633
+ for (let item of microPhoneData) {
634
+ if (item.isDefault) {
635
+ deviceId = item.deviceId;
636
+ deviceName = item.deviceName;
637
+ hasGetMicrophone = true;
638
+ break;
639
+ }
640
+ }
641
+ if (!hasGetMicrophone) {
642
+ deviceId = microPhoneData[0].deviceId;
643
+ deviceName = microPhoneData[0].deviceName;
644
+ }
645
+ } else {
646
+ deviceId = '';
647
+ }
648
+ if (deviceId === '') {
649
+ NOTICE.noDevice({
650
+ deviceType: 'microphone'
651
+ });
652
+ }
653
+ }
654
+ // NOTE 2. ???
655
+ try {
656
+ dataReport.setDevice({
657
+ device_type: 2,
658
+ device_id: deviceId,
659
+ device_name: deviceListReport.micList[deviceId],
660
+ operationType,
661
+ fore_state: operationType == 'hotPlug' ? deviceState + 1 : '-'
662
+ });
663
+ window.zbyAVSDK_device_checker_init.microphone.name = deviceListReport.micList[deviceId];
664
+ } catch (e) { };
665
+ usingAudioDeviceId.microphone = deviceId;
666
+ // NOTE 3. 设置麦克风
667
+ await setAudioDevice(0, deviceId);
668
+ // NOTE WEB无此功能,native 此接口是音量变化事件的开关。
669
+ // await setAudioVolumeNotify(0, deviceId);
670
+ if (operationType == 'hotPlug' || operationType == 'deviceError') {
671
+ if (!microPhoneData) {
672
+ microPhoneData = await getMicrophoneDeviceList();
673
+ deviceName = deviceListReport.micList[deviceId];
674
+ }
675
+ NOTICE[operationType]({
676
+ deviceType: 'microphone',
677
+ useDeviceId: deviceId,
678
+ useDeviceName: deviceName,
679
+ deviceList: microPhoneData,
680
+ message: code,
681
+ deviceState,
682
+ changedDeviceId
683
+ });
684
+ }
685
+ }
686
+ function getMicDeviceVolume() {
687
+ return microphoneVolume;
688
+ }
689
+ function setMicDeviceVolume(volume) {
690
+ microphoneVolume = volume;
691
+ return localStream.setVolume(volume);
692
+ }
693
+ /**
694
+ * @function 设置麦克风静音
695
+ * @param mute:Boolean 必选
696
+ * @return Promise | void
697
+ */
698
+ function setMicDeviceMute(mute) {
699
+ if(localStream) {
700
+ return zg.mutePublishStreamAudio(localStream, mute);
701
+ }
702
+ }
703
+ /**
704
+ * @function 获取当前应用程序音量
705
+ * @return Promise | void
706
+ */
707
+ function getSpeakerSimpleVolume() {
708
+ // NOTE native端是设置扬声器的程序音量
709
+ return speakerVolume;
710
+ }
711
+ function setSpeakerSimpleVolume(volume) {
712
+ speakerVolume = volume;
713
+ // NOTE 遍历播放器,分别设置
714
+ // NOTE TODO ...
715
+ Object.keys(streamIdToStreamView).forEach(streamId=> {
716
+ const streamView = streamIdToStreamView[streamId];
717
+ streamView.setVolume(volume);
718
+ });
719
+ }
720
+ function setSpeakerSimpleMute(mute) {
721
+ // NOTE 遍历所有的拉流播放器,设置mute
722
+ // 用setAudioMuted,因为禁流轨道有active接口
723
+ setSpeakerDeviceMute(mute);
724
+ }
725
+ function setPreviewView() {
726
+ // NOTE WEB无此功能
727
+ }
728
+ async function startPreview(viewElement) {
729
+ if(!localStream) {
730
+ cameraConstraints.captureView = viewElement;
731
+ cameraConstraints.camera.audio.input = window.zbyAVSDK_device_checker_init.microphone.use;
732
+ cameraConstraints.camera.video.input = window.zbyAVSDK_device_checker_init.camera.use;
733
+ const constraints = Object.assign({}, cameraConstraints);
734
+ if(!hasCamera) {
735
+ constraints.camera.video = false;
736
+ }
737
+ if(!hasMicrophone) {
738
+ constraints.camera.audio = false;
739
+ }
740
+ localStream = await zg.createZegoStream(constraints);
741
+ localStream.on('canPlayCaptureVideo', function(){
742
+ console.warn('[采集首帧]', localStream);
743
+ // 大班数据上报 TODO ...
744
+ // dabanDataReport({
745
+ // action: 'open_camera',
746
+ // camera_status: 1,
747
+ // uid: window.zbyAVSDK_init_params.zego.userId // zbyAVSDK_init_params这个对象在上层没被定义
748
+ // });
749
+ });
750
+ setMicDeviceVolume(microphoneVolume);
751
+ }
752
+
753
+ if(viewElement) {
754
+ localStream.playVideo(viewElement); // NOTE 渲染
755
+ }
756
+ }
757
+ async function startLocalOrRemotePreview(isLocal, streamId, domId) {
758
+ if(!domId) { return; }
759
+ // NOTE 1. 预览流渲染
760
+ if(isLocal) {
761
+ if (streamId === localStreamId) {
762
+ return startPreview(document.querySelector(`#${domId}`));
763
+ }
764
+ if (streamId === screenStreamId) {
765
+ return startScreenPreview(document.querySelector(`#${domId}`));
766
+ }
767
+ defaultApi.writeLog(`ZEGO: startLocalOrRemotePreview wrong streamId: ${streamId} domId: ${domId}`, null, error);
768
+ }else {
769
+ // NOTE 拉流渲染前,先读取activate预置的参数
770
+ const playInfo1 = streamIdRtcPlayerInfo1[streamId];//true -> 拉取,false -> 不拉取,必选
771
+ if(!playInfo1.video_type) {
772
+ try {
773
+ zg.mutePlayStreamVideo(streamId, !playInfo1.video_type); // NOTE 没视轨会报错
774
+ } catch (error) { }
775
+ }
776
+ if(!playInfo1.audio_type) {
777
+ try {
778
+ zg.mutePlayStreamAudio(streamId, !playInfo1.audio_type); // NOTE 没音轨会报错
779
+ } catch (error) { }
780
+
781
+ }
782
+ const remoteStream = streamIdToStream[streamId];
783
+ const remoteView = zg.createRemoteStreamView(remoteStream);
784
+ streamIdToStreamView[streamId] = remoteView;
785
+ remoteView.setVolume(speakerVolume);
786
+ remoteView.useAudioOutputDevice(window.zbyAVSDK_device_checker_init.speaker.use);
787
+ remoteView.on('canPlayVideo', function() {
788
+ console.warn('[拉流视频首帧]', streamId, remoteView);
789
+ // TODO
790
+ // if (window.zbyAVSDK_init_params.zego.role === 'student') {
791
+ // dabanDataReport({
792
+ // action: 'interact_play',
793
+ // teammate_streamid: _data.pszStreamID,
794
+ // uid: window.zbyAVSDK_init_params.zego.userId // NOTE zbyAVSDK_init_params这个对象在上层没被定义
795
+ // });
796
+ // }
797
+ try {
798
+ NOTICE.firstVideoSize({
799
+ streamId: streamId,
800
+ userId: util.getUidByStreamId(streamId)
801
+ });
802
+ dataReport.firstVideoSize({
803
+ pull_streamid: streamId,
804
+ pull_uid: util.getUidByStreamIdDr(streamId),
805
+ code: '0'
806
+ });
807
+ } catch (e) { };
808
+ defaultApi.writeLog(`ZEGO: pullStream success & haveFirstVideoSize streamId: ${streamId}`);
809
+ });
810
+ remoteView.on('canPlayAudio', function() {
811
+ console.warn('[拉流音频首帧]', streamId);
812
+ try {
813
+ NOTICE.firstAudioSize({
814
+ streamId: streamId,
815
+ userId: util.getUidByStreamId(streamId)
816
+ });
817
+ dataReport.firstAudioSize({
818
+ pull_streamid: streamId,
819
+ pull_uid: util.getUidByStreamIdDr(streamId),
820
+ code: '0'
821
+ });
822
+ } catch (e) { };
823
+ });
824
+ remoteView.on('autoplayFailed', function() {
825
+ console.error('自动播放音频失败');
826
+ });
827
+ remoteView.play(domId);
828
+ try {
829
+ // NOTE 防止二次拉流,业务侧创重名播放容器
830
+ document.querySelectorAll('#' + domId)[1].remove();
831
+ } catch (error) {}
832
+ }
833
+ }
834
+ async function setEnableNoiseSuppress(ANS) { // 降噪
835
+ // NOTE 1. 保存设置
836
+ cameraConstraints.camera.audio['ANS'] = ANS;
837
+ // NOTE 2. 已创建流了, 更新
838
+ if(localStream) {
839
+ return zg.setAudioConfig(localStream, { ANS });
840
+ }
841
+ return Promise.resolve(true);
842
+ }
843
+ async function setEnableAEC(AEC) { // 回声消除
844
+ // NOTE 1. 保存设置
845
+ cameraConstraints.camera.audio['AEC'] = AEC;
846
+ // NOTE 2. 已创建流了, 更新
847
+ if(localStream) {
848
+ return zg.setAudioConfig(localStream, { AEC });
849
+ }
850
+ return Promise.resolve(true);
851
+ }
852
+ async function setVideoFPS(frameRate) {
853
+ // NOTE 1. 保存设置
854
+ cameraConstraints.camera.video.frameRate = frameRate;
855
+ // NOTE 2. 已创建流了, 更新
856
+ if(localStream) {
857
+ return zg.setVideoConfig(localStream, { frameRate });
858
+ }
859
+ return Promise.resolve(true);
860
+ }
861
+ async function setVideoBitrate(maxBitrate) {
862
+ // NOTE 1. 保存设置
863
+ cameraConstraints.videoBitrate = maxBitrate/1000;
864
+ // NOTE 2. 已创建流了, 更新
865
+ if(localStream) {
866
+ return zg.setVideoConfig(localStream, { maxBitrate });
867
+ }
868
+ return Promise.resolve(true);
869
+ }
870
+ async function setAudioBitrate(maxBitrate) {
871
+ // NOTE 1. 保存设置
872
+ cameraConstraints.audioBitrate = maxBitrate/1000;
873
+ // NOTE 2. WEB 不支持动态改音频码率,非要改的话需要销毁音频重新创建
874
+ return Promise.resolve(true);
875
+ }
876
+ async function setVideoCaptureResolution(width, height) {
877
+ // NOTE 1. 保存设置
878
+ cameraConstraints.camera.video.width = width;
879
+ cameraConstraints.camera.video.height = height;
880
+ // NOTE 2. 已创建流了, 更新
881
+ if(localStream) {
882
+ console.warn(localStream, { width, height });
883
+ return zg.setVideoConfig(localStream, { width, height });
884
+ }
885
+ // NOTE 3. setCapturePipelineScaleMode WEB无缩放时机
886
+ return Promise.resolve(true);
887
+ }
888
+ async function setCameraParam(width, height) {
889
+ // NOTE WEB没有采集、编码分辨率,只是提供一个目标值,采多少编多少浏览器动态调控
890
+ return setVideoCaptureResolution(width, height);
891
+ }
892
+ async function setVideoEncodeResolution(width, height) {
893
+ // NOTE WEB没有采集、编码分辨率,只是提供一个目标值,采多少编多少浏览器动态调控
894
+ return setVideoCaptureResolution(width, height);
895
+ }
896
+ /**
897
+ * 开始推流
898
+ * @param {string} streamId 流唯一标识
899
+ * @param {0 | 1} idx 0: 启动摄像头 1: 启动屏幕共享
900
+ * @returns
901
+ */
902
+ async function startPublish2(streamId, idx) {
903
+ if (!streamId) {
904
+ streamId = localStreamId;
905
+ }
906
+
907
+ if (streamId === localStreamId) {
908
+ // NOTE 初始化推流埋点数据
909
+ resetHeartBeatDataReportObj();
910
+ const publisRes = zg.startPublishingStream(streamId, localStream);
911
+ if(!publisRes) {
912
+ NOTICE.pushStreamError({
913
+ streamId,
914
+ });
915
+ return;
916
+ }
917
+ } else if (streamId === screenStreamId) {
918
+ screenStream = await startScreenPreview();
919
+ const publisRes = zg.startPublishingStream(streamId, screenStream);
920
+ if(!publisRes) {
921
+ NOTICE.pushStreamError({
922
+ streamId,
923
+ });
924
+ return;
925
+ }
926
+ } else {
927
+ defaultApi.writeLog(`ZEGO:: startPublish2 streamId:${streamId} is not exist[${localStreamId}, ${screenStreamId}]`, null, 'error');
928
+ NOTICE.pushStreamError({
929
+ streamId,
930
+ msg: `streamId:${streamId} is not exist[${localStreamId}, ${screenStreamId}]`
931
+ });
932
+
933
+ }
934
+ }
935
+ function stopPublish(idx) {
936
+ console.log('[停止推流]', localStreamId);
937
+ if (idx !== 0 || idx !== 1) {
938
+ defaultApi.writeLog(`ZEGO:: stopPublish idx:${idx} is not exist[${localStreamId}, ${screenStreamId}]`, null, 'error');
939
+ }
940
+ return zg.stopPublishingStream(idx === 0 ? localStreamId : screenStreamId);
941
+ }
942
+ async function startScreenPreview(captureView) {
943
+ if (!screenStream) {
944
+ screenConstraints.videoBitrate = initParams.screenSameBitrate / 1000;
945
+ screenConstraints.captureView = captureView;
946
+ screenConstraints.screen.video.frameRate = initParams.screenSameFps;
947
+ screenConstraints.screen.width = initParams.screenSameResolutionWidth;
948
+ screenConstraints.screen.height = initParams.screenSameResolutionHeight;
949
+ screenConstraints.screen.audio = false;
950
+ console.warn('[创建屏幕共享流]', screenConstraints);
951
+ screenStream = await zg.createZegoStream(screenConstraints);
952
+ }
953
+
954
+ if (captureView) {
955
+ screenStream.playVideo(captureView);
956
+ }
957
+ return screenStream;
958
+ }
959
+
960
+ async function initPullFlow(streamId, domId, mute, pInfo, notAutoPlay, audioOnly) {
961
+ defaultApi.writeLog(`ZEGO:: initPullFlow_start ,streamId :${streamId}, domId: ${domId}, mute: ${mute}, pInfo: ${pInfo}, notAutoPlay: ${notAutoPlay}, audioOnly: ${audioOnly}`);
962
+ // NOTE 1. 重置埋点数据
963
+ let playChannel = _initPullData(streamId);
964
+ // NOTE 5. 配置拉流参数
965
+ //是否拉取声音
966
+ await activateAudioPlayStream(streamId, !mute);
967
+ //是否拉取画面
968
+ await activateVideoPlayStream(streamId, !audioOnly);
969
+ // NOTE 6. 拉流
970
+ // if (!notAutoPlay) { // NOTE 此处不自动播放,什么地方拉流?
971
+ // await playStream(streamId, playChannel, pInfo);
972
+ // }
973
+ const remoteStream = await playStream(streamId, playChannel, pInfo);
974
+ streamIdToStream[streamId] = remoteStream;
975
+ // 创建视图
976
+ await startLocalOrRemotePreview(false, streamId, domId);
977
+ return {
978
+ videoSrc: '',
979
+ playerId: playChannel
980
+ };
981
+ }
982
+
983
+ function _initPullData(streamId) {
984
+ let playChannel;
985
+ // NOTE 1. 重置埋点数据
986
+ resetStreamIdRtcPlayerInfo1(streamId);
987
+ resetStreamIdRtcPlayerInfo(streamId);
988
+ // NOTE 2. 首次拉流,启动埋点上报程序
989
+ try {
990
+ if (!isFirstHeartBeatReport) {
991
+ isFirstHeartBeatReport = true;
992
+ heartBeatDataReport('start');
993
+ }
994
+ } catch (error) { }
995
+ // NOTE 3. 程序限制了拉流49条,给每条拉流设置一个编号
996
+ if (streamIdToPreviewId[streamId] == undefined) {
997
+ defaultApi.writeLog(`ZEGO:: The streamId queue does not contain this streamId ${uiChnIndexs}`);
998
+ // NOTE 3.1 超50条,不能再拉流了
999
+ if (uiChnIndexs.length === 0) {
1000
+ NOTICE.overMaxConnect();
1001
+ defaultApi.writeLog('error', `ZEGO:: all uiChnIndexs are used ${uiChnIndexs}`);
1002
+ return {
1003
+ videoSrc: '',
1004
+ playerId: -2
1005
+ };
1006
+ }
1007
+ playChannel = uiChnIndexs.pop();
1008
+ streamIdToPreviewId[streamId] = playChannel;
1009
+ defaultApi.writeLog(`streamIdIsNoExited ${playChannel} ${uiChnIndexs}`);
1010
+ } else {
1011
+ // NOTE 3.2 已存在,停拉重拉
1012
+ playChannel = streamIdToPreviewId[streamId];
1013
+ defaultApi.writeLog(`ZEGO:: The streamId queue contains this streamId ${playChannel} ${uiChnIndexs}`);
1014
+ stopPlayStream(streamId);
1015
+ resetStreamIdRtcPlayerInfo(streamId);
1016
+ }
1017
+ // NOTE 4. 判断是否创建或者获取播放编号成功,不成功就删掉重试
1018
+ if (playChannel == undefined) {
1019
+ delete streamIdToPreviewId[streamId];
1020
+ defaultApi.writeLog('error', 'ZEGO:: uiChnIndex is not exist');
1021
+ return initPullFlow(streamId);
1022
+ }
1023
+ previewIdToStreamId[playChannel] = streamId;
1024
+ return playChannel;
1025
+ }
1026
+
1027
+ /**
1028
+ * @function 恢复拉指定的流
1029
+ * @param streamId:String 被拉取的流的 id,必选
1030
+ * @param uiChnIndex:Number 播放通道,可选
1031
+ * @param pInfo:String 多媒体流附加信息,可选,默认为 'none'
1032
+ * @return Promise | void
1033
+ */
1034
+ async function playStream(streamId, uiChnIndex, pInfo) {
1035
+ if (typeof pInfo === 'undefined') { // SEI
1036
+ pInfo = 'none';
1037
+ }
1038
+ const streamInfo = streamIdRtcPlayerInfo1[streamId];
1039
+ defaultApi.writeLog(`avsdk ZEGO::PlayStream streamId: ${streamId} streamInfo: ${JSON.stringify(streamInfo)}`);
1040
+ return zg.startPlayingStream(streamId, {
1041
+ // TODO 拉流参数配置
1042
+ // video: !!!audioOnly,
1043
+ video: streamInfo.video_type,
1044
+ audio: streamInfo.audio_type,
1045
+ isSEIStart: pInfo != 'none'? true: false,
1046
+ });
1047
+ }
1048
+ async function stopPlayStream(streamId, recovery) {
1049
+ console.warn('[停止拉流]', streamId);
1050
+ delete streamIdRtcPlayerInfo[streamId];
1051
+ // if (recovery && id && !uiChnIndexs.includes(id)) {
1052
+ // uiChnIndexs.push(id);
1053
+ // defaultApi.writeLog(`avsdk ZEGO::stopPlayStream streamId: ${streamId} id: ${id} uiChnIndexs: ${uiChnIndexs}`);
1054
+ delete streamIdToPreviewId[streamId];
1055
+ // }
1056
+ delete streamIdToStream[streamId];
1057
+ delete streamIdToStreamView[streamId];
1058
+ zg.stopPlayingStream(streamId);
1059
+ // NOTE 清除渲染节点 拉纯音频,渲染节点直接挂在body上
1060
+ try {
1061
+ const dom = document.querySelector('#' + streamId); // id不存在会抛错
1062
+ if(dom) { dom.remove(); }
1063
+ } catch (error) { }
1064
+
1065
+ }
1066
+ function stopAllPlayStream() {
1067
+ Object.keys(streamIdToPreviewId).forEach(streamId=> {
1068
+ const id = streamIdToPreviewId[streamId];
1069
+ if (id !==void(0) && !uiChnIndexs.includes(id)) {
1070
+ uiChnIndexs.push(id);
1071
+ stopPlayStream(streamId);
1072
+ }
1073
+ });
1074
+ streamIdToPreviewId = {};
1075
+ streamIdToStream = {};
1076
+ streamIdToStreamView = {};
1077
+ }
1078
+ async function leaveRoom(type) {
1079
+ // NOTE 1. 停止声浪监听 WEB端无需单独停止
1080
+ // NOTE 2. 停拉
1081
+ for (let key in streamIdToPreviewId) {
1082
+ stopPlayStream(key, true);
1083
+ }
1084
+ // NOTE 3. 停推
1085
+ zg.stopPublishingStream(localStreamId);
1086
+ // NOTE 4. 停事件监听
1087
+ removerListener();
1088
+ // NOTE 5. 刷新按钮
1089
+ if(type == 'reload') {
1090
+ // TODO ...
1091
+ }else {
1092
+ // 停预览
1093
+ zg.destroyStream(localStream);
1094
+ // 销毁引擎
1095
+ zg.destroyEngine();
1096
+ }
1097
+ // NOTE 6. 其它业务逻辑
1098
+ heartBeatDataReport('stop');
1099
+ isFirstHeartBeatReport = false;
1100
+ streamIdToPreviewId = {};
1101
+ previewIdToStreamId = {};
1102
+ streamIdRtcPlayerInfo = {};
1103
+ streamIdRtcPlayerInfo1 = {};
1104
+ // heartBeatDataReportObj = {};
1105
+ hasLoginChannel = 0;
1106
+ }
1107
+
1108
+ async function playerSnapShot(streamId) {
1109
+ defaultApi.writeLog(`avsdk ZEGO::playerSnapShot streamId: ${streamId}`);
1110
+ let uri;
1111
+ if (streamId === localStreamId) {
1112
+ uri = localStream.takeStreamSnapshot();
1113
+ } else {
1114
+ uri = screenStream.takeStreamSnapshot();
1115
+ }
1116
+ defaultApi.writeLog(`avsdk ZEGO::playerSnapShot uri: ${uri}`);
1117
+ return util.getImageDataFromURL(uri);
1118
+ }
1119
+ async function activateAudioPlayStream(streamId, bActive) {
1120
+ // NOTE WEB无此接口,此处业务侧应该是想达到是否渲染音视频的目的。所以web端先拉流,再按此设置值渲染
1121
+ // NOTE 关联代码 streamview.play()
1122
+ if(!streamIdRtcPlayerInfo1[streamId]) {
1123
+ resetStreamIdRtcPlayerInfo1(streamId);
1124
+ }
1125
+ streamIdRtcPlayerInfo1[streamId].audio_type = bActive;
1126
+ if(streamIdToStream[streamId]) { // 流已存在
1127
+ await zg.mutePlayStreamAudio(streamId, !bActive);
1128
+ }
1129
+ }
1130
+ /**
1131
+ * @function 拉取音频流
1132
+ * @param operation:Boolean true/不拉音频,false/拉音频,必选
1133
+ * @param streamid:String 被拉取的流的 id,必选
1134
+ */
1135
+ async function pullAudioFlow(streamId, operation) {
1136
+ // NOTE 1. 数据埋点、上报
1137
+ defaultApi.writeLog(`pullAudioFlow -- operation ${operation} streamId ${streamId}`);
1138
+ if (!streamId) {
1139
+ throw new Error('pullAudioFlow -- streamId is not allowed to be empty');
1140
+ }
1141
+ if (streamIdToPreviewId[streamId] == undefined) {
1142
+ // NOTICE.pullAudioFlowError({ streamid })
1143
+ _initPullData(streamId); // 启动
1144
+ }
1145
+ try {
1146
+ streamIdRtcPlayerInfo1[streamId].audio_type = operation;
1147
+ } catch (e) {
1148
+ defaultApi.writeLog(`pullAudioFlow ${streamId} --error ${JSON.stringify(e)}`, null, 'error');
1149
+ }
1150
+ try {
1151
+ dataReport.setPullVoice({
1152
+ code: +operation,
1153
+ pull_uid: util.getUidByStreamId(streamId),
1154
+ pull_streamid: streamId,
1155
+ playerId
1156
+ // operator:'client'
1157
+ });
1158
+ } catch (e) { };
1159
+ // NOTE 2. 正式拉流;
1160
+ if (!streamIdToStream[streamId]) {
1161
+ streamIdToStream[streamId] = await zg.startPlayingStream(streamId, { video: false });
1162
+ };
1163
+ // NOTE 3. 流挂载到body上
1164
+ if (!operation) {
1165
+ try {
1166
+ const dom = document.querySelector('#' + streamId); // id不存在会抛错
1167
+ if(dom) { dom.remove(); }
1168
+ } catch (error) { }
1169
+ const div = document.createElement('div');
1170
+ div.style.display = 'none';
1171
+ div.id = streamId;
1172
+ document.body.appendChild(div);
1173
+ await startLocalOrRemotePreview(false, streamId, streamId);
1174
+ }
1175
+
1176
+ await zg.mutePlayStreamAudio(streamId, operation);
1177
+ }
1178
+ async function activateVideoPlayStream(streamId, bActive) {
1179
+ // NOTE WEB无此接口,此处业务侧应该是想达到是否渲染音视频的目的。所以web端先拉流,再按此设置值渲染
1180
+ if(!streamIdRtcPlayerInfo1[streamId]) {
1181
+ resetStreamIdRtcPlayerInfo1(streamId);
1182
+ }
1183
+ streamIdRtcPlayerInfo1[streamId].video_type = bActive;
1184
+ if(streamIdToStream[streamId]) { // 流已拉取
1185
+ await zg.mutePlayStreamVideo(streamId, !bActive);
1186
+ }
1187
+ }
1188
+
1189
+ function setAudioVolumeNotify() {
1190
+ // NOTE WEB无此功能
1191
+ }
1192
+ function stopAudioVolumeNotify() {
1193
+ // NOTE WEB无此功能
1194
+ }
1195
+ function openMicVolumeCb() {}
1196
+ // 声浪开关
1197
+ function startSoundLevelMonitor() {
1198
+ zg.setSoundLevelDelegate(true, soundLevelMonitorCycle);
1199
+ }
1200
+ function stopSoundLevelMonitor() {
1201
+ zg.setSoundLevelDelegate(false);
1202
+ }
1203
+ // 设置声浪回调周期
1204
+ function setSoundLevelMonitorCycle(interval) {
1205
+ console.warn('[声浪周期]', interval);
1206
+ soundLevelMonitorCycle = interval;
1207
+ zg.setSoundLevelDelegate(true, soundLevelMonitorCycle);
1208
+ }
1209
+ function setMirrorStatus(mirror) {
1210
+ setVideoMirrorMode(mirror);
1211
+ }
1212
+ function getStreamType() {
1213
+ // NOTE 此接口 外层未被使用
1214
+ }
1215
+ /**
1216
+ * @function 设置拉流镜像状态
1217
+ * @param { boolean } type true=开启 false=关闭
1218
+ * @return: Promise
1219
+ */
1220
+ function setPlayViewMirror(mirror) {
1221
+ // 遍历所有的拉流streamview,逐个镜像
1222
+ Object.keys(streamIdToStreamView).forEach(streamId=> {
1223
+ try {
1224
+ const streamView = streamIdToStreamView[streamId];
1225
+ streamView.play(streamView.v.container, { mirror });
1226
+ } catch (error) { }
1227
+ });
1228
+ }
1229
+ /**
1230
+ * @function 是否推声音
1231
+ * @param mute:Boolean true/不推声音,false/推声音,必选
1232
+ * @return Promise | void
1233
+ */
1234
+ function setMuteStreamAudio(mute) {
1235
+ if(localStream) {
1236
+ return zg.mutePublishStreamAudio(localStream, mute);
1237
+ }
1238
+ }
1239
+ /**
1240
+ * @function 是否推画面
1241
+ * @param mute:Boolean true/不推画面,false/推画面,必选
1242
+ * @return Promise | void
1243
+ */
1244
+ function setMuteStreamVideo() {
1245
+ if(localStream) {
1246
+ return zg.mutePublishStreamVideo(localStream, mute);
1247
+ }
1248
+ }
1249
+ function teacherStartLinkMic() {
1250
+ // NOTE 此接口 暂时不用实现
1251
+ }
1252
+ function teacherStopLinkMic() {
1253
+ // NOTE 此接口 暂时不用实现
1254
+ }
1255
+ function getSDKVersion() {
1256
+ return zg.getVersion();
1257
+ }
1258
+ function getCameraSupportedRes() {
1259
+ // TODO WEB需要创建流去确认,能创成则支持
1260
+ }
1261
+ function setVideoMirrorMode(mirror) {
1262
+ // localStream.stopVideo();
1263
+ localStream.playVideo(cameraConstraints.captureView, {mirror});
1264
+ }
1265
+ function startMultiScreen() {}
1266
+ function addListener() {
1267
+ console.warn('[注册监听]');
1268
+ // OnInitSDK
1269
+ // NOTE WEB没有此事件
1270
+ // OnLoginChannel
1271
+ zg.on('roomStateChanged', _onRoomStateChanged);
1272
+ // OnPublishStateUpdate
1273
+ zg.on('publisherStateUpdate', _onPublisherStateUpdate);
1274
+ // OnPublishQualityUpdate
1275
+ zg.on('publishQualityUpdate', _onPublishQualityUpdate); // 重要
1276
+ // OnCaptureSoundLevelUpdate
1277
+ zg.on('capturedSoundLevelUpdate', _onCapturedSoundLevelUpdate);
1278
+ // OnSoundLevelUpdate
1279
+ zg.on('soundLevelUpdate', _onSoundLevelUpdate); // 重要
1280
+ // OnPlayStateUpdate
1281
+ zg.on('playerStateUpdate', _onPlayerStateUpdate);
1282
+ // OnPlayQulityUpdate
1283
+ zg.on('playQualityUpdate', _onPlayQualityUpdate); // 重要
1284
+ // OnVideoDeviceStateChanged
1285
+ zg.on('videoDeviceStateChanged', _onVideoDeviceStateChanged);
1286
+ // OnAudioDeviceStateChanged
1287
+ zg.on('audioDeviceStateChanged', _onAudioDeviceStateChanged);
1288
+ // OnDeviceError
1289
+ zg.on('deviceError', _onDeviceError);
1290
+ // screenSharingEnded
1291
+ zg.on('screenSharingEnded', _onScreenSharingEnded);
1292
+ // OnAVKitEvent
1293
+ // OnAVEngineStop
1294
+ // OnMixStream
1295
+ // OnRenderRemoteVideoFirstFrame 渲染相关的事件在streamView上管理
1296
+ // OnRecvRemoteAudioFirstFrame 渲染相关的事件在streamView上管理
1297
+ // onRecvMediaSideInfo
1298
+ // zego_recovery_start
1299
+ // OnRelayCDNStateUpdate
1300
+ // OnCaptureVideoFirstFrame 渲染相关的事件在streamView上管理
1301
+ // OnKaDunEvent
1302
+
1303
+ // NOTE 原native代码里缺少流更新事件监听
1304
+ zg.on('roomStreamUpdate', _onRoomStreamUpdate);
1305
+ }
1306
+ function removerListener() {
1307
+ console.warn('[注销监听]');
1308
+ // OnInitSDK
1309
+ // NOTE WEB没有此事件
1310
+ // OnLoginChannel
1311
+ zg.off('roomStateChanged');
1312
+ // OnPublishStateUpdate
1313
+ zg.off('publisherStateUpdate');
1314
+ // OnPublishQualityUpdate
1315
+ zg.off('publishQualityUpdate'); // 重要
1316
+ // OnCaptureSoundLevelUpdate
1317
+ zg.off('capturedSoundLevelUpdate'); // 重要
1318
+ // OnSoundLevelUpdate
1319
+ zg.off('soundLevelUpdate'); // 重要
1320
+ // OnPlayStateUpdate
1321
+ zg.off('playerStateUpdate');
1322
+ // OnPlayQulityUpdate
1323
+ zg.off('playQualityUpdate'); // 重要
1324
+ // OnVideoDeviceStateChanged
1325
+ zg.off('videoDeviceStateChanged');
1326
+ // OnAudioDeviceStateChanged
1327
+ zg.off('audioDeviceStateChanged');
1328
+ // OnDeviceError
1329
+ zg.off('deviceError');
1330
+ // screenSharingEnded
1331
+ zg.off('screenSharingEnded');
1332
+ // OnAVKitEvent
1333
+ // OnAVEngineStop
1334
+ // OnMixStream
1335
+ // OnRenderRemoteVideoFirstFrame 渲染相关的事件在streamView上管理 重要
1336
+ // OnRecvRemoteAudioFirstFrame 渲染相关的事件在streamView上管理 重要
1337
+ // onRecvMediaSideInfo
1338
+ // zego_recovery_start
1339
+ // OnRelayCDNStateUpdate
1340
+ // OnCaptureVideoFirstFrame 渲染相关的事件在streamView上管理
1341
+ // OnKaDunEvent
1342
+
1343
+ // NOTE 原native代码里缺少流更新事件监听
1344
+ zg.off('roomStreamUpdate');
1345
+ }
1346
+
1347
+ function _onRoomStateChanged(roomID, reason, errorCode, extendData) {
1348
+ console.warn('[房间状态回调]', roomID, reason, errorCode, extendData);
1349
+ if (reason == 'LOGINED') {
1350
+ hasLoginChannel = 1;
1351
+ }
1352
+ if (errorCode >= 1002050 && errorCode < 1002060) {
1353
+ defaultApi.writeLog('ZEGO::leaveRoom-- trigger by roomStateChange');
1354
+ window.zbztsdk.leaveRoom();
1355
+ }
1356
+ defaultApi.writeLog(`ZEGO::addListener-- OnLoginChannel: ${errorCode}`);
1357
+ }
1358
+ function _onPublisherStateUpdate(result) {
1359
+ const { state, streamID, errorCode, extendedData } = result;
1360
+ console.log('[推流状态回调]', result);
1361
+ // NOTE 状态码变化相关日志 待补充
1362
+ // NOTE 1. hasPushFlow: 推流成功后启动数据上报; window.zegoHasPushFlow: 加学生端加入房间消息; 当前都用不着
1363
+ if (state == 'PUBLISHING') {
1364
+ // 心跳开始
1365
+ if (!isFirstHeartBeatReport) {
1366
+ isFirstHeartBeatReport = true;
1367
+ heartBeatDataReport('start');
1368
+ }
1369
+ NOTICE.pushFlowSuccess({ code: 0, publish_streamid: streamID });
1370
+ }
1371
+ //
1372
+ let ReportArgs = { publish_rtc_streamid: streamID };
1373
+ dataReport.publishResult({
1374
+ code: errorCode,
1375
+ ...ReportArgs
1376
+ });
1377
+ }
1378
+ function _onPublishQualityUpdate(streamId, stats) {
1379
+ console.log('推流质量回调', streamId, stats);
1380
+ NOTICE.pushQuality({
1381
+ video: {
1382
+ streamId,
1383
+ bitrate: Math.round(stats.video.videoBitrate),
1384
+ fps: Math.round(stats.video.videoFPS)
1385
+ }
1386
+ });
1387
+ NOTICE.pushLossAndDelay({
1388
+ userId: util.getUidByStreamId(streamId),
1389
+ delay: stats.currentRoundTripTime,
1390
+ lostrate: stats.video.videoPacketsLostRate
1391
+ });
1392
+ heartBeatDataReportCalc('onPublishQualityUpdate', stats, streamId);
1393
+ }
1394
+ function _onCapturedSoundLevelUpdate(soundLevel) {
1395
+ if (isNoticeMicVolumeZego) {
1396
+ NOTICE.captureMicVolumeChanged({
1397
+ volume: Math.round(soundLevel)
1398
+ });
1399
+ }
1400
+ heartBeatDataReportObj.volume = heartBeatDataReportObj.volume + Math.round(soundLevel) + ',';
1401
+ }
1402
+ function _onSoundLevelUpdate(soundLevelList) {
1403
+ soundLevelList.forEach(item => {
1404
+ const cbData = {
1405
+ streamid: item.streamID,
1406
+ userId: util.getUidByStreamId(item.streamID),
1407
+ volume: Math.round(item.soundLevel)
1408
+ };
1409
+ NOTICE.playerVolumeChanged(cbData);
1410
+ NOTICE.volumeChange(cbData);
1411
+ if (streamIdRtcPlayerInfo[item.streamID]) {
1412
+ streamIdRtcPlayerInfo[item.streamID].volume = streamIdRtcPlayerInfo[item.streamID].volume + Math.round(item.soundLevel) + ',';
1413
+ }
1414
+ });
1415
+ }
1416
+ function _onPlayerStateUpdate(result) {
1417
+ // TODO
1418
+ const { state, streamID, errorCode, extendedData } = result;
1419
+ if (state == 'PLAYING') {
1420
+ console.log('成功拉取音视频流:', streamID);
1421
+ } else if (state == 'NO_PLAY') {
1422
+ console.log('未拉取音视频流');
1423
+ } else if (state == 'PLAY_REQUESTING') {
1424
+ console.log('请求拉取音视频流:', streamID);
1425
+ }
1426
+ console.log('错误码:', errorCode,' 额外信息:', extendedData);
1427
+ }
1428
+ function _onPlayQualityUpdate(streamID,stats) {
1429
+ console.log('拉流质量回调', streamID, stats);
1430
+ NOTICE.pullQuality({
1431
+ video: {
1432
+ streamId: streamID,
1433
+ userId: util.getUidByStreamId(streamID),
1434
+ bitrate: stats.video.videoBitrate,
1435
+ fps: stats.video.videoFPS,
1436
+ volume: stats.audio.audioLevel * 100 >> 0,
1437
+ }
1438
+ });
1439
+ // console.log('拉流延迟丢包zego',_data);
1440
+ NOTICE.playLossAndDelay({
1441
+ userId: util.getUidByStreamId(streamID),
1442
+ delay: stats.currentRoundTripTime,
1443
+ lostrate: stats.video.videoPacketsLostRate
1444
+ });
1445
+ currStreamIdRtcPlayerInfo = streamIdRtcPlayerInfo[streamID]; // ???
1446
+ heartBeatDataReportCalc('onPlayQualityUpdate', stats, streamID);
1447
+ }
1448
+ function _onRoomStreamUpdate(roomID, updateType, streamList, extendedData) {
1449
+ console.warn('[房间流回调]', roomID, updateType, streamList, extendedData);
1450
+ }
1451
+ function _onVideoDeviceStateChanged(updateType, deviceInfo) {
1452
+ // NOTE 1. state 0=添加设备 1=移除设备
1453
+ const state = updateType == 'ADD'? 0: 1;
1454
+ const agrs = {
1455
+ // NOTE 2. 插设备,用新设备;拔设备,用默认设备
1456
+ deviceId: updateType == 'DELETE' ? '' : deviceInfo.deviceID,
1457
+ idx: 0,
1458
+ operationType: 'hotPlug',
1459
+ deviceState: state,
1460
+ changedDeviceId: deviceInfo.deviceID
1461
+ };
1462
+ setVideoDevice(agrs);
1463
+ }
1464
+ function _onAudioDeviceStateChanged(updateType, deviceType, deviceInfo) {
1465
+ const state = updateType == 'ADD'? 0: 1;
1466
+ const agrs = {
1467
+ // NOTE 2. 插设备,用新设备;拔设备,用默认设备
1468
+ deviceId: updateType == 'DELETE' ? '' : deviceInfo.deviceID,
1469
+ idx: 0,
1470
+ operationType: 'hotPlug',
1471
+ deviceState: state,
1472
+ changedDeviceId: deviceInfo.deviceID
1473
+ };
1474
+ if(deviceType == 'Input') { // microphone
1475
+ setMicrophoneDevice(agrs);
1476
+ }else { // speaker
1477
+ setSpeakerDevice(agrs);
1478
+ }
1479
+ }
1480
+
1481
+ function _onDeviceError(errorCode, deviceName) {
1482
+ defaultApi.writeLog(`avsdk ZEGO:: OnDeviceError ${deviceName} ${errorCode}`);
1483
+ dataReport.deviceError({ deviceName, errorCode });
1484
+ }
1485
+
1486
+ function _onScreenSharingEnded() {
1487
+ defaultApi.writeLog('avsdk ZEGO:: _onScreenSharingEnded');
1488
+ zg.stopPublishingStream(screenStreamId);
1489
+ zg.destroyStream(screenStream);
1490
+ NOTICE.screenShareEnded();
1491
+ }
1492
+
1493
+ function loadZego() {}
1494
+
1495
+ //推流字段
1496
+ function resetHeartBeatDataReportObj() {
1497
+ heartBeatDataReportObj = {
1498
+ ctime: [],
1499
+ push_type: 'none',
1500
+ // video_fps: 0,
1501
+ // video_bitrate: 0,
1502
+ // audio_fps: 0,
1503
+ // audio_bitrate: 0,
1504
+ video_fps: [],
1505
+ video_bitrate: [],
1506
+ audio_fps: [],
1507
+ audio_bitrate: [],
1508
+ push_loss: [],
1509
+ push_delay: [],
1510
+ // count: 0,
1511
+ volume: ''
1512
+ };
1513
+ };
1514
+ //记录拉流类型,用作数据上报
1515
+ function resetStreamIdRtcPlayerInfo1(streamId) {
1516
+ streamIdRtcPlayerInfo1[streamId] = {
1517
+ audio_type: false,
1518
+ video_type: false,
1519
+ stream_type: 'none'
1520
+ };
1521
+ }
1522
+ //拉流字段
1523
+ function resetStreamIdRtcPlayerInfo(streamId) {
1524
+ streamIdRtcPlayerInfo[streamId] = {
1525
+ pull_uid: util.getUidByStreamId(streamId),
1526
+ streamid: streamId,
1527
+ ctime: [],
1528
+ pull_type: streamIdRtcPlayerInfo1[streamId].stream_type,
1529
+ volume: '',
1530
+ // 平均值
1531
+ video_fps: [],
1532
+ video_bitrate: [],
1533
+ audio_fps: [],
1534
+ audio_bitrate: [],
1535
+ pull_loss: [],
1536
+ pull_delay: [],
1537
+ //音画不同步字段
1538
+ avtimestampdiff: [],
1539
+ // 累加
1540
+ audio_ifg: 0,
1541
+ video_ifg: 0
1542
+ };
1543
+ }
1544
+ function heartBeatDataReport(type) {
1545
+ try {
1546
+ if (type === 'start' && !heartBeatDataReportTimer) {
1547
+ _heartBeatDataReport();
1548
+ heartBeatDataReportTimer = setInterval(() => {
1549
+ _heartBeatDataReport();
1550
+ }, 30 * 1000);
1551
+ }
1552
+ if (type === 'stop') {
1553
+ clearInterval(heartBeatDataReportTimer);
1554
+ heartBeatDataReportTimer = null;
1555
+ }
1556
+ } catch (error) {
1557
+ console.log(error);
1558
+ }
1559
+ };
1560
+
1561
+ function _heartBeatDataReport() {
1562
+ let rateCount = 0;
1563
+ let rateTimer = setInterval(async () => {
1564
+ rateCount++;
1565
+ // NOTE 30s后上报?
1566
+ if (rateCount >= 3) {
1567
+ const pullInfo = [];
1568
+ Object.keys(streamIdRtcPlayerInfo).forEach(streamid => {
1569
+ heartBeatRealKeys.forEach(realKey => {
1570
+ if (!streamIdRtcPlayerInfo[streamid].hasOwnProperty(realKey)) {
1571
+ streamIdRtcPlayerInfo[streamid][realKey] = [];
1572
+ }
1573
+ });
1574
+
1575
+ //获取拉流类型,后期可写为函数提出去
1576
+ if (streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
1577
+ streamIdRtcPlayerInfo1[streamid].stream_type = 'both';
1578
+ } else if (!streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
1579
+ streamIdRtcPlayerInfo1[streamid].stream_type = 'video';
1580
+ } else if (streamIdRtcPlayerInfo1[streamid].audio_type && !streamIdRtcPlayerInfo1[streamid].video_type) {
1581
+ streamIdRtcPlayerInfo1[streamid].stream_type = 'audio';
1582
+ } else {
1583
+ streamIdRtcPlayerInfo1[streamid].stream_type = 'none';
1584
+ }
1585
+
1586
+ pullInfo.push({
1587
+ streamid,
1588
+ ...streamIdRtcPlayerInfo[streamid],
1589
+ pull_type: streamIdRtcPlayerInfo1[streamid].stream_type,
1590
+ volume: streamIdRtcPlayerInfo[streamid].volume.slice(0, streamIdRtcPlayerInfo[streamid].volume.length - 1)
1591
+ });
1592
+ resetStreamIdRtcPlayerInfo(streamid);
1593
+ });
1594
+ if (isFirstHeartBeatReport && rateCount > 0) {
1595
+ try {
1596
+ //静音推流时过滤掉音频帧率和码率,上报为0;
1597
+
1598
+ //获取推流类型,后期可写为函数提出去
1599
+ // TODO
1600
+ // if (zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
1601
+ // zbysdk.deviceStatus.stream_type = 'both';
1602
+ // } else if (!zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
1603
+ // zbysdk.deviceStatus.stream_type = 'audio';
1604
+ // } else if (zbysdk.deviceStatus.camera && !zbysdk.deviceStatus.microphone) {
1605
+ // zbysdk.deviceStatus.stream_type = 'video';
1606
+ // } else {
1607
+ // zbysdk.deviceStatus.stream_type = 'none';
1608
+ // };
1609
+ // defaultApi.writeLog(`push_type_zego,camera: ${zbysdk.deviceStatus.camera},microphone: ${zbysdk.deviceStatus.microphone},type: ${zbysdk.deviceStatus.stream_type},a_fps: ${dataZegoCapture.afps}, a_bit: ${dataZegoCapture.akbps}, v_fps: ${dataZegoCapture.fps}, v_bit: ${dataZegoCapture.kbps}`);
1610
+
1611
+ // dataReport.heartbeat({
1612
+ // ...{ ...heartBeatDataReportObj, push_type: zbysdk.deviceStatus.stream_type, volume: heartBeatDataReportObj.volume.slice(0, heartBeatDataReportObj.volume.length - 1) },
1613
+ // pull_info: pullInfo,
1614
+ // });
1615
+ } catch (e) {
1616
+ console.log(e);
1617
+ }
1618
+ }
1619
+ resetHeartBeatDataReportObj();
1620
+ rateCount = 0;
1621
+
1622
+ clearInterval(rateTimer);
1623
+ }
1624
+ }, 10 * 1000);
1625
+ };
1626
+
1627
+ function heartBeatDataReportCalc(name, stats, streamId) {
1628
+ const audioFPS = parseFloat(parseInt(stats.audio.audioFPS));
1629
+ const audioBitrate = parseFloat(parseInt(stats.audio.audioBitrate));
1630
+ const videoFPS = parseFloat(parseInt(stats.video.videoFPS));
1631
+ const videoBitrate = parseFloat(parseInt(stats.video.videoBitrate));
1632
+ let info = {};
1633
+ if(name == 'onPublishQualityUpdate') {
1634
+ info = heartBeatDataReportObj;
1635
+ dataZegoCapture = stats;
1636
+ info.push_loss.push(stats.video.videoPacketsLostRate);
1637
+ info.push_delay.push(stats.currentRoundTripTime);
1638
+ } else if(name == 'onPlayQualityUpdate') {
1639
+ info = streamIdRtcPlayerInfo[streamId];
1640
+ // streamIdRtcPlayerInfo[streamId].avtimestampdiff.push(_d.avTimestampDiff); // 时间差??? TODO
1641
+ info.pull_loss.push(stats.video.videoPacketsLostRate);
1642
+ info.pull_delay.push(stats.currentRoundTripTime);
1643
+ }
1644
+ info.audio_fps.push(audioFPS);
1645
+ info.audio_bitrate.push(audioBitrate);
1646
+ info.video_fps.push(videoFPS);
1647
+ info.video_bitrate.push(videoBitrate);
1648
+
1649
+ info.ctime.push(Math.round((new Date().getTime() + dataReport.timestamp) / 1000));
1650
+
1651
+ }
1652
+
1653
+ export default {
1654
+ startEngine,
1655
+ destroyEngine,
1656
+ setDefaultDevice,
1657
+ getVideoDeviceList,
1658
+ setVideoDevice,
1659
+ enableCamera,
1660
+ enableMic,
1661
+ setAudioSpeakerCapture, // done
1662
+ getAudioDeviceList,
1663
+ setAudioDevice,
1664
+ getSpeakerDeviceList,
1665
+ setSpeakerDevice,
1666
+ getCurrentSpeakerDevice,
1667
+ getSpeakerDeviceVolume,
1668
+ setSpeakerDeviceVolume,
1669
+ setSpeakerDeviceMute,
1670
+ getMicrophoneDeviceList,
1671
+ setMicrophoneDevice,
1672
+ getMicDeviceVolume,
1673
+ setMicDeviceVolume,
1674
+ setMicDeviceMute,
1675
+ getSpeakerSimpleVolume,
1676
+ setSpeakerSimpleVolume,
1677
+ setSpeakerSimpleMute,
1678
+ setPreviewView, // native端是先设置视图再预览,WEB不需要设置视图,无此功能
1679
+ startPreview,
1680
+ startLocalOrRemotePreview,
1681
+ setVideoFPS,
1682
+ setVideoBitrate,
1683
+ setAudioBitrate,
1684
+ setVideoCaptureResolution,
1685
+ setCameraParam,
1686
+ setVideoEncodeResolution,
1687
+ init,
1688
+ startPublish2,
1689
+ stopPublish,
1690
+ initPullFlow,
1691
+ playStream,
1692
+ stopPlayStream,
1693
+ stopAllPlayStream,
1694
+ leaveRoom,
1695
+ playerSnapShot,
1696
+ activateAudioPlayStream,
1697
+ pullAudioFlow,
1698
+ activateVideoPlayStream,
1699
+ setAudioVolumeNotify, // native 监听系统设备音量变化开关,WEB无此接口
1700
+ stopAudioVolumeNotify,
1701
+ openMicVolumeCb,
1702
+ startSoundLevelMonitor,
1703
+ stopSoundLevelMonitor,
1704
+ setSoundLevelMonitorCycle,
1705
+ setMirrorStatus,
1706
+ getStreamType,
1707
+ setPlayViewMirror,
1708
+ setMuteStreamAudio,
1709
+ setMuteStreamVideo,
1710
+ teacherStartLinkMic,
1711
+ teacherStopLinkMic, // 不要
1712
+ getSDKVersion,
1713
+ getCameraSupportedRes,
1714
+ setVideoMirrorMode,
1715
+ startMultiScreen,
1716
+ addListener,
1717
+ };
1718
+