@tencentcloud/trtc-cloud-wx 1.0.7 → 1.0.9-beta.0

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 (56) hide show
  1. package/.babelrc +6 -0
  2. package/.eslintrc.json +129 -0
  3. package/build/chokidar.js +19 -0
  4. package/build/clear.js +24 -0
  5. package/build/copy.js +24 -0
  6. package/build/copy_to_roomkit.js +19 -0
  7. package/dist/package.json +14 -0
  8. package/{trtc-cloud-wx.js → dist/trtc-cloud-wx.js} +25 -40
  9. package/docs/API/TRTCCloud.html +4353 -0
  10. package/docs/API/index.html +98 -0
  11. package/docs/API/scripts/add-toc.js +57 -0
  12. package/docs/API/scripts/collapse.js +20 -0
  13. package/docs/API/scripts/highlight/highlight.min.js +1282 -0
  14. package/docs/API/scripts/highlight/highlightjs-line-numbers.min.js +1 -0
  15. package/docs/API/scripts/linenumber.js +25 -0
  16. package/docs/API/scripts/nav.js +12 -0
  17. package/docs/API/scripts/polyfill.js +4 -0
  18. package/docs/API/scripts/prettify/Apache-License-2.0.txt +202 -0
  19. package/docs/API/scripts/prettify/lang-css.js +2 -0
  20. package/docs/API/scripts/prettify/prettify.js +28 -0
  21. package/docs/API/scripts/search.js +83 -0
  22. package/docs/API/styles/font.css +81 -0
  23. package/docs/API/styles/fonts/JTURjIg1_i6t8kCHKm45_dJE3g3D_vx3rCubqg.woff2 +0 -0
  24. package/docs/API/styles/fonts/JTURjIg1_i6t8kCHKm45_dJE3gTD_vx3rCubqg.woff2 +0 -0
  25. package/docs/API/styles/fonts/JTURjIg1_i6t8kCHKm45_dJE3gbD_vx3rCubqg.woff2 +0 -0
  26. package/docs/API/styles/fonts/JTURjIg1_i6t8kCHKm45_dJE3gfD_vx3rCubqg.woff2 +0 -0
  27. package/docs/API/styles/fonts/JTURjIg1_i6t8kCHKm45_dJE3gnD_vx3rCs.woff2 +0 -0
  28. package/docs/API/styles/fonts/JTUSjIg1_i6t8kCHKm459W1hyyTh89ZNpQ.woff2 +0 -0
  29. package/docs/API/styles/fonts/JTUSjIg1_i6t8kCHKm459WRhyyTh89ZNpQ.woff2 +0 -0
  30. package/docs/API/styles/fonts/JTUSjIg1_i6t8kCHKm459WZhyyTh89ZNpQ.woff2 +0 -0
  31. package/docs/API/styles/fonts/JTUSjIg1_i6t8kCHKm459WdhyyTh89ZNpQ.woff2 +0 -0
  32. package/docs/API/styles/fonts/JTUSjIg1_i6t8kCHKm459WlhyyTh89Y.woff2 +0 -0
  33. package/docs/API/styles/highlight/highlight.min.css +26 -0
  34. package/docs/API/styles/highlight/rainbow.min.css +1 -0
  35. package/docs/API/styles/jsdoc.css +684 -0
  36. package/docs/API/styles/prettify.css +79 -0
  37. package/docs/API/styles/toc.css +44 -0
  38. package/docs/API/tutorial-00-guideline.html +81 -0
  39. package/docs/doc-src/home.md +7 -0
  40. package/docs/doc-src/tutorials/00-guideline.md +1 -0
  41. package/docs/doc-src/tutorials/tutorials.json +5 -0
  42. package/jsdoc.json +43 -0
  43. package/package.json +40 -3
  44. package/rollup.config.js +19 -0
  45. package/sdk_publish.bash +5 -0
  46. package/src/TaskMachine.ts +371 -0
  47. package/src/index.ts +1184 -0
  48. package/src/interface/index.ts +45 -0
  49. package/src/interface/types.ts +95 -0
  50. package/src/log/logger.ts +80 -0
  51. package/src/types.d.ts +1 -0
  52. package/src/utils/common.ts +50 -0
  53. package/src/utils/index.ts +2 -0
  54. package/src/utils/translate.ts +139 -0
  55. package/tsconfig.json +15 -0
  56. package/typedoc.json +21 -0
package/src/index.ts ADDED
@@ -0,0 +1,1184 @@
1
+ import EventEmitter from 'eventemitter3'
2
+ import TRTC from 'trtc-wx-sdk'
3
+ import {
4
+ TRTCParams,
5
+ TRTCAppScene,
6
+ TRTCRoleType,
7
+ TRTCVideoStreamType,
8
+ TRTCVideoEncParam,
9
+ TRTCRenderParams,
10
+ TRTCAudioQuality,
11
+ TRTCVolumeInfo,
12
+ TRTCBeautyStyle,
13
+ AudioMusicParam,
14
+ TRTCAudioRoute,
15
+ } from './interface'
16
+ import {
17
+ setHandle, Handletype, TaskQueueEvent, taskMachine
18
+ } from './TaskMachine'
19
+ import {
20
+ translateTRTCAppScene,
21
+ translateTRTCStreamId,
22
+ translateTRTCVideoMirrorType,
23
+ translateTRTCVideoResolution,
24
+ translateTRTCVideoRotation,
25
+ translateVideoFillMod,
26
+ translateTRTCAudioRoute,
27
+ uniqueFunc,
28
+ isNumber,
29
+ safelyParse,
30
+ translateBeautyStyle
31
+ } from './utils'
32
+
33
+ import {logger} from './log/logger'
34
+
35
+ import packageJson from '../package.json' // 假设 package.json 文件在项目根目录下
36
+
37
+ export * from './interface'
38
+ export * from './utils/translate'
39
+
40
+ /**
41
+ * TRTCCloud
42
+ * @interface
43
+ */
44
+ export class TRTCCloud {
45
+ static instance;
46
+
47
+ public version = packageJson.version;
48
+
49
+ // 内部使用,用于调用接口时UI测监听,完成剩余逻辑
50
+ public InterfaceEventEmitter = new EventEmitter();
51
+
52
+ // 外部使用,用户监听回调事件
53
+ private EventEmitter = new EventEmitter();
54
+
55
+ public trtc;
56
+
57
+ public TRTC_EVENT;
58
+
59
+ private role = TRTCRoleType.TRTCRoleAnchor;
60
+
61
+ private scene: TRTCAppScene;
62
+
63
+ public userId = '';
64
+
65
+ private isEnterRoom = false;
66
+
67
+ private isVideoMuted = false;
68
+
69
+ private isOpenCamera = false;
70
+
71
+ private currentSoundMode = TRTCAudioRoute.TRTC_AUDIO_ROUTE_SPEAKER;
72
+
73
+ private audioVolumeEvaluation = 0;
74
+
75
+ private encsmall = 0;
76
+
77
+ private enableinfiniteanchor = 0;
78
+
79
+ private allTimer: Record<string, NodeJS.Timer> = {};
80
+
81
+ private renderMap = new Map();
82
+
83
+ private pusherIsReady = false;
84
+
85
+ public logger = logger;
86
+
87
+ // private handleAudioVolumeUpdate: (userVolumes: Array<TRTCVolumeInfo>) => void;
88
+
89
+ constructor() {
90
+ this.init()
91
+ }
92
+
93
+ /**
94
+ * 获取 TRTCCloud 实例(单例模式)
95
+ * @category Base
96
+ */
97
+ static getTRTCShareInstance(): TRTCCloud {
98
+ if (!TRTCCloud.instance) {
99
+ TRTCCloud.instance = new TRTCCloud()
100
+ }
101
+ return TRTCCloud.instance
102
+ }
103
+
104
+ /**
105
+ * 销毁 TRTCCloud 实例(单例模式)
106
+ * @category Base
107
+ */
108
+ static destroyTRTCShareInstance(): void {
109
+ if (!TRTCCloud.instance) return
110
+ TRTCCloud.instance.destroy()
111
+ TRTCCloud.instance = null
112
+ }
113
+
114
+ public createSubCloud(): TRTCCloud {
115
+ return TRTCCloud.getTRTCShareInstance()
116
+ }
117
+
118
+ getSDKVersion(): string {
119
+ return this.version
120
+ }
121
+
122
+ private destroy(): void {
123
+ this.offTRTCEvent()
124
+ this.EventEmitter.removeAllListeners()
125
+ this.InterfaceEventEmitter.removeAllListeners()
126
+ this.trtc.exitRoom()
127
+ }
128
+
129
+ private init(): void {
130
+ this.trtc = new TRTC(this)
131
+ this.trtc.setLogLevel(3)
132
+ this.TRTC_EVENT = this.trtc.EVENT
133
+ this.bindTRTCEvent()
134
+ this.bindComponentEvent()
135
+ this.handleNetworkQuality = this.getHandleNetworkQuality(2000)
136
+ }
137
+
138
+ private reset(): void {
139
+ this.role = TRTCRoleType.TRTCRoleAnchor
140
+ this.userId = ''
141
+ this.isEnterRoom = false
142
+ this.isVideoMuted = false
143
+ this.isOpenCamera = false
144
+ this.currentSoundMode = TRTCAudioRoute.TRTC_AUDIO_ROUTE_SPEAKER
145
+ this.encsmall = 0
146
+ this.enableinfiniteanchor = 0
147
+ taskMachine.reset()
148
+ Object.keys(this.allTimer).forEach((key) => {
149
+ clearInterval(this.allTimer[key])
150
+ this.allTimer[key] = null
151
+ })
152
+ }
153
+
154
+ private bindComponentEvent(): void {
155
+ this.InterfaceEventEmitter.on('pusherDomReady', (isReady: boolean) => {
156
+ this.pusherIsReady = isReady
157
+ taskMachine.setPusherDomReady(isReady)
158
+ if (isReady) {
159
+ this.trtc.createPusher({})
160
+ taskMachine.send(TaskQueueEvent.SETPROCESS)
161
+ } else {
162
+ if (this.isEnterRoom) {
163
+ this.exitRoom()
164
+ }
165
+ taskMachine.send(TaskQueueEvent.SETDONE)
166
+ }
167
+ })
168
+
169
+ this.InterfaceEventEmitter.on('playerDomReady', ({isReady, view}) => {
170
+ taskMachine.setPlayerDomReady(view, isReady)
171
+ if (isReady) {
172
+ taskMachine.send(TaskQueueEvent.SETPROCESS, view)
173
+ } else {
174
+ taskMachine.send(TaskQueueEvent.SETDONE, view)
175
+ }
176
+ })
177
+ }
178
+
179
+ private bindTRTCEvent(): void {
180
+ this.trtc.on(this.TRTC_EVENT.LOCAL_LEAVE, (event) => {
181
+ // todo 第二个参数为进房耗时 此处调用start的时候处理
182
+ // this.emit('onEnterRoom', 0)
183
+ })
184
+ this.trtc.on(this.TRTC_EVENT.PLAYER_WARNING, (event) => {
185
+ const {code, message} = event.data
186
+ this.emit('onWarnning', code, message)
187
+ })
188
+ this.trtc.on(this.TRTC_EVENT.ERROR, (event) => {
189
+ const {code, message} = event.data
190
+ this.emit('onError', code, message)
191
+ })
192
+ this.trtc.on(this.TRTC_EVENT.REMOTE_USER_JOIN, (event) => {
193
+ const {userID} = event.data
194
+ this.emit('onRemoteUserEnterRoom', userID)
195
+ })
196
+ // 远端用户退出
197
+ this.trtc.on(this.TRTC_EVENT.REMOTE_USER_LEAVE, (event) => {
198
+ const {userID, playerList} = event.data
199
+ this.InterfaceEventEmitter.emit('onRemoteUserLeaveRoom', userID, 0)
200
+ this.emit('onUserVideoAvailable', userID, 0)
201
+ this.emit('onUserSubStreamAvailable', userID, 0)
202
+ this.emit('onRemoteUserLeaveRoom', userID, 0)
203
+ })
204
+ // 远端用户推送视频
205
+ this.trtc.on(this.TRTC_EVENT.REMOTE_VIDEO_ADD, (event) => {
206
+ const {player} = event.data
207
+ this.emit(player.streamType === 'aux' ? 'onUserSubStreamAvailable' : 'onUserVideoAvailable', player.userID, 1)
208
+ })
209
+ // 远端用户取消推送视频
210
+ this.trtc.on(this.TRTC_EVENT.REMOTE_VIDEO_REMOVE, (event) => {
211
+ const {player} = event.data
212
+ this.emit(player.streamType === 'aux' ? 'onUserSubStreamAvailable' : 'onUserVideoAvailable', player.userID, 0)
213
+ })
214
+ // 远端用户推送音频
215
+ this.trtc.on(this.TRTC_EVENT.REMOTE_AUDIO_ADD, (event) => {
216
+ const {player} = event.data
217
+ this.emit('onUserAudioAvailable', player.userID, 1)
218
+ })
219
+ // 远端用户取消推送音频
220
+ this.trtc.on(this.TRTC_EVENT.REMOTE_AUDIO_REMOVE, (event) => {
221
+ const {player} = event.data
222
+ this.emit('onUserAudioAvailable', player.userID, 0)
223
+ })
224
+ this.trtc.on(this.TRTC_EVENT.REMOTE_AUDIO_VOLUME_UPDATE, (event) => {
225
+ const {playerList} = event.data
226
+ const userVolumes = playerList.map((player) => ({
227
+ userId: player.userID,
228
+ volume: player.volume,
229
+ }))
230
+ this.handleAudioVolumeUpdate(userVolumes)
231
+ })
232
+ this.trtc.on(this.TRTC_EVENT.LOCAL_AUDIO_VOLUME_UPDATE, (event) => {
233
+ const {pusher} = event.data
234
+ const userVolumes = [
235
+ {
236
+ userId: pusher.userID,
237
+ volume: pusher.volume,
238
+ },
239
+ ]
240
+ this.handleAudioVolumeUpdate(userVolumes)
241
+ })
242
+ this.trtc.on(this.TRTC_EVENT.LOCAL_NET_STATE_UPDATE, (event) => {
243
+ const {pusher} = event.data
244
+ this.handleNetworkQuality({pusher})
245
+ })
246
+ this.trtc.on(this.TRTC_EVENT.REMOTE_NET_STATE_UPDATE, (event) => {
247
+ const {playerList} = event.data
248
+ this.handleNetworkQuality({playerList})
249
+ })
250
+ }
251
+
252
+ private offTRTCEvent(): void {
253
+ this.trtc.off(this.TRTC_EVENT.LOCAL_LEAVE)
254
+ this.trtc.off(this.TRTC_EVENT.ERROR)
255
+ this.trtc.off(this.TRTC_EVENT.REMOTE_USER_JOIN)
256
+ this.trtc.off(this.TRTC_EVENT.REMOTE_USER_LEAVE)
257
+ this.trtc.off(this.TRTC_EVENT.REMOTE_VIDEO_ADD)
258
+ this.trtc.off(this.TRTC_EVENT.REMOTE_VIDEO_REMOVE)
259
+ this.trtc.off(this.TRTC_EVENT.REMOTE_AUDIO_ADD)
260
+ this.trtc.off(this.TRTC_EVENT.REMOTE_AUDIO_REMOVE)
261
+ this.trtc.off(this.TRTC_EVENT.REMOTE_AUDIO_VOLUME_UPDATE)
262
+ this.trtc.off(this.TRTC_EVENT.REMOTE_NET_STATE_UPDATE)
263
+ }
264
+
265
+ private getHandleAudioVolumeUpdate(
266
+ interval: number
267
+ ): (userVolumes: Array<TRTCVolumeInfo>) => void {
268
+ if (this.allTimer?.AudioVolume) {
269
+ clearInterval(this.allTimer.AudioVolume)
270
+ this.allTimer.AudioVolume = null
271
+ }
272
+ if (interval === 0) return (): void => { }
273
+ let tempUserVolumes = []
274
+ const filterUselessItem = function (userId: string): void {
275
+ tempUserVolumes = tempUserVolumes.filter(userVolume => userVolume.userId !== userId)
276
+ }
277
+ this.InterfaceEventEmitter.off('onRemoteUserLeaveRoom') // 防止重复监听
278
+ this.InterfaceEventEmitter.on('onRemoteUserLeaveRoom', filterUselessItem, this.getHandleAudioVolumeUpdate)
279
+ return (userVolumes: Array<TRTCVolumeInfo>): void => {
280
+ tempUserVolumes = uniqueFunc(
281
+ [...userVolumes, ...tempUserVolumes],
282
+ 'userId'
283
+ )
284
+ if (this.allTimer.AudioVolume) return
285
+ const timer = setInterval(() => {
286
+ this.emit(
287
+ 'onUserVoiceVolume',
288
+ tempUserVolumes,
289
+ tempUserVolumes.length,
290
+ 0 // todo 此参数无用,暂时废弃
291
+ )
292
+ }, interval)
293
+ this.allTimer.AudioVolume = timer
294
+ }
295
+ }
296
+
297
+ private handleAudioVolumeUpdate(userVolumes: Array<TRTCVolumeInfo>): void { }
298
+
299
+ private getHandleNetworkQuality(
300
+ interval: number
301
+ ): (data: {
302
+ pusher?: Record<string, unknown>;
303
+ playerList?: Array<Record<string, unknown>>;
304
+ }) => void {
305
+ if (this.allTimer?.networkQuality) {
306
+ clearInterval(this.allTimer.networkQuality)
307
+ this.allTimer.networkQuality = null
308
+ }
309
+ if (interval === 0) return (): void => { }
310
+ const resultQuality = {localQuality: null, remoteQuality: []}
311
+ this.on('onRemoteUserLeaveRoom', (userId) => {
312
+ resultQuality.remoteQuality = resultQuality.remoteQuality.filter(quality => quality.userId !== userId)
313
+ }, this)
314
+
315
+ return (data): void => {
316
+ const {pusher, playerList} = data
317
+ if (pusher) resultQuality.localQuality = {userId: pusher.userID, quality: pusher.netStatus}
318
+ if (playerList) {
319
+ resultQuality.remoteQuality = playerList.map(player => ({
320
+ userId: player.userID,
321
+ quality: player.netStatus
322
+ }))
323
+ }
324
+ if (this.allTimer.networkQuality) return
325
+ const timer = setInterval(() => {
326
+ this.emit('onNetworkQuality', resultQuality.localQuality, resultQuality.remoteQuality)
327
+ }, interval)
328
+ this.allTimer.networkQuality = timer
329
+ }
330
+ }
331
+
332
+ private handleNetworkQuality(quality: {
333
+ pusher?: Record<string, unknown>;
334
+ playerList?: Array<Record<string, unknown>>;
335
+ }): void { }
336
+
337
+ /**
338
+ * @category Base
339
+ */
340
+ on(eventName, handler, context): void {
341
+ this.EventEmitter.on(eventName, handler, context)
342
+ }
343
+
344
+ /**
345
+ * @category Base
346
+ */
347
+ off(eventName, handler, context): void {
348
+ this.EventEmitter.off(eventName, handler, context)
349
+ }
350
+
351
+ /**
352
+ * @category Base
353
+ */
354
+ emit(eventName, ...args): void {
355
+ this.EventEmitter.emit(eventName, ...args)
356
+ }
357
+
358
+ /**
359
+ * @category 房间
360
+ */
361
+ async enterRoom(params: TRTCParams, scene: TRTCAppScene): Promise<void> {
362
+ if (this.isEnterRoom) {
363
+ logger.warn('You are already in the room, please exit the current room first.')
364
+ return
365
+ }
366
+ this.trtc.createPusher({})
367
+ const startTime = new Date().getTime()
368
+ const {
369
+ sdkAppId,
370
+ userId,
371
+ userSig,
372
+ roomId,
373
+ strRoomId,
374
+ role,
375
+ privateMapKey,
376
+ } = params
377
+ logger.setInfo({sdkAppId, userId, roomId: strRoomId})
378
+ logger.info('enterRoom with options: ', JSON.stringify(params), scene)
379
+ this.userId = userId
380
+ this.isEnterRoom = true
381
+ try {
382
+ await this.setAttributes({
383
+ params: {
384
+ sdkAppID: sdkAppId, // 您的腾讯云账号
385
+ userID: userId, // 当前进房用户的userID
386
+ userSig, // 您服务端生成的userSig
387
+ roomID: roomId, // 您进房的房间号,
388
+ strRoomID: strRoomId,
389
+ role,
390
+ privateMapKey,
391
+ scene: translateTRTCAppScene(scene),
392
+ enableMic: false, // 进房默认开启音频上行
393
+ enableCamera: false, // 进房默认开启视频上行
394
+ videoPreview: false,
395
+ encsmall: this.encsmall,
396
+ enableinfiniteanchor: this.enableinfiniteanchor, // 是否开启无限主播
397
+ recvMode: this.enableinfiniteanchor ? 2 : 1, // 1 自动接收音视频 2 仅自动接收音频
398
+ }
399
+ }, 'enterRoom')
400
+ } catch (error) {
401
+ this.isEnterRoom = false
402
+ logger.info('enterRoom fail')
403
+ this.emit('onEnterRoom', -1)
404
+ }
405
+ this.trtc.getPusherInstance().start({
406
+ success: async () => {
407
+ logger.info('enterRoom success')
408
+ this.scene = scene
409
+ const endTime = new Date().getTime()
410
+ this.emit('onEnterRoom', endTime - startTime)
411
+ if (this.trtc.getPusherAttributes().videoPreview === true) {
412
+ // 如果进房前有打开摄像头,需要开启视频上行,并抛出 onSendFirstLocalVideoFrame 事件
413
+ await this.setAttributes(
414
+ {
415
+ params: {
416
+ videoPreview: false,
417
+ enableCamera: true,
418
+ }
419
+ },
420
+ 'other'
421
+ )
422
+ this.emit(
423
+ 'onSendFirstLocalVideoFrame',
424
+ TRTCVideoStreamType.TRTCVideoStreamTypeBig,
425
+ )
426
+ }
427
+ },
428
+ fail: () => {
429
+ this.isEnterRoom = false
430
+ logger.info('enterRoom fail')
431
+ this.emit('onEnterRoom', -1)
432
+ },
433
+ })
434
+ }
435
+
436
+ /**
437
+ * @category 房间
438
+ */
439
+ exitRoom(): void {
440
+ logger.info('exitRoom')
441
+ const {pusher} = this.trtc.exitRoom()
442
+ this.reset()
443
+ if (!this.pusherIsReady) {
444
+ logger.info('exitRoom success')
445
+ this.emit('onExitRoom', 0)
446
+ } else {
447
+ this.InterfaceEventEmitter.emit('pusherAttributesChange', {
448
+ pusher,
449
+ callback: () => {
450
+ logger.info('exitRoom success')
451
+ this.emit('onExitRoom', 0)
452
+ },
453
+ })
454
+ }
455
+ }
456
+
457
+ /**
458
+ * @category 房间
459
+ */
460
+ switchRoom(): void { }
461
+
462
+ /**
463
+ * 切换角色,仅适用于直播场景(TRTCAppSceneLIVE 和 TRTCAppSceneVoiceChatRoom)
464
+ * @category 房间
465
+ */
466
+ async switchRole(role: TRTCRoleType): Promise<void> {
467
+ logger.info('switchRole with options: ', role)
468
+ const tempRole = this.role
469
+ this.role = role
470
+ if (tempRole === role || role === TRTCRoleType.TRTCRoleAnchor) {
471
+ logger.info('switchRole success')
472
+ this.emit('onSwitchRole', 0, '')
473
+ return
474
+ }
475
+ await this.setAttributes({params: {enableCamera: false, enableMic: false}}, 'switchRole')
476
+ logger.info('switchRole success')
477
+ this.emit('onSwitchRole', 0, '')
478
+ }
479
+
480
+ connectOtherRoom(): void { }
481
+
482
+ disconnectOtherRoom(): void { }
483
+
484
+ setDefaultStreamRecvMode(): void { }
485
+
486
+ useVirtualBackground(): void { }
487
+
488
+ /**
489
+ * 启动本地摄像头采集和预览
490
+ * @category 视频
491
+ */
492
+ async startLocalPreview(frontCamera = true): Promise<void> {
493
+ logger.info('startLocalPreview with options: ', frontCamera)
494
+ if (this.role !== TRTCRoleType.TRTCRoleAnchor) {
495
+ return
496
+ }
497
+ const pusher = await this.setAttributes({
498
+ params: () => ({
499
+ videoPreview: this.isEnterRoom ? this.isVideoMuted : true,
500
+ enableCamera: this.isEnterRoom ? !this.isVideoMuted : false,
501
+ frontCamera: frontCamera ? 'front' : 'back',
502
+ })
503
+ }, 'startLocalPreview')
504
+ logger.info('startLocalPreview success')
505
+ this.emit('onCameraDidReady')
506
+ this.emit(
507
+ 'onFirstVideoFrame',
508
+ '',
509
+ TRTCVideoStreamType.TRTCVideoStreamTypeBig,
510
+ pusher.videoWidth,
511
+ pusher.videoHeight
512
+ )
513
+ if (this.isEnterRoom) {
514
+ this.emit(
515
+ 'onSendFirstLocalVideoFrame',
516
+ TRTCVideoStreamType.TRTCVideoStreamTypeBig,
517
+ )
518
+ }
519
+ this.isOpenCamera = true
520
+ }
521
+
522
+ /**
523
+ * 停止本地摄像头采集和预览
524
+ * @category 视频
525
+ */
526
+ async stopLocalPreview(): Promise<void> {
527
+ logger.info('stopLocalPreview')
528
+ await this.setAttributes({
529
+ params: {
530
+ videoPreview: false,
531
+ enableCamera: false,
532
+ }
533
+ }, 'stopLocalPreview')
534
+ logger.info('stopLocalPreview success')
535
+ this.isOpenCamera = false
536
+ this.emit('onUserVideoAvailable', this.userId, 0)
537
+ }
538
+
539
+ /**
540
+ * 修改本地摄像头预览的 HTML 元素,小程序不支持
541
+ * @category 视频
542
+ */
543
+ updateLocalView(view: string | null): void {
544
+ logger.info('updateLocalView with options: ', view)
545
+ if (view !== null) {
546
+ this.startLocalPreview()
547
+ return
548
+ }
549
+ this.stopLocalPreview()
550
+ }
551
+
552
+ /**
553
+ * 暂停/恢复发布本地的视频流
554
+ * @category 视频
555
+ * @param {boolean} mute true:屏蔽;false:开启,默认值:false
556
+ * @param {TRTCVideoStreamType} streamType 要暂停/恢复的视频流类型, 仅支持 TRTCVideoStreamTypeBig 和 TRTCVideoStreamTypeSub
557
+ */
558
+ async muteLocalVideo(
559
+ mute: boolean,
560
+ streamType: TRTCVideoStreamType
561
+ ): Promise<void> {
562
+ logger.info('muteLocalVideo called with options: ', mute, streamType)
563
+ if (!this.isOpenCamera) {
564
+ logger.info('muteLocalVideo early exit: isOpenCamera is false')
565
+ return
566
+ }
567
+ switch (streamType) {
568
+ case TRTCVideoStreamType.TRTCVideoStreamTypeBig:
569
+ case TRTCVideoStreamType.TRTCVideoStreamTypeSub:
570
+ this.isVideoMuted = mute
571
+ logger.info('muteLocalVideo: isVideoMuted set to ', mute)
572
+ try {
573
+ await this.setAttributes({
574
+ params: {
575
+ videoPreview: mute,
576
+ enableCamera: !mute,
577
+ }
578
+ }, 'muteLocalVideo')
579
+ logger.info('muteLocalVideo: setAttributes success')
580
+ } catch (error) {
581
+ logger.error('muteLocalVideo: setAttributes failed with error ', error)
582
+ }
583
+ logger.info('muteLocalVideo success')
584
+ break
585
+ default:
586
+ logger.info('muteLocalVideo: streamType not supported ', streamType)
587
+ }
588
+ }
589
+
590
+ /**
591
+ * 开始显示远端视频画面
592
+ * @category 视频
593
+ * @param userId 对方的用户标识
594
+ * @param view 此参数无效,小程序不支持
595
+ * @param streamType 视频流类型
596
+ */
597
+ async startRemoteView(
598
+ userId: string,
599
+ view: string,
600
+ streamType: TRTCVideoStreamType
601
+ ): Promise<void> {
602
+ logger.info('startRemoteView with options: ', userId, view, streamType)
603
+ const streamId = translateTRTCStreamId(userId, streamType) // trtc
604
+ this.renderMap.set(streamId, view)
605
+ await this.setAttributes({
606
+ params: {
607
+ muteVideo: false,
608
+ stopVideo: false,
609
+ },
610
+ streamId,
611
+ view
612
+ }, 'startRemoteView')
613
+ this.updateAudioRoute()
614
+ logger.info('startRemoteView success')
615
+ this.emit('onFirstVideoFrame', userId, streamType, 0, 0)
616
+ }
617
+
618
+ /**
619
+ * 停止显示远端视频画面,同时不再拉取该远端用户的视频数据流
620
+ * @param userId 对方的用户标识
621
+ * @param streamType 视频流类型
622
+ */
623
+ async stopRemoteView(userId: string, streamType: TRTCVideoStreamType): Promise<void> {
624
+ logger.info('stopRemoteView with options: ', userId, streamType)
625
+ const streamId = translateTRTCStreamId(userId, streamType)
626
+ await this.setAttributes({
627
+ params: {
628
+ muteVideo: true,
629
+ stopVideo: true,
630
+ },
631
+ streamId,
632
+ view: this.renderMap.get(streamId),
633
+ }, 'stopRemoteView')
634
+ logger.info('stopRemoteView success')
635
+ }
636
+
637
+ /**
638
+ * 修改远端视频渲染的 HTML 元素,小程序不支持
639
+ * @param userId 远端视频流的用户 ID
640
+ * @param view
641
+ * 接受远端视频流渲染的 HTML 元素,传入 null 结束远端视频的渲染
642
+ * - 传入的 HTML 元素必须时块元素,例如:div
643
+ * @param streamType
644
+ * @returns
645
+ */
646
+ async updateRemoteView(
647
+ userId: string,
648
+ view: string,
649
+ streamType: TRTCVideoStreamType
650
+ ): Promise<void> {
651
+ logger.info('updateRemoteView with options: ', userId, view, streamType)
652
+ if (view !== null) {
653
+ await this.startRemoteView(userId, view, streamType)
654
+ return
655
+ }
656
+ await this.stopRemoteView(userId, streamType)
657
+ }
658
+
659
+
660
+ // 停止显示所有远端视频画面,同时不再拉取该远端用户的视频数据流
661
+ async stopAllRemoteView(): Promise<void> {
662
+ logger.info('stopAllRemoteView')
663
+ const playerList = this.trtc.getPlayerList()
664
+ playerList.forEach(async (player) => {
665
+ const streamId = player.streamID
666
+ await this.setAttributes({
667
+ params: {
668
+ muteVideo: true,
669
+ stopVideo: true,
670
+ },
671
+ streamId,
672
+ view: this.renderMap.get(streamId),
673
+ }, 'stopAllRemoteView')
674
+ })
675
+ logger.info('stopAllRemoteView success')
676
+ }
677
+
678
+ // 暂停接收指定的远端视频流
679
+ // 该接口仅停止接收远程用户的视频流,但并不释放显示资源,所以视频画面会冻屏在 mute 前的最后一帧。
680
+ async muteRemoteVideoStream(
681
+ userId: string,
682
+ mute: boolean,
683
+ streamType: TRTCVideoStreamType
684
+ ): Promise<void> {
685
+ logger.info('muteRemoteVideoStream')
686
+ const streamId = translateTRTCStreamId(userId, streamType)
687
+ await this.setAttributes({
688
+ params: {muteVideo: mute},
689
+ streamId,
690
+ view: this.renderMap.get(streamId),
691
+ }, 'muteRemoteVideoStream')
692
+ logger.info('muteRemoteVideoStream success')
693
+ }
694
+
695
+ async muteAllRemoteVideoStreams(mute: boolean): Promise<void> {
696
+ logger.info('muteAllRemoteVideoStreams with options: ', mute)
697
+ const playerList = this.trtc.getPlayerList()
698
+ playerList.forEach(async (player) => {
699
+ const streamId = player.streamID
700
+ await this.setAttributes({
701
+ params: {muteVideo: mute},
702
+ streamId,
703
+ view: this.renderMap.get(streamId),
704
+ }, 'muteAllRemoteVideoStreams')
705
+ })
706
+ logger.info('muteAllRemoteVideoStreams success')
707
+ }
708
+
709
+ private setTRTCPlayerAttributes(streamId, options): object {
710
+ logger.info('setTRTCPlayerAttributes with options: ', streamId, options)
711
+ this.trtc.setPlayerAttributes(streamId, options)
712
+ return this.trtc.getPlayerInstance(streamId)?.playerAttributes || {}
713
+ }
714
+
715
+ async setVideoEncoderParam(params: TRTCVideoEncParam): Promise<void> {
716
+ logger.info('setVideoEncoderParam with options: ', JSON.stringify(params))
717
+ const {
718
+ videoResolution,
719
+ resMode,
720
+ videoFps,
721
+ videoBitrate,
722
+ minVideoBitrate,
723
+ } = params
724
+ const {videoWidth, videoHeight} =
725
+ translateTRTCVideoResolution(videoResolution)
726
+ await this.setAttributes({
727
+ params: {
728
+ videoWidth,
729
+ videoHeight,
730
+ // videoOrientation,
731
+ fps: videoFps,
732
+ minBitrate: minVideoBitrate,
733
+ }
734
+ }, 'setVideoEncoderParam')
735
+ logger.info('setVideoEncoderParam success')
736
+ }
737
+
738
+ async setLocalRenderParams(params: TRTCRenderParams): Promise<void> {
739
+ logger.info('setLocalRenderParams with options: ', JSON.stringify(params))
740
+ const {rotation, fillMode, mirrorType} = params
741
+ const videoOrientation = translateTRTCVideoRotation(rotation)
742
+ const localMirror = translateTRTCVideoMirrorType(mirrorType)
743
+ await this.setAttributes({
744
+ params: {
745
+ videoOrientation,
746
+ localMirror,
747
+ }
748
+ }, 'setLocalRenderParams')
749
+ logger.info('setLocalRenderParams success')
750
+ }
751
+
752
+ /**
753
+ * 设置远端图像的渲染模式
754
+ * @param {string} userId
755
+ * @param {TRTCVideoStreamType} streamType
756
+ * @param {TRTCRenderParams} params
757
+ */
758
+ async setRemoteRenderParams(
759
+ userId: string, streamType: TRTCVideoStreamType, params: TRTCRenderParams
760
+ ): Promise<void> {
761
+ logger.info('setRemoteRenderParams with options: ', userId, streamType, JSON.stringify(params))
762
+ const {fillMode, rotation, mirrorType} = params
763
+ const streamId = translateTRTCStreamId(userId, streamType) // 音频默认都是主流
764
+ await this.setAttributes({
765
+ params: {
766
+ objectFit: translateVideoFillMod(fillMode),
767
+ orientation: translateTRTCVideoRotation(rotation)
768
+ },
769
+ streamId,
770
+ view: this.renderMap.get(streamId),
771
+ }, 'setRemoteRenderParams')
772
+ logger.info('setRemoteRenderParams success')
773
+ }
774
+
775
+ async startLocalAudio(quality: TRTCAudioQuality): Promise<void> {
776
+ logger.info('startLocalAudio with options: ', quality)
777
+ if (this.role !== TRTCRoleType.TRTCRoleAnchor) {
778
+ throw new Error('Current role is audience, unable to access mic. Call switchRole to become a host.')
779
+ }
780
+ const getAudioQuality = (quality: TRTCAudioQuality) => {
781
+ if (quality === TRTCAudioQuality.TRTCAudioQualitySpeech) {
782
+ return 'low'
783
+ }
784
+ return 'high'
785
+ }
786
+
787
+ await this.setAttributes({params: {enableMic: true, audioQuality: getAudioQuality(quality)}}, 'startLocalAudio')
788
+ logger.info('startLocalAudio success')
789
+ this.emit('onMicDidReady')
790
+ if (this.enterRoom) {
791
+ this.emit('onSendFirstLocalAudioFrame')
792
+ }
793
+ }
794
+
795
+ async stopLocalAudio(): Promise<void> {
796
+ logger.info('stopLocalAudio')
797
+ await this.setAttributes({params: {enableMic: false}}, 'stopLocalAudio')
798
+ logger.info('stopLocalAudio success')
799
+ }
800
+
801
+ async muteLocalAudio(mute: boolean): Promise<void> {
802
+ logger.info('muteLocalAudio called with options: ', mute)
803
+
804
+ try {
805
+ await this.setAttributes({params: {muted: mute}}, 'muteLocalAudio')
806
+ logger.info('setAttributes executed successfully')
807
+ } catch (error) {
808
+ logger.error('Error in setAttributes: ', error)
809
+ throw error
810
+ }
811
+
812
+ try {
813
+ this.emit('onUserAudioAvailable', this.userId, mute === true ? 0 : 1)
814
+ logger.info('onUserAudioAvailable event emitted successfully')
815
+ } catch (error) {
816
+ logger.error('Error in emitting onUserAudioAvailable event: ', error)
817
+ throw error
818
+ }
819
+
820
+ logger.info('muteLocalAudio execution finished')
821
+ }
822
+
823
+ async setAudioRoute(route: TRTCAudioRoute): Promise<void> {
824
+ logger.info('setAudioRoute with options: ', route)
825
+ this.currentSoundMode = route
826
+ const soundMode = translateTRTCAudioRoute(route)
827
+ try {
828
+ await this.playerAudioRouteChange({
829
+ soundMode,
830
+ })
831
+ logger.info('setAudioRoute success')
832
+ } catch (error) {
833
+ logger.error('Error in setAttributes: ', error)
834
+ throw error
835
+ }
836
+ }
837
+
838
+ private async updateAudioRoute(): Promise<void> {
839
+ logger.info('updateAudioRoute with options: ', this.currentSoundMode)
840
+ const soundMode = translateTRTCAudioRoute(this.currentSoundMode)
841
+ try {
842
+ await this.playerAudioRouteChange({
843
+ soundMode,
844
+ })
845
+ } catch (error) {
846
+ logger.error('Error in setAttributes: ', error)
847
+ throw error
848
+ }
849
+ }
850
+
851
+ async muteRemoteAudio(userId: string, mute: boolean): Promise<void> {
852
+ logger.info('muteRemoteAudio with options: ', userId, mute)
853
+ const streamId = translateTRTCStreamId(
854
+ userId,
855
+ TRTCVideoStreamType.TRTCVideoStreamTypeBig
856
+ ) // 音频默认都是主流
857
+ await this.setAttributes({
858
+ params: {
859
+ muteAudio: mute,
860
+ },
861
+ streamId,
862
+ view: this.renderMap.get(streamId),
863
+ }, 'muteRemoteAudio')
864
+ logger.info('muteRemoteAudio success')
865
+ }
866
+
867
+ async muteAllRemoteAudio(mute: boolean): Promise<void> {
868
+ logger.info('muteAllRemoteAudio with options: ', mute)
869
+ const playerList = this.trtc.getPlayerList()
870
+ playerList.forEach(async (player) => {
871
+ const streamId = player.streamID
872
+ await this.setAttributes({
873
+ params: {
874
+ muteAudio: mute,
875
+ },
876
+ streamId,
877
+ view: this.renderMap.get(streamId),
878
+ }, 'muteRemoteAudio')
879
+ })
880
+ logger.info('muteAllRemoteAudio success')
881
+ }
882
+
883
+ enableAudioVolumeEvaluation(interval: number): void {
884
+ logger.info('enableAudioVolumeEvaluation with options: ', interval)
885
+ this.handleAudioVolumeUpdate = this.getHandleAudioVolumeUpdate(interval)
886
+ }
887
+
888
+ /**
889
+ * 切换前后摄像头
890
+ */
891
+ switchCamera(frontCamera: boolean): Promise<any> {
892
+ logger.info('switchCamera with options: ', frontCamera)
893
+ const pusher = this.trtc.getPusherInstance()
894
+ if (frontCamera === (pusher.pusherAttributes.frontCamera === 'front')) return Promise.resolve()
895
+ return new Promise((resolve, reject) => {
896
+ wx.createLivePusherContext().switchCamera({
897
+ success: async (event) => {
898
+ await this.setAttributes(
899
+ {params: {frontCamera: frontCamera ? 'front' : 'back'}},
900
+ 'switchCamera'
901
+ )
902
+ logger.info('switchCamera success')
903
+ resolve(event)
904
+ },
905
+ fail: (error) => {
906
+ logger.info('switchCamera fail')
907
+ reject(error)
908
+ },
909
+ })
910
+ })
911
+ }
912
+
913
+ async setBeautyStyle(
914
+ style: TRTCBeautyStyle,
915
+ beauty: number,
916
+ white: number,
917
+ ruddiness: number
918
+ ): Promise<void> {
919
+ logger.info('setBeautyStyle with options: ', style, beauty, white, ruddiness)
920
+ await this.setAttributes({
921
+ params: {
922
+ beautyStyle: translateBeautyStyle(style),
923
+ beautyLevel: beauty,
924
+ whitenessLevel: white,
925
+ }
926
+ }, 'setBeautyStyle')
927
+ logger.info('setBeautyStyle success')
928
+ }
929
+
930
+ sendSEIMsg(msg: string, repeatCount = 1): Promise<any> {
931
+ logger.info('sendSEIMsg with options: ', msg, repeatCount)
932
+ return new Promise((resolve, reject) => {
933
+ wx.createLivePusherContext().sendMessage({
934
+ msg,
935
+ success: (event) => {
936
+ logger.info('sendSEIMsg success')
937
+ resolve(event)
938
+ },
939
+ fail: (error) => {
940
+ logger.info('sendSEIMsg fail')
941
+ reject(error)
942
+ },
943
+ })
944
+ })
945
+ }
946
+
947
+ startPlayMusic(
948
+ musicParam: AudioMusicParam,
949
+ callbackMap: Record<'onStart' | 'onPlayProgress' | 'onComplete', () => void>
950
+ ): Promise<any> {
951
+ logger.info('startPlayMusic with options: ', JSON.stringify(musicParam))
952
+ return new Promise((resolve, reject) => {
953
+ const {path, startTimeMS = 0, endTimeMS = 0} = musicParam
954
+ wx.createLivePusherContext().playBGM({
955
+ url: path,
956
+ startTimeMs: startTimeMS,
957
+ endTimeMs: endTimeMS,
958
+ success: (event) => {
959
+ logger.info('startPlayMusic success')
960
+ resolve(event)
961
+ },
962
+ fail: (error) => {
963
+ logger.info('startPlayMusic fail')
964
+ reject(error)
965
+ },
966
+ })
967
+ })
968
+ }
969
+
970
+ stopPlayMusic(): Promise<any> {
971
+ logger.info('stopPlayMusic')
972
+ return new Promise((resolve, reject) => {
973
+ wx.createLivePusherContext().stopBGM({
974
+ success: (event) => {
975
+ logger.info('stopPlayMusic success')
976
+ resolve(event)
977
+ },
978
+ fail: (error) => {
979
+ logger.info('stopPlayMusic fail')
980
+ reject(error)
981
+ },
982
+ })
983
+ })
984
+ }
985
+
986
+ pausePlayMusic(): Promise<any> {
987
+ logger.info('pausePlayMusic')
988
+ return new Promise((resolve, reject) => {
989
+ wx.createLivePusherContext().pauseBGM({
990
+ success: (event) => {
991
+ logger.info('pausePlayMusic success')
992
+ resolve(event)
993
+ },
994
+ fail: (error) => {
995
+ logger.info('pausePlayMusic fail')
996
+ reject(error)
997
+ },
998
+ })
999
+ })
1000
+ }
1001
+
1002
+ resumePlayMusic(): Promise<any> {
1003
+ logger.info('resumePlayMusic')
1004
+ return new Promise((resolve, reject) => {
1005
+ wx.createLivePusherContext().resumeBGM({
1006
+ success: (event) => {
1007
+ logger.info('resumePlayMusic success')
1008
+ resolve(event)
1009
+ },
1010
+ fail: (error) => {
1011
+ logger.info('resumePlayMusic fail')
1012
+ reject(error)
1013
+ },
1014
+ })
1015
+ })
1016
+ }
1017
+
1018
+ // 设置背景音乐的音量大小,播放背景音乐混音时使用,用来控制背景音音量大小
1019
+ setAllMusicVolume(volume: number): Promise<any> {
1020
+ logger.info('setAllMusicVolume with options: ', volume)
1021
+ return new Promise((resolve, reject) => {
1022
+ const volumeStr = (volume / 200).toString()
1023
+ wx.createLivePusherContext().setBGMVolume({
1024
+ volume: volumeStr,
1025
+ success: (event) => {
1026
+ logger.info('setAllMusicVolume success')
1027
+ resolve(event)
1028
+ },
1029
+ fail: (error) => {
1030
+ logger.info('setAllMusicVolume fail')
1031
+ reject(error)
1032
+ },
1033
+ })
1034
+ })
1035
+ }
1036
+
1037
+ // 开启大小画面双路编码模式
1038
+ enableSmallVideoStream(enable: boolean, params: TRTCVideoEncParam) {
1039
+ logger.info('enableSmallVideoStream with options: ', enable, JSON.stringify(params))
1040
+ this.encsmall = enable ? 1 : 0
1041
+ }
1042
+
1043
+ /**
1044
+ * 调用实验性 API 接口
1045
+ *
1046
+ * 注意:该接口用于调用一些实验性功能, 目前 web 端空实现
1047
+ *
1048
+ * @param {String} jsonStr - 接口及参数描述的 JSON 字符串
1049
+ */
1050
+ callExperimentalAPI(jsonStr: string): void {
1051
+ logger.info('callExperimentalAPI with options: ', jsonStr)
1052
+ const jsonObj = safelyParse(jsonStr)
1053
+ if (jsonObj === jsonStr) {
1054
+ return
1055
+ }
1056
+ const {api, params} = jsonObj
1057
+ if (!api || !params) {
1058
+ return
1059
+ }
1060
+ switch (api) {
1061
+ case 'setFramework':
1062
+ this.handleSetFrameWork(params)
1063
+ break
1064
+ case 'enableInfiniteAnchor':
1065
+ this.enableinfiniteanchor = params?.enable ? 1 : 0
1066
+ break
1067
+ default:
1068
+ break
1069
+ }
1070
+ }
1071
+
1072
+ private handleSetFrameWork(params: any): void {
1073
+ const {component} = params
1074
+ if (isNumber(component)) {
1075
+ try {
1076
+ if (wx) {
1077
+ wx.TUIScene = component
1078
+ wx.setStorageSync('TUIScene', String(component))
1079
+ }
1080
+ } catch (err) {
1081
+ throw err
1082
+ }
1083
+ }
1084
+ }
1085
+
1086
+ private pusherAttributesChange(pusher: any): Promise<any> {
1087
+ return new Promise((resolve) => {
1088
+ this.InterfaceEventEmitter.emit(
1089
+ 'pusherAttributesChange',
1090
+ {
1091
+ pusher,
1092
+ callback: () => {
1093
+ resolve(pusher)
1094
+ },
1095
+ }
1096
+ )
1097
+ })
1098
+ }
1099
+
1100
+ private playerAttributesChange(params: any): Promise<any> {
1101
+ const {streamId, playerAttributes} = params
1102
+ return new Promise((resolve) => {
1103
+ this.InterfaceEventEmitter.emit(
1104
+ 'playerAttributesChange',
1105
+ {
1106
+ streamId,
1107
+ view: this.renderMap.get(streamId),
1108
+ playerAttributes,
1109
+ callback: () => {
1110
+ resolve(playerAttributes)
1111
+ }
1112
+ }
1113
+ )
1114
+ })
1115
+ }
1116
+
1117
+ private playerAudioRouteChange(params: any): Promise<any> {
1118
+ const {soundMode} = params
1119
+ return new Promise((resolve) => {
1120
+ this.InterfaceEventEmitter.emit(
1121
+ 'playerAudioRouteChange',
1122
+ {
1123
+ soundMode,
1124
+ callback: () => {
1125
+ resolve(soundMode)
1126
+ }
1127
+ }
1128
+ )
1129
+ })
1130
+ }
1131
+
1132
+ @setHandle
1133
+ private async setAttributes(options: any, funName: string, handleType?: Handletype): Promise<any> {
1134
+ const {params, streamId} = options
1135
+ const handleMap = {
1136
+ [Handletype.setPlayer]: async () => {
1137
+ const playerAttributes = await this.playerAttributesChange({
1138
+ playerAttributes: this.setTRTCPlayerAttributes(streamId, params),
1139
+ streamId
1140
+ })
1141
+ return playerAttributes
1142
+ },
1143
+ [Handletype.setPusher]: async () => {
1144
+ if (funName === 'enterRoom') {
1145
+ const pusher = await this.pusherAttributesChange(this.trtc.enterRoom(params))
1146
+ return pusher
1147
+ }
1148
+ const pusher = await this.pusherAttributesChange(this.trtc.setPusherAttributes(params))
1149
+ return pusher
1150
+ }
1151
+ }
1152
+ // 定义一个超时时间(以毫秒为单位)
1153
+ const timeoutDuration = 2000
1154
+ // 定义超时重试次数
1155
+ const retriesNumber = 3
1156
+ return this.retryWithTimeout(
1157
+ () => (handleMap[handleType]
1158
+ ? handleMap[handleType]()
1159
+ : Promise.reject(Object.assign(new Error('setAttributes fail'), {errorCode: -1}))),
1160
+ timeoutDuration,
1161
+ retriesNumber
1162
+ )
1163
+ }
1164
+
1165
+ private async retryWithTimeout<T>(fn: () => Promise<T>, timeout: number, retries: number): Promise<T> {
1166
+ let lastError = null
1167
+ for (let i = 0; i < retries; i++) {
1168
+ try {
1169
+ logger.info(`setAttributes ${i + 1}st retry`)
1170
+ const result = await Promise.race([
1171
+ fn(),
1172
+ new Promise<T>((resolve, reject) => setTimeout(() => reject(new Error('setAttributes timed out')), timeout)),
1173
+ ])
1174
+ return result
1175
+ } catch (error) {
1176
+ if (error?.errorCode === -1) return error
1177
+ lastError = error
1178
+ }
1179
+ }
1180
+ throw lastError
1181
+ }
1182
+ }
1183
+
1184
+ export default TRTCCloud