@tencentcloud/trtc-cloud-wx 0.0.25 → 0.0.27

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