agora-rte-sdk 3.11.0-rc.3 → 3.11.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.
@@ -186,7 +186,7 @@ var AgoraRteStreamPlayer = exports.AgoraRteStreamPlayer = /*#__PURE__*/function
186
186
  key: "takeSnapshot",
187
187
  value: function () {
188
188
  var _takeSnapshot = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(streamId, filePath) {
189
- var _yield$this$_rtcChann, data, type;
189
+ var _yield$this$_rtcChann, data, type, imageData;
190
190
  return _regenerator["default"].wrap(function (_context) {
191
191
  while (1) switch (_context.prev = _context.next) {
192
192
  case 0:
@@ -196,13 +196,26 @@ var AgoraRteStreamPlayer = exports.AgoraRteStreamPlayer = /*#__PURE__*/function
196
196
  _yield$this$_rtcChann = _context.sent;
197
197
  data = _yield$this$_rtcChann.data;
198
198
  type = _yield$this$_rtcChann.type;
199
+ if (!(type === 'filepath')) {
200
+ _context.next = 2;
201
+ break;
202
+ }
199
203
  return _context.abrupt("return", Promise.resolve({
200
- streamId: '',
201
- filePath: '',
204
+ streamId: streamId,
205
+ filePath: data,
202
206
  width: 0,
203
207
  height: 0
204
208
  }));
205
209
  case 2:
210
+ imageData = data;
211
+ return _context.abrupt("return", Promise.resolve({
212
+ streamId: streamId,
213
+ filePath: filePath,
214
+ width: imageData.width,
215
+ height: imageData.height,
216
+ imageData: imageData
217
+ }));
218
+ case 3:
206
219
  case "end":
207
220
  return _context.stop();
208
221
  }
@@ -447,6 +447,7 @@ export type AgoraRteSnapshotInfo = {
447
447
  filePath: string;
448
448
  width: number;
449
449
  height: number;
450
+ imageData?: ImageData;
450
451
  };
451
452
  export type AgoraRteUserPageRequest = {
452
453
  userRole: number;
@@ -312,7 +312,6 @@ var AgoraWebAudioPlayer = exports.AgoraWebAudioPlayer = /*#__PURE__*/function ()
312
312
  this._analyserNode = this._audioContext.createAnalyser();
313
313
  this._analyserNode.smoothingTimeConstant = 0.3;
314
314
  this._analyserNode.fftSize = 1024;
315
- this._analyserNode.connect(this._audioContext.destination);
316
315
  if (this._audioContext.state === 'running') {
317
316
  this._initialized = true;
318
317
  }
@@ -269,7 +269,7 @@ var AgoraElectronMediaRecorderManager = exports.AgoraElectronMediaRecorderManage
269
269
  recordingHeight = (_currentWindowInfo$bo2 = currentWindowInfo.bounds) === null || _currentWindowInfo$bo2 === void 0 ? void 0 : _currentWindowInfo$bo2.height;
270
270
  this.logger.info("startClientRecording: using window bounds for recording resolution, width=".concat(recordingWidth, ", height=").concat(recordingHeight));
271
271
  }
272
- this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, true);
272
+ this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, false);
273
273
  this.logger.info("startClientRecording: started own window capture for recording (without highlight), windowId: ".concat(currentWindowInfo.id));
274
274
  } else {
275
275
  this.logger.warn('startClientRecording: failed to find current window for recording');
@@ -362,7 +362,7 @@ var AgoraElectronMediaRecorderManager = exports.AgoraElectronMediaRecorderManage
362
362
  key: "resumeOwnCapture",
363
363
  value: (function () {
364
364
  var _resumeOwnCapture = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3() {
365
- var currentWindowInfo, maxRetries, retryInterval, i, res;
365
+ var currentWindowInfo, maxRetries, retryInterval, i, maxStartAttempts, startRetryInterval, res, attempt;
366
366
  return _regenerator["default"].wrap(function (_context3) {
367
367
  while (1) switch (_context3.prev = _context3.next) {
368
368
  case 0:
@@ -411,44 +411,80 @@ var AgoraElectronMediaRecorderManager = exports.AgoraElectronMediaRecorderManage
411
411
  _context3.next = 3;
412
412
  break;
413
413
  case 7:
414
- if (currentWindowInfo) {
415
- // 保护这个 sourceId 不被 onLocalVideoStateChanged 的延迟回调误杀
416
- this._sourceManager.protectSourceId(currentWindowInfo.id);
417
-
418
- // 启动自己的窗口采集
419
- this._clientRecordingSourceId = currentWindowInfo.id;
420
- this._clientRecordingCaptureType = _type.AgoraRtcScreenCaptureType.WINDOW;
421
- this._clientRecordingOwnsCapture = true;
422
- res = this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, true);
423
- this.logger.info("resumeOwnCapture: startScreenCaptureByWindowId result=".concat(res, ", windowId: ").concat(currentWindowInfo.id));
424
- if (res !== 0) {
425
- this.logger.warn("resumeOwnCapture: startScreenCaptureByWindowId failed with code ".concat(res));
426
- this._sourceManager.unprotectSourceId(currentWindowInfo.id);
427
- this._clientRecordingSourceId = undefined;
428
- this._clientRecordingCaptureType = undefined;
429
- this._clientRecordingOwnsCapture = false;
430
- } else if (this._channelId && this._uid) {
431
- // 采集成功后重建 MediaRecorder,因为屏幕共享停止时 SDK 内部管道已断开,
432
- // 旧的 MediaRecorder 无法接收新采集源的视频帧。
433
- this.logger.info('resumeOwnCapture: recreating MediaRecorder for new capture source');
434
- if (this._mediaRecorder && this._isRecordingStarted) {
435
- this._mediaRecorder.stopRecording();
436
- this._isRecordingStarted = false;
437
- }
438
- if (this._mediaRecorder) {
439
- this._recorderGeneration++;
440
- this._rtcEngine.destroyMediaRecorder(this._mediaRecorder);
441
- this._mediaRecorder = undefined;
442
- this._mediaRecorderObserver = undefined;
443
- }
444
- this._createMediaRecorder(this._channelId, this._uid);
445
- this._setupMediaRecorderObserver(this._channelId, this._uid);
446
- this._startRecording(this._lastRecordingConfig);
447
- }
448
- } else {
449
- this.logger.warn('resumeOwnCapture: failed to find current window after retries, recording may be affected');
414
+ if (!currentWindowInfo) {
415
+ _context3.next = 12;
416
+ break;
450
417
  }
418
+ // 保护这个 sourceId 不被 onLocalVideoStateChanged 的延迟回调误杀
419
+ this._sourceManager.protectSourceId(currentWindowInfo.id);
420
+
421
+ // 启动自己的窗口采集
422
+ this._clientRecordingSourceId = currentWindowInfo.id;
423
+ this._clientRecordingCaptureType = _type.AgoraRtcScreenCaptureType.WINDOW;
424
+ this._clientRecordingOwnsCapture = true;
425
+
426
+ // 屏幕共享停止后 RTC 内部的 VideoSourceScreenPrimary 通道还在异步拆除,
427
+ // 此时立刻调用 startScreenCaptureByWindowId 会返回 -2 (ERR_INVALID_ARGUMENT),
428
+ // 导致后续录制全是黑帧。这里做有界重试。
429
+ maxStartAttempts = 6;
430
+ startRetryInterval = 200;
431
+ res = -1;
432
+ attempt = 0;
451
433
  case 8:
434
+ if (!(attempt < maxStartAttempts)) {
435
+ _context3.next = 11;
436
+ break;
437
+ }
438
+ res = this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, false);
439
+ this.logger.info("resumeOwnCapture: startScreenCaptureByWindowId result=".concat(res, ", windowId: ").concat(currentWindowInfo.id, ", attempt: ").concat(attempt + 1, "/").concat(maxStartAttempts));
440
+ if (!(res === 0)) {
441
+ _context3.next = 9;
442
+ break;
443
+ }
444
+ return _context3.abrupt("continue", 11);
445
+ case 9:
446
+ this.logger.warn("resumeOwnCapture: startScreenCaptureByWindowId attempt ".concat(attempt + 1, " failed with code ").concat(res, ", retrying in ").concat(startRetryInterval, "ms..."));
447
+ _context3.next = 10;
448
+ return new Promise(function (r) {
449
+ return setTimeout(r, startRetryInterval);
450
+ });
451
+ case 10:
452
+ attempt++;
453
+ _context3.next = 8;
454
+ break;
455
+ case 11:
456
+ if (res !== 0) {
457
+ this.logger.warn("resumeOwnCapture: startScreenCaptureByWindowId failed with code ".concat(res, " after ").concat(maxStartAttempts, " attempts"));
458
+ this._sourceManager.unprotectSourceId(currentWindowInfo.id);
459
+ this._clientRecordingSourceId = undefined;
460
+ this._clientRecordingCaptureType = undefined;
461
+ this._clientRecordingOwnsCapture = false;
462
+ }
463
+ // else if (this._channelId && this._uid) {
464
+ // // 采集成功后重建 MediaRecorder,因为屏幕共享停止时 SDK 内部管道已断开,
465
+ // // 旧的 MediaRecorder 无法接收新采集源的视频帧。
466
+ // this.logger.info('resumeOwnCapture: recreating MediaRecorder for new capture source');
467
+
468
+ // if (this._mediaRecorder && this._isRecordingStarted) {
469
+ // this._mediaRecorder.stopRecording();
470
+ // this._isRecordingStarted = false;
471
+ // }
472
+ // if (this._mediaRecorder) {
473
+ // this._recorderGeneration++;
474
+ // this._rtcEngine.destroyMediaRecorder(this._mediaRecorder);
475
+ // this._mediaRecorder = undefined;
476
+ // this._mediaRecorderObserver = undefined;
477
+ // }
478
+
479
+ // this._createMediaRecorder(this._channelId, this._uid);
480
+ // this._setupMediaRecorderObserver(this._channelId, this._uid);
481
+ // this._startRecording(this._lastRecordingConfig);
482
+ // }
483
+ _context3.next = 13;
484
+ break;
485
+ case 12:
486
+ this.logger.warn('resumeOwnCapture: failed to find current window after retries, recording may be affected');
487
+ case 13:
452
488
  case "end":
453
489
  return _context3.stop();
454
490
  }
@@ -588,7 +624,10 @@ var AgoraElectronMediaRecorderManager = exports.AgoraElectronMediaRecorderManage
588
624
  videoSourceType: 2,
589
625
  width: config === null || config === void 0 ? void 0 : config.width,
590
626
  height: config === null || config === void 0 ? void 0 : config.height,
591
- fps: config === null || config === void 0 ? void 0 : config.fps
627
+ fps: config === null || config === void 0 ? void 0 : config.fps,
628
+ audioConfiguration: {
629
+ audioRecordingType: 4
630
+ }
592
631
  };
593
632
  var result = (_this$_mediaRecorder2 = this._mediaRecorder) === null || _this$_mediaRecorder2 === void 0 ? void 0 : _this$_mediaRecorder2.startRecording(recordingConfig);
594
633
  if (result !== undefined && result !== 0) {
@@ -397,9 +397,10 @@ var AgoraRtcChannelWebAdapterImp = exports.AgoraRtcChannelWebAdapterImp = /*#__P
397
397
  }, {
398
398
  key: "takeSnapshot",
399
399
  value: function takeSnapshot(streamId, filePath) {
400
+ var imageData = this.subscriber.takeSnapshot(streamId);
400
401
  return Promise.resolve({
401
402
  type: 'imageData',
402
- data: this.subscriber.takeSnapshot(streamId)
403
+ data: imageData
403
404
  });
404
405
  }
405
406
  }, {
@@ -179,7 +179,7 @@ export var AgoraRteStreamPlayer = /*#__PURE__*/function () {
179
179
  key: "takeSnapshot",
180
180
  value: function () {
181
181
  var _takeSnapshot = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(streamId, filePath) {
182
- var _yield$this$_rtcChann, data, type;
182
+ var _yield$this$_rtcChann, data, type, imageData;
183
183
  return _regeneratorRuntime.wrap(function (_context) {
184
184
  while (1) switch (_context.prev = _context.next) {
185
185
  case 0:
@@ -189,13 +189,26 @@ export var AgoraRteStreamPlayer = /*#__PURE__*/function () {
189
189
  _yield$this$_rtcChann = _context.sent;
190
190
  data = _yield$this$_rtcChann.data;
191
191
  type = _yield$this$_rtcChann.type;
192
+ if (!(type === 'filepath')) {
193
+ _context.next = 2;
194
+ break;
195
+ }
192
196
  return _context.abrupt("return", Promise.resolve({
193
- streamId: '',
194
- filePath: '',
197
+ streamId: streamId,
198
+ filePath: data,
195
199
  width: 0,
196
200
  height: 0
197
201
  }));
198
202
  case 2:
203
+ imageData = data;
204
+ return _context.abrupt("return", Promise.resolve({
205
+ streamId: streamId,
206
+ filePath: filePath,
207
+ width: imageData.width,
208
+ height: imageData.height,
209
+ imageData: imageData
210
+ }));
211
+ case 3:
199
212
  case "end":
200
213
  return _context.stop();
201
214
  }
@@ -305,7 +305,6 @@ export var AgoraWebAudioPlayer = /*#__PURE__*/function () {
305
305
  this._analyserNode = this._audioContext.createAnalyser();
306
306
  this._analyserNode.smoothingTimeConstant = 0.3;
307
307
  this._analyserNode.fftSize = 1024;
308
- this._analyserNode.connect(this._audioContext.destination);
309
308
  if (this._audioContext.state === 'running') {
310
309
  this._initialized = true;
311
310
  }
@@ -263,7 +263,7 @@ export var AgoraElectronMediaRecorderManager = /*#__PURE__*/function () {
263
263
  recordingHeight = (_currentWindowInfo$bo2 = currentWindowInfo.bounds) === null || _currentWindowInfo$bo2 === void 0 ? void 0 : _currentWindowInfo$bo2.height;
264
264
  this.logger.info("startClientRecording: using window bounds for recording resolution, width=".concat(recordingWidth, ", height=").concat(recordingHeight));
265
265
  }
266
- this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, true);
266
+ this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, false);
267
267
  this.logger.info("startClientRecording: started own window capture for recording (without highlight), windowId: ".concat(currentWindowInfo.id));
268
268
  } else {
269
269
  this.logger.warn('startClientRecording: failed to find current window for recording');
@@ -356,7 +356,7 @@ export var AgoraElectronMediaRecorderManager = /*#__PURE__*/function () {
356
356
  key: "resumeOwnCapture",
357
357
  value: (function () {
358
358
  var _resumeOwnCapture = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
359
- var currentWindowInfo, maxRetries, retryInterval, i, res;
359
+ var currentWindowInfo, maxRetries, retryInterval, i, maxStartAttempts, startRetryInterval, res, attempt;
360
360
  return _regeneratorRuntime.wrap(function (_context3) {
361
361
  while (1) switch (_context3.prev = _context3.next) {
362
362
  case 0:
@@ -405,44 +405,80 @@ export var AgoraElectronMediaRecorderManager = /*#__PURE__*/function () {
405
405
  _context3.next = 3;
406
406
  break;
407
407
  case 7:
408
- if (currentWindowInfo) {
409
- // 保护这个 sourceId 不被 onLocalVideoStateChanged 的延迟回调误杀
410
- this._sourceManager.protectSourceId(currentWindowInfo.id);
411
-
412
- // 启动自己的窗口采集
413
- this._clientRecordingSourceId = currentWindowInfo.id;
414
- this._clientRecordingCaptureType = AgoraRtcScreenCaptureType.WINDOW;
415
- this._clientRecordingOwnsCapture = true;
416
- res = this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, true);
417
- this.logger.info("resumeOwnCapture: startScreenCaptureByWindowId result=".concat(res, ", windowId: ").concat(currentWindowInfo.id));
418
- if (res !== 0) {
419
- this.logger.warn("resumeOwnCapture: startScreenCaptureByWindowId failed with code ".concat(res));
420
- this._sourceManager.unprotectSourceId(currentWindowInfo.id);
421
- this._clientRecordingSourceId = undefined;
422
- this._clientRecordingCaptureType = undefined;
423
- this._clientRecordingOwnsCapture = false;
424
- } else if (this._channelId && this._uid) {
425
- // 采集成功后重建 MediaRecorder,因为屏幕共享停止时 SDK 内部管道已断开,
426
- // 旧的 MediaRecorder 无法接收新采集源的视频帧。
427
- this.logger.info('resumeOwnCapture: recreating MediaRecorder for new capture source');
428
- if (this._mediaRecorder && this._isRecordingStarted) {
429
- this._mediaRecorder.stopRecording();
430
- this._isRecordingStarted = false;
431
- }
432
- if (this._mediaRecorder) {
433
- this._recorderGeneration++;
434
- this._rtcEngine.destroyMediaRecorder(this._mediaRecorder);
435
- this._mediaRecorder = undefined;
436
- this._mediaRecorderObserver = undefined;
437
- }
438
- this._createMediaRecorder(this._channelId, this._uid);
439
- this._setupMediaRecorderObserver(this._channelId, this._uid);
440
- this._startRecording(this._lastRecordingConfig);
441
- }
442
- } else {
443
- this.logger.warn('resumeOwnCapture: failed to find current window after retries, recording may be affected');
408
+ if (!currentWindowInfo) {
409
+ _context3.next = 12;
410
+ break;
444
411
  }
412
+ // 保护这个 sourceId 不被 onLocalVideoStateChanged 的延迟回调误杀
413
+ this._sourceManager.protectSourceId(currentWindowInfo.id);
414
+
415
+ // 启动自己的窗口采集
416
+ this._clientRecordingSourceId = currentWindowInfo.id;
417
+ this._clientRecordingCaptureType = AgoraRtcScreenCaptureType.WINDOW;
418
+ this._clientRecordingOwnsCapture = true;
419
+
420
+ // 屏幕共享停止后 RTC 内部的 VideoSourceScreenPrimary 通道还在异步拆除,
421
+ // 此时立刻调用 startScreenCaptureByWindowId 会返回 -2 (ERR_INVALID_ARGUMENT),
422
+ // 导致后续录制全是黑帧。这里做有界重试。
423
+ maxStartAttempts = 6;
424
+ startRetryInterval = 200;
425
+ res = -1;
426
+ attempt = 0;
445
427
  case 8:
428
+ if (!(attempt < maxStartAttempts)) {
429
+ _context3.next = 11;
430
+ break;
431
+ }
432
+ res = this._sourceManager.startScreenCaptureByWindowId(currentWindowInfo.id, false);
433
+ this.logger.info("resumeOwnCapture: startScreenCaptureByWindowId result=".concat(res, ", windowId: ").concat(currentWindowInfo.id, ", attempt: ").concat(attempt + 1, "/").concat(maxStartAttempts));
434
+ if (!(res === 0)) {
435
+ _context3.next = 9;
436
+ break;
437
+ }
438
+ return _context3.abrupt("continue", 11);
439
+ case 9:
440
+ this.logger.warn("resumeOwnCapture: startScreenCaptureByWindowId attempt ".concat(attempt + 1, " failed with code ").concat(res, ", retrying in ").concat(startRetryInterval, "ms..."));
441
+ _context3.next = 10;
442
+ return new Promise(function (r) {
443
+ return setTimeout(r, startRetryInterval);
444
+ });
445
+ case 10:
446
+ attempt++;
447
+ _context3.next = 8;
448
+ break;
449
+ case 11:
450
+ if (res !== 0) {
451
+ this.logger.warn("resumeOwnCapture: startScreenCaptureByWindowId failed with code ".concat(res, " after ").concat(maxStartAttempts, " attempts"));
452
+ this._sourceManager.unprotectSourceId(currentWindowInfo.id);
453
+ this._clientRecordingSourceId = undefined;
454
+ this._clientRecordingCaptureType = undefined;
455
+ this._clientRecordingOwnsCapture = false;
456
+ }
457
+ // else if (this._channelId && this._uid) {
458
+ // // 采集成功后重建 MediaRecorder,因为屏幕共享停止时 SDK 内部管道已断开,
459
+ // // 旧的 MediaRecorder 无法接收新采集源的视频帧。
460
+ // this.logger.info('resumeOwnCapture: recreating MediaRecorder for new capture source');
461
+
462
+ // if (this._mediaRecorder && this._isRecordingStarted) {
463
+ // this._mediaRecorder.stopRecording();
464
+ // this._isRecordingStarted = false;
465
+ // }
466
+ // if (this._mediaRecorder) {
467
+ // this._recorderGeneration++;
468
+ // this._rtcEngine.destroyMediaRecorder(this._mediaRecorder);
469
+ // this._mediaRecorder = undefined;
470
+ // this._mediaRecorderObserver = undefined;
471
+ // }
472
+
473
+ // this._createMediaRecorder(this._channelId, this._uid);
474
+ // this._setupMediaRecorderObserver(this._channelId, this._uid);
475
+ // this._startRecording(this._lastRecordingConfig);
476
+ // }
477
+ _context3.next = 13;
478
+ break;
479
+ case 12:
480
+ this.logger.warn('resumeOwnCapture: failed to find current window after retries, recording may be affected');
481
+ case 13:
446
482
  case "end":
447
483
  return _context3.stop();
448
484
  }
@@ -582,7 +618,10 @@ export var AgoraElectronMediaRecorderManager = /*#__PURE__*/function () {
582
618
  videoSourceType: 2,
583
619
  width: config === null || config === void 0 ? void 0 : config.width,
584
620
  height: config === null || config === void 0 ? void 0 : config.height,
585
- fps: config === null || config === void 0 ? void 0 : config.fps
621
+ fps: config === null || config === void 0 ? void 0 : config.fps,
622
+ audioConfiguration: {
623
+ audioRecordingType: 4
624
+ }
586
625
  };
587
626
  var result = (_this$_mediaRecorder2 = this._mediaRecorder) === null || _this$_mediaRecorder2 === void 0 ? void 0 : _this$_mediaRecorder2.startRecording(recordingConfig);
588
627
  if (result !== undefined && result !== 0) {
@@ -390,9 +390,10 @@ export var AgoraRtcChannelWebAdapterImp = /*#__PURE__*/function (_AgoraRtcChanne
390
390
  }, {
391
391
  key: "takeSnapshot",
392
392
  value: function takeSnapshot(streamId, filePath) {
393
+ var imageData = this.subscriber.takeSnapshot(streamId);
393
394
  return Promise.resolve({
394
395
  type: 'imageData',
395
- data: this.subscriber.takeSnapshot(streamId)
396
+ data: imageData
396
397
  });
397
398
  }
398
399
  }, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-rte-sdk",
3
- "version": "3.11.0-rc.3",
3
+ "version": "3.11.0",
4
4
  "description": "SDK for building interactive scenarios",
5
5
  "author": "agora.io",
6
6
  "license": "ISC",
@@ -30,7 +30,7 @@
30
30
  "@types/sinon": "^17.0.2",
31
31
  "@types/ua-parser-js": "^0.7.35",
32
32
  "agora-token": "^2.0.3",
33
- "agora-toolchain": "3.11.0-rc.3",
33
+ "agora-toolchain": "3.11.0",
34
34
  "core-js": "^3.33.3",
35
35
  "electron": "22.3.27",
36
36
  "husky": "^9.0.11",
@@ -41,11 +41,11 @@
41
41
  "typescript": "^5.3.2"
42
42
  },
43
43
  "dependencies": {
44
- "shengwang-electron-sdk": "4.6.3-build.133-rc.14",
44
+ "shengwang-electron-sdk": "4.6.3-build.133-rc.16",
45
45
  "agora-extension-ai-denoiser": "^1.0.0",
46
46
  "agora-extension-beauty-effect": "^1.0.1-beta",
47
47
  "agora-extension-virtual-background": "^2.1.0",
48
- "agora-foundation": "3.11.0-rc.3",
48
+ "agora-foundation": "3.11.0",
49
49
  "agora-rtc-sdk-ng": "4.23.3",
50
50
  "agora-rtm": "2.2.2-3",
51
51
  "await-to-js": "^3.0.0",