@rongcloud/plugin-rtc 5.4.6-alpha.1 → 5.4.7-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/command/LocalTrackMuteCommand.d.ts +9 -1
- package/dist/core/enums/RCLoggerTag.d.ts +4 -4
- package/dist/core/enums/RCRTCCode.d.ts +3 -1
- package/dist/core/enums/RCRTCResolution.d.ts +34 -4
- package/dist/core/enums/RCResolution.d.ts +6 -1
- package/dist/core/tracks/RCLocalTrack.d.ts +10 -2
- package/dist/core/tracks/RCTrack.d.ts +3 -3
- package/dist/core/webrtc/sdp/ASdpBuilder.d.ts +1 -0
- package/dist/core/webrtc/sdp/SDPUtils.d.ts +60 -0
- package/dist/index.d.ts +25 -10
- package/dist/index.esm.js +308 -80
- package/dist/index.js +308 -80
- package/dist/index.umd.js +308 -80
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* RCRTC - v5.4.
|
|
3
|
-
* CommitId -
|
|
4
|
-
*
|
|
2
|
+
* RCRTC - v5.4.7-alpha.2
|
|
3
|
+
* CommitId - 1826738708cea2e2c6cc7586713f2c24ab40e665
|
|
4
|
+
* Wed Aug 31 2022 18:33:00 GMT+0800 (China Standard Time)
|
|
5
5
|
* ©2020 RongCloud, Inc. All rights reserved.
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
10
10
|
|
|
11
11
|
var engine = require('@rongcloud/engine');
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
/*! *****************************************************************************
|
|
14
14
|
Copyright (c) Microsoft Corporation.
|
|
15
15
|
|
|
16
16
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
@@ -124,6 +124,8 @@ exports.RCRTCCode = void 0;
|
|
|
124
124
|
RCRTCCode[RCRTCCode["MAC_IN_ELECTRON_NOT_SUPPORT_SCREEN_SHARE_WITH_AUDIO"] = 53032] = "MAC_IN_ELECTRON_NOT_SUPPORT_SCREEN_SHARE_WITH_AUDIO";
|
|
125
125
|
/** 获取媒体资源时,无系统权限 */
|
|
126
126
|
RCRTCCode[RCRTCCode["SYSTEM_PERMISSION_DENIED"] = 53033] = "SYSTEM_PERMISSION_DENIED";
|
|
127
|
+
/** 发布时无有效资源,如 track 已被销毁 */
|
|
128
|
+
RCRTCCode[RCRTCCode["PUBLISH_TRACKS_IS_INVALID"] = 53034] = "PUBLISH_TRACKS_IS_INVALID";
|
|
127
129
|
})(exports.RCRTCCode || (exports.RCRTCCode = {}));
|
|
128
130
|
|
|
129
131
|
var RCLoggerTag;
|
|
@@ -297,19 +299,19 @@ var RCLoggerTag;
|
|
|
297
299
|
*/
|
|
298
300
|
RCLoggerTag["L_ABSTRACT_ROOM_DELETE_ROOM_ATTRIBUTE_R"] = "L-abstract_room_delete_room_attribute_R";
|
|
299
301
|
/**
|
|
300
|
-
*
|
|
302
|
+
* 房间发布资源的任务
|
|
301
303
|
*/
|
|
302
304
|
RCLoggerTag["L_ABSTRACT_ROOM_PUBLISH_T"] = "L-abstract_room_publish-T";
|
|
303
305
|
/**
|
|
304
|
-
*
|
|
306
|
+
* 房间发布资源结果
|
|
305
307
|
*/
|
|
306
308
|
RCLoggerTag["L_ABSTRACT_ROOM_PUBLISH_R"] = "L-abstract_room_publish-R";
|
|
307
309
|
/**
|
|
308
|
-
*
|
|
310
|
+
* 房间取消发布资源的任务
|
|
309
311
|
*/
|
|
310
312
|
RCLoggerTag["L_ABSTRACT_ROOM_UNPUBLISH_T"] = "L-abstract_room_unpublish-T";
|
|
311
313
|
/**
|
|
312
|
-
*
|
|
314
|
+
* 房间取消发布资源结果
|
|
313
315
|
*/
|
|
314
316
|
RCLoggerTag["L_ABSTRACT_ROOM_UNPUBLISH_R"] = "L-abstract_room_unpublish-R";
|
|
315
317
|
/**
|
|
@@ -6198,6 +6200,11 @@ exports.RCResolution = void 0;
|
|
|
6198
6200
|
RCResolution["W720_H480"] = "W720_H480";
|
|
6199
6201
|
RCResolution["W1280_H720"] = "W1280_H720";
|
|
6200
6202
|
RCResolution["W1920_H1080"] = "W1920_H1080";
|
|
6203
|
+
RCResolution["W180_H180"] = "W180_H180";
|
|
6204
|
+
RCResolution["W240_H180"] = "W240_H180";
|
|
6205
|
+
RCResolution["W360_H360"] = "W360_H360";
|
|
6206
|
+
RCResolution["W848_H480"] = "W848_H480";
|
|
6207
|
+
RCResolution["W960_H720"] = "W960_H720";
|
|
6201
6208
|
})(exports.RCResolution || (exports.RCResolution = {}));
|
|
6202
6209
|
|
|
6203
6210
|
/**
|
|
@@ -6222,25 +6229,46 @@ exports.RCMediaType = void 0;
|
|
|
6222
6229
|
// export const RongRTCVideoBitrate: { [key: RCResolution]: BitrateConf } = {
|
|
6223
6230
|
const RongRTCVideoBitrate = {
|
|
6224
6231
|
[exports.RCResolution.W176_H132]: { width: 176, height: 132, maxBitrate: 150, minBitrate: 80 },
|
|
6225
|
-
[exports.RCResolution.W176_H144]: { width: 176, height: 144, maxBitrate:
|
|
6232
|
+
[exports.RCResolution.W176_H144]: { width: 176, height: 144, maxBitrate: 150, minBitrate: 80 },
|
|
6233
|
+
[exports.RCResolution.W180_H180]: { width: 180, height: 180, maxBitrate: 200, minBitrate: 100 },
|
|
6234
|
+
[exports.RCResolution.W240_H180]: { width: 240, height: 180, maxBitrate: 240, minBitrate: 120 },
|
|
6235
|
+
[exports.RCResolution.W240_H240]: { width: 240, height: 240, maxBitrate: 280, minBitrate: 120 },
|
|
6226
6236
|
[exports.RCResolution.W256_H144]: { width: 256, height: 144, maxBitrate: 240, minBitrate: 120 },
|
|
6227
6237
|
[exports.RCResolution.W320_H180]: { width: 320, height: 180, maxBitrate: 280, minBitrate: 120 },
|
|
6228
|
-
[exports.RCResolution.W240_H240]: { width: 240, height: 240, maxBitrate: 280, minBitrate: 120 },
|
|
6229
6238
|
[exports.RCResolution.W320_H240]: { width: 320, height: 240, maxBitrate: 400, minBitrate: 120 },
|
|
6239
|
+
[exports.RCResolution.W360_H360]: { width: 360, height: 360, maxBitrate: 520, minBitrate: 140 },
|
|
6230
6240
|
[exports.RCResolution.W480_H360]: { width: 480, height: 360, maxBitrate: 650, minBitrate: 150 },
|
|
6231
|
-
[exports.RCResolution.W640_H360]: { width: 640, height: 360, maxBitrate: 800, minBitrate: 180 },
|
|
6232
6241
|
[exports.RCResolution.W480_H480]: { width: 480, height: 480, maxBitrate: 800, minBitrate: 180 },
|
|
6242
|
+
[exports.RCResolution.W640_H360]: { width: 640, height: 360, maxBitrate: 800, minBitrate: 180 },
|
|
6233
6243
|
[exports.RCResolution.W640_H480]: { width: 640, height: 480, maxBitrate: 900, minBitrate: 200 },
|
|
6234
6244
|
[exports.RCResolution.W720_H480]: { width: 720, height: 480, maxBitrate: 1000, minBitrate: 200 },
|
|
6245
|
+
[exports.RCResolution.W848_H480]: { width: 848, height: 480, maxBitrate: 1860, minBitrate: 200 },
|
|
6246
|
+
[exports.RCResolution.W960_H720]: { width: 960, height: 720, maxBitrate: 2000, minBitrate: 250 },
|
|
6235
6247
|
[exports.RCResolution.W1280_H720]: { width: 1280, height: 720, maxBitrate: 2200, minBitrate: 250 },
|
|
6236
6248
|
[exports.RCResolution.W1920_H1080]: { width: 1920, height: 1080, maxBitrate: 4000, minBitrate: 400 }
|
|
6237
6249
|
};
|
|
6250
|
+
/**
|
|
6251
|
+
* 分辨率适配
|
|
6252
|
+
* @param {number} resolution
|
|
6253
|
+
* @returns
|
|
6254
|
+
*/
|
|
6255
|
+
const resolutionAdapter = (resolution) => {
|
|
6256
|
+
const ResolutionAdapter = {
|
|
6257
|
+
192: 180,
|
|
6258
|
+
368: 360,
|
|
6259
|
+
1088: 1080
|
|
6260
|
+
};
|
|
6261
|
+
return ResolutionAdapter[resolution] ? ResolutionAdapter[resolution] : resolution;
|
|
6262
|
+
};
|
|
6238
6263
|
/**
|
|
6239
6264
|
* 向上取最接近的视频分辨率配置
|
|
6240
6265
|
* @param {number} width
|
|
6241
6266
|
* @param {number} height
|
|
6242
6267
|
*/
|
|
6243
6268
|
const getNearestResolution = (width, height) => {
|
|
6269
|
+
// 分辨率适配
|
|
6270
|
+
width = resolutionAdapter(width);
|
|
6271
|
+
height = resolutionAdapter(height);
|
|
6244
6272
|
// 优先精准匹配
|
|
6245
6273
|
const conf = RongRTCVideoBitrate[`W${width}_H${height}`];
|
|
6246
6274
|
if (conf) {
|
|
@@ -6406,7 +6434,7 @@ class RCTrack extends engine.EventEmitter {
|
|
|
6406
6434
|
}, {
|
|
6407
6435
|
logSource: engine.LogSource.RTC
|
|
6408
6436
|
});
|
|
6409
|
-
this._setLocalMuted(true);
|
|
6437
|
+
return this._setLocalMuted(true);
|
|
6410
6438
|
}
|
|
6411
6439
|
/**
|
|
6412
6440
|
* 启用
|
|
@@ -6419,7 +6447,7 @@ class RCTrack extends engine.EventEmitter {
|
|
|
6419
6447
|
}, {
|
|
6420
6448
|
logSource: engine.LogSource.RTC
|
|
6421
6449
|
});
|
|
6422
|
-
this._setLocalMuted(false);
|
|
6450
|
+
return this._setLocalMuted(false);
|
|
6423
6451
|
}
|
|
6424
6452
|
/**
|
|
6425
6453
|
* 本端是否已禁用该轨道数据
|
|
@@ -6614,7 +6642,10 @@ class RCTrack extends engine.EventEmitter {
|
|
|
6614
6642
|
logSource: engine.LogSource.RTC
|
|
6615
6643
|
});
|
|
6616
6644
|
try {
|
|
6617
|
-
(_c = this._element) === null || _c === void 0 ? void 0 : _c.play();
|
|
6645
|
+
yield ((_c = this._element) === null || _c === void 0 ? void 0 : _c.play());
|
|
6646
|
+
if ((options === null || options === void 0 ? void 0 : options.audioDeviceId) && !isVideoTrack) {
|
|
6647
|
+
yield this._element.setSinkId(options.audioDeviceId);
|
|
6648
|
+
}
|
|
6618
6649
|
}
|
|
6619
6650
|
catch (error) {
|
|
6620
6651
|
/**
|
|
@@ -6661,9 +6692,9 @@ class RCTrack extends engine.EventEmitter {
|
|
|
6661
6692
|
this._element.x5PlaysInline = true;
|
|
6662
6693
|
this._element.webkitPlaysInline = true;
|
|
6663
6694
|
}
|
|
6664
|
-
if (
|
|
6665
|
-
|
|
6666
|
-
}
|
|
6695
|
+
// if (options?.audioDeviceId && !isVideoTrack) {
|
|
6696
|
+
// await (this._element as any).setSinkId(options.audioDeviceId)
|
|
6697
|
+
// }
|
|
6667
6698
|
// audio 标签设置音量
|
|
6668
6699
|
if (!isVideoTrack && ((options === null || options === void 0 ? void 0 : options.volume) || (options === null || options === void 0 ? void 0 : options.volume) === 0)) {
|
|
6669
6700
|
this._element.volume = (options === null || options === void 0 ? void 0 : options.volume) / 100;
|
|
@@ -6716,9 +6747,9 @@ exports.RCAudioBitrate = void 0;
|
|
|
6716
6747
|
RCAudioBitrate["MusicHigh"] = "MusicHigh";
|
|
6717
6748
|
})(exports.RCAudioBitrate || (exports.RCAudioBitrate = {}));
|
|
6718
6749
|
const getAudioBitrate = {
|
|
6719
|
-
[exports.RCAudioBitrate.Music]: { max:
|
|
6720
|
-
[exports.RCAudioBitrate.MusicHigh]: { max:
|
|
6721
|
-
[exports.RCAudioBitrate.Speech]: { max:
|
|
6750
|
+
[exports.RCAudioBitrate.Music]: { max: 32 * 2, min: 32 * 2 },
|
|
6751
|
+
[exports.RCAudioBitrate.MusicHigh]: { max: 32 * 2 * 2, min: 32 * 2 * 2 },
|
|
6752
|
+
[exports.RCAudioBitrate.Speech]: { max: 32, min: 32 }
|
|
6722
6753
|
};
|
|
6723
6754
|
exports.RCVideoBitrate = void 0;
|
|
6724
6755
|
(function (RCVideoBitrate) {
|
|
@@ -6882,6 +6913,10 @@ RC3AnoiseTrack.isOpen = false;
|
|
|
6882
6913
|
class RCLocalTrack extends RCTrack {
|
|
6883
6914
|
constructor(tag, userId, kind, track) {
|
|
6884
6915
|
super(tag, userId, kind, true);
|
|
6916
|
+
/**
|
|
6917
|
+
* track 是否被销毁
|
|
6918
|
+
*/
|
|
6919
|
+
this._isDestroyed = false;
|
|
6885
6920
|
this._isPublished = false;
|
|
6886
6921
|
this.__innerSetMediaStreamTrack(track);
|
|
6887
6922
|
// 监听流结束事件
|
|
@@ -6896,17 +6931,22 @@ class RCLocalTrack extends RCTrack {
|
|
|
6896
6931
|
* @param bool
|
|
6897
6932
|
*/
|
|
6898
6933
|
_setLocalMuted(bool) {
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6934
|
+
return new Promise((resolve, reject) => {
|
|
6935
|
+
const changed = this._localMuted !== bool;
|
|
6936
|
+
super._setLocalMuted(bool);
|
|
6937
|
+
// 本端流,remoteMuted 与 localMuted 始终保持一致
|
|
6938
|
+
this._remoteMuted = this._localMuted;
|
|
6939
|
+
/**
|
|
6940
|
+
* 派发事件以通知房间内其他成员
|
|
6941
|
+
* 本端禁用资源时需等待 signal 的扩散结果
|
|
6942
|
+
*/
|
|
6943
|
+
changed && this.emit(RCLocalTrack.__INNER_EVENT_MUTED_CHANGE__, this, resolve);
|
|
6944
|
+
engine.logger.info(RCLoggerTag.L_LOCAL_TRACK_SET_LOCAL_MUTED_O, {
|
|
6945
|
+
status: RCLoggerStatus.SUCCESSED,
|
|
6946
|
+
bool
|
|
6947
|
+
}, {
|
|
6948
|
+
logSource: engine.LogSource.RTC
|
|
6949
|
+
});
|
|
6910
6950
|
});
|
|
6911
6951
|
}
|
|
6912
6952
|
__innerSetPublished(bool) {
|
|
@@ -6930,6 +6970,7 @@ class RCLocalTrack extends RCTrack {
|
|
|
6930
6970
|
destroy() {
|
|
6931
6971
|
var _a;
|
|
6932
6972
|
logger.info(`track is destroyed -> trackId: ${this.getTrackId()}`);
|
|
6973
|
+
this._isDestroyed = true;
|
|
6933
6974
|
(_a = this._msTrack) === null || _a === void 0 ? void 0 : _a.stop();
|
|
6934
6975
|
super.__innerDestroy();
|
|
6935
6976
|
this.isAudioTrack() && super.__releaseMediaElement();
|
|
@@ -6941,6 +6982,12 @@ class RCLocalTrack extends RCTrack {
|
|
|
6941
6982
|
logSource: engine.LogSource.RTC
|
|
6942
6983
|
});
|
|
6943
6984
|
}
|
|
6985
|
+
/**
|
|
6986
|
+
* 判断 track 是否被销毁
|
|
6987
|
+
*/
|
|
6988
|
+
isDestroyed() {
|
|
6989
|
+
return this._isDestroyed;
|
|
6990
|
+
}
|
|
6944
6991
|
/**
|
|
6945
6992
|
* 为本地流设定上行码率,仅视频流有效,音频默认 15 kbps,不支持修改
|
|
6946
6993
|
* @description 当 `max` 或 `min` 值为 `0` 时,取动态码率计算结果
|
|
@@ -6984,7 +7031,7 @@ class RCLocalTrack extends RCTrack {
|
|
|
6984
7031
|
}
|
|
6985
7032
|
else if (((_b = this._msTrack) === null || _b === void 0 ? void 0 : _b.kind) === RCTrackKind.AUDIO) {
|
|
6986
7033
|
// 音频动态码率
|
|
6987
|
-
({ min, max } = { min:
|
|
7034
|
+
({ min, max } = { min: 32, max: 32 });
|
|
6988
7035
|
}
|
|
6989
7036
|
if ((_c = this._bitrateInfo) === null || _c === void 0 ? void 0 : _c.max) {
|
|
6990
7037
|
start = ((_d = this._bitrateInfo) === null || _d === void 0 ? void 0 : _d.max) * 0.7;
|
|
@@ -7156,10 +7203,15 @@ class RCLocalFileAudioTrack extends RCLocalFileTrack {
|
|
|
7156
7203
|
this._stopProcess = () => { };
|
|
7157
7204
|
}
|
|
7158
7205
|
_setLocalMuted(bool) {
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
}
|
|
7162
|
-
|
|
7206
|
+
const _super = Object.create(null, {
|
|
7207
|
+
_setLocalMuted: { get: () => super._setLocalMuted }
|
|
7208
|
+
});
|
|
7209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
7210
|
+
if (this._resource) {
|
|
7211
|
+
this._resource.muted = bool;
|
|
7212
|
+
}
|
|
7213
|
+
return _super._setLocalMuted.call(this, bool);
|
|
7214
|
+
});
|
|
7163
7215
|
}
|
|
7164
7216
|
play() {
|
|
7165
7217
|
// 自定义文件中的声音播放只需要修改 video 标签的 muted 属性
|
|
@@ -7425,6 +7477,11 @@ const getUUID22 = () => {
|
|
|
7425
7477
|
* @returns
|
|
7426
7478
|
*/
|
|
7427
7479
|
const transResolution = (resolution) => {
|
|
7480
|
+
if (RongRTCVideoBitrate[resolution]) {
|
|
7481
|
+
const { width, height } = RongRTCVideoBitrate[resolution];
|
|
7482
|
+
return { width, height };
|
|
7483
|
+
}
|
|
7484
|
+
// 保留原有逻辑,防止客户有自定义传值解析异常
|
|
7428
7485
|
const [width, height] = resolution.split('_').map(item => {
|
|
7429
7486
|
return parseInt(item.replace(/[^\d]/g, ''));
|
|
7430
7487
|
});
|
|
@@ -7881,7 +7938,7 @@ const getCommonHeader = () => ({
|
|
|
7881
7938
|
'Content-Type': 'application/json;charset=UTF-8',
|
|
7882
7939
|
'Cache-Control': 'no-cache',
|
|
7883
7940
|
ClientType: `web|${browserInfo.browser}|${browserInfo.version}`,
|
|
7884
|
-
ClientVersion: "5.4.
|
|
7941
|
+
ClientVersion: "5.4.7-alpha.2",
|
|
7885
7942
|
'Client-Session-Id': getUUID(),
|
|
7886
7943
|
'Request-Id': Date.now().toString()
|
|
7887
7944
|
});
|
|
@@ -9285,7 +9342,7 @@ class ASdpBuilder {
|
|
|
9285
9342
|
if (!/\bmaxaveragebitrate\b/ig.test(sdpAudioBlock)) {
|
|
9286
9343
|
const audioBitrate = [
|
|
9287
9344
|
'$1',
|
|
9288
|
-
`maxaveragebitrate=${bitrate.max *
|
|
9345
|
+
`maxaveragebitrate=${bitrate.max * ASdpBuilder.KBitrate}`
|
|
9289
9346
|
];
|
|
9290
9347
|
sdpAudioBlock = sdpAudioBlock.replace(/(\buseinbandfec=[^\r\n]+)/ig, audioBitrate.join(';'));
|
|
9291
9348
|
}
|
|
@@ -9299,7 +9356,7 @@ class ASdpBuilder {
|
|
|
9299
9356
|
*/
|
|
9300
9357
|
addAudioBlineAS(sdpAudioBlock, bitrate) {
|
|
9301
9358
|
if (!/\bb=AS:\d+\b/ig.test(sdpAudioBlock)) {
|
|
9302
|
-
return sdpAudioBlock.replace(/^(m=audio[^\r\n]+)/, `$1\r\nb=AS:${bitrate.max *
|
|
9359
|
+
return sdpAudioBlock.replace(/^(m=audio[^\r\n]+)/, `$1\r\nb=AS:${bitrate.max * ASdpBuilder.KBitrate}`);
|
|
9303
9360
|
}
|
|
9304
9361
|
return sdpAudioBlock;
|
|
9305
9362
|
}
|
|
@@ -9379,11 +9436,12 @@ class ASdpBuilder {
|
|
|
9379
9436
|
if (!/\bx-google-max-bitrate\b/.test(sdpVideoBlock)) {
|
|
9380
9437
|
const videoBitrate = [
|
|
9381
9438
|
'$1',
|
|
9382
|
-
`x-google-max-bitrate=${bitrate.max *
|
|
9383
|
-
`x-google-min-bitrate=${bitrate.min *
|
|
9384
|
-
`x-google-start-bitrate=${bitrate.start || bitrate.max *
|
|
9439
|
+
`x-google-max-bitrate=${bitrate.max * ASdpBuilder.KBitrate}`,
|
|
9440
|
+
`x-google-min-bitrate=${bitrate.min * ASdpBuilder.KBitrate}`,
|
|
9441
|
+
`x-google-start-bitrate=${bitrate.start || bitrate.max * ASdpBuilder.KBitrate * 0.7}`
|
|
9385
9442
|
];
|
|
9386
|
-
|
|
9443
|
+
// 42e01f 42001f 为 H264 编码
|
|
9444
|
+
sdpVideoBlock = sdpVideoBlock.replace(/(\bprofile-level-id=42[e0]01f\b)/ig, videoBitrate.join(';'));
|
|
9387
9445
|
}
|
|
9388
9446
|
return this.addVideoBlineAS(sdpVideoBlock, bitrate);
|
|
9389
9447
|
}
|
|
@@ -9396,7 +9454,7 @@ class ASdpBuilder {
|
|
|
9396
9454
|
addVideoBlineAS(sdpVideoBlock, bitrate) {
|
|
9397
9455
|
// b=AS: 定义本端带宽信息
|
|
9398
9456
|
if (!/\bb=AS:\d+\b/ig.test(sdpVideoBlock)) {
|
|
9399
|
-
return sdpVideoBlock.replace(/^(m=video[^\r\n]+)/, `$1\r\nb=AS:${bitrate.max *
|
|
9457
|
+
return sdpVideoBlock.replace(/^(m=video[^\r\n]+)/, `$1\r\nb=AS:${bitrate.max * ASdpBuilder.KBitrate}`);
|
|
9400
9458
|
}
|
|
9401
9459
|
return sdpVideoBlock;
|
|
9402
9460
|
}
|
|
@@ -9446,6 +9504,7 @@ class ASdpBuilder {
|
|
|
9446
9504
|
return stream;
|
|
9447
9505
|
}
|
|
9448
9506
|
}
|
|
9507
|
+
ASdpBuilder.KBitrate = 1;
|
|
9449
9508
|
|
|
9450
9509
|
var SdpSemantics;
|
|
9451
9510
|
(function (SdpSemantics) {
|
|
@@ -9760,6 +9819,113 @@ class UnifiedPlanSdpBuilder extends ASdpBuilder {
|
|
|
9760
9819
|
}
|
|
9761
9820
|
}
|
|
9762
9821
|
|
|
9822
|
+
/* 它是一堆解析 SDP 字符串的静态函数 */
|
|
9823
|
+
class SDPUtils {
|
|
9824
|
+
/* 将 SDP 拆分为多个部分。 */
|
|
9825
|
+
static splitSections(blob) {
|
|
9826
|
+
const parts = blob.split('\nm=');
|
|
9827
|
+
return parts.map((part, index) => (index > 0 ? 'm=' + part : part).trim() + '\r\n');
|
|
9828
|
+
}
|
|
9829
|
+
/**
|
|
9830
|
+
* 它需要一个 SDP 文本块并返回一个仅包含媒体部分的 SDP 文本数组
|
|
9831
|
+
* @param {string} blob - 您要解析的 SDP 字符串。
|
|
9832
|
+
* @returns 字符串数组。
|
|
9833
|
+
*/
|
|
9834
|
+
static getMediaSections(blob) {
|
|
9835
|
+
const sections = SDPUtils.splitSections(blob);
|
|
9836
|
+
sections.shift();
|
|
9837
|
+
return sections;
|
|
9838
|
+
}
|
|
9839
|
+
/* 解析 extmap 属性的 SDP 标头。 */
|
|
9840
|
+
static parseExtmap(line) {
|
|
9841
|
+
const parts = line.substr(9).split(' ');
|
|
9842
|
+
return {
|
|
9843
|
+
id: parseInt(parts[0], 10),
|
|
9844
|
+
direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
|
|
9845
|
+
uri: parts[1]
|
|
9846
|
+
};
|
|
9847
|
+
}
|
|
9848
|
+
/* 将 SDP 字符串拆分为字符串数组。 */
|
|
9849
|
+
static splitLines(blob) {
|
|
9850
|
+
return blob.trim().split('\n').map(line => line.trim());
|
|
9851
|
+
}
|
|
9852
|
+
/**
|
|
9853
|
+
* 它接受一个媒体部分并返回一个对象,其中包含媒体部分的种类、端口、协议和格式
|
|
9854
|
+
* @param {string} mediaSection - SDP的媒体部分。
|
|
9855
|
+
* @returns 具有以下属性的对象:
|
|
9856
|
+
* kind:媒体的类型(音频、视频等)
|
|
9857
|
+
* 端口:端口号
|
|
9858
|
+
* 协议:使用的协议
|
|
9859
|
+
* fmt:媒体的格式
|
|
9860
|
+
*/
|
|
9861
|
+
static parseMLine(mediaSection) {
|
|
9862
|
+
const lines = SDPUtils.splitLines(mediaSection);
|
|
9863
|
+
const parts = lines[0].substr(2).split(' ');
|
|
9864
|
+
return {
|
|
9865
|
+
kind: parts[0],
|
|
9866
|
+
port: parseInt(parts[1], 10),
|
|
9867
|
+
protocol: parts[2],
|
|
9868
|
+
fmt: parts.slice(3).join(' ')
|
|
9869
|
+
};
|
|
9870
|
+
}
|
|
9871
|
+
static matchPrefix(blob, prefix) {
|
|
9872
|
+
return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);
|
|
9873
|
+
}
|
|
9874
|
+
/**
|
|
9875
|
+
* 它从 SDP 媒体部分解析 msid 属性
|
|
9876
|
+
* @param {string} mediaSection - SDP的媒体部分。
|
|
9877
|
+
* @returns 具有两个属性的对象:流和轨道。
|
|
9878
|
+
*/
|
|
9879
|
+
static parseMsid(mediaSection) {
|
|
9880
|
+
let parts;
|
|
9881
|
+
const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
|
|
9882
|
+
if (spec.length === 1) {
|
|
9883
|
+
parts = spec[0].substr(7).split(' ');
|
|
9884
|
+
return { stream: parts[0], track: parts[1] };
|
|
9885
|
+
}
|
|
9886
|
+
const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
|
|
9887
|
+
.map(line => SDPUtils.parseSsrcMedia(line))
|
|
9888
|
+
.filter(msidParts => msidParts.attribute === 'msid');
|
|
9889
|
+
if (planB.length > 0) {
|
|
9890
|
+
if (planB[0].value) {
|
|
9891
|
+
parts = planB[0].value.split(' ');
|
|
9892
|
+
return { stream: parts[0], track: parts[1] };
|
|
9893
|
+
}
|
|
9894
|
+
}
|
|
9895
|
+
return { stream: '', track: '' };
|
|
9896
|
+
}
|
|
9897
|
+
/**
|
|
9898
|
+
* 它接受一个字符串并返回一个对象
|
|
9899
|
+
* @param {string} line - 我们正在解析的 SDP 行。
|
|
9900
|
+
*/
|
|
9901
|
+
static parseSsrcMedia(line) {
|
|
9902
|
+
const sp = line.indexOf(' ');
|
|
9903
|
+
const parts = {
|
|
9904
|
+
ssrc: parseInt(line.substr(7, sp - 7), 10)
|
|
9905
|
+
};
|
|
9906
|
+
const colon = line.indexOf(':', sp);
|
|
9907
|
+
if (colon > -1) {
|
|
9908
|
+
parts.attribute = line.substr(sp + 1, colon - sp - 1);
|
|
9909
|
+
parts.value = line.substr(colon + 1);
|
|
9910
|
+
}
|
|
9911
|
+
else {
|
|
9912
|
+
parts.attribute = line.substr(sp + 1);
|
|
9913
|
+
}
|
|
9914
|
+
return parts;
|
|
9915
|
+
}
|
|
9916
|
+
/**
|
|
9917
|
+
* 它从 SDP 的媒体部分返回中间值
|
|
9918
|
+
* @param {string} mediaSection - SDP的媒体部分。
|
|
9919
|
+
* @returns 媒体部分的中间。
|
|
9920
|
+
*/
|
|
9921
|
+
static getMid(mediaSection) {
|
|
9922
|
+
const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
|
|
9923
|
+
if (mid) {
|
|
9924
|
+
return mid.substr(6);
|
|
9925
|
+
}
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
|
|
9763
9929
|
class UnifiedPlanStrategy extends ASdpStrategy {
|
|
9764
9930
|
constructor() {
|
|
9765
9931
|
super(...arguments);
|
|
@@ -9928,12 +10094,36 @@ class UnifiedPlanStrategy extends ASdpStrategy {
|
|
|
9928
10094
|
const sdpBuilder = new UnifiedPlanSdpBuilder(sdp);
|
|
9929
10095
|
for (const msid in this._sendTransceiver) {
|
|
9930
10096
|
const transceiver = this._sendTransceiver[msid];
|
|
9931
|
-
const
|
|
10097
|
+
const kind = (_a = transceiver.sender.track) === null || _a === void 0 ? void 0 : _a.kind;
|
|
10098
|
+
let { direction, mid } = transceiver;
|
|
10099
|
+
if (!mid) {
|
|
10100
|
+
const sections = SDPUtils.getMediaSections(sdp);
|
|
10101
|
+
sections.forEach(item => {
|
|
10102
|
+
SDPUtils.parseExtmap(item);
|
|
10103
|
+
const { kind: parseKind } = SDPUtils.parseMLine(item);
|
|
10104
|
+
let { stream } = SDPUtils.parseMsid(item);
|
|
10105
|
+
const parseMid = SDPUtils.getMid(item);
|
|
10106
|
+
if (kind === 'video') {
|
|
10107
|
+
if (/tiny$/.test(stream)) {
|
|
10108
|
+
stream = stream.replace(/tiny$/ig, '1_tiny');
|
|
10109
|
+
}
|
|
10110
|
+
else {
|
|
10111
|
+
stream = stream + '_1';
|
|
10112
|
+
}
|
|
10113
|
+
}
|
|
10114
|
+
else {
|
|
10115
|
+
stream = stream + '_0';
|
|
10116
|
+
}
|
|
10117
|
+
// getMid
|
|
10118
|
+
if (stream === msid && kind === parseKind && parseMid) {
|
|
10119
|
+
mid = parseMid;
|
|
10120
|
+
}
|
|
10121
|
+
});
|
|
10122
|
+
}
|
|
9932
10123
|
if (mid) {
|
|
9933
10124
|
if (direction === RtpTransceiverDirection.SENDONLY) {
|
|
9934
10125
|
const localTrack = this._localTracks[msid];
|
|
9935
10126
|
const bitrate = localTrack === null || localTrack === void 0 ? void 0 : localTrack.getBitrate();
|
|
9936
|
-
const kind = (_a = transceiver.sender.track) === null || _a === void 0 ? void 0 : _a.kind;
|
|
9937
10127
|
if (kind === RCTrackKind.AUDIO && bitrate) {
|
|
9938
10128
|
sdpBuilder.setAudioBitrateWithMid(bitrate, mid);
|
|
9939
10129
|
}
|
|
@@ -9950,10 +10140,10 @@ class UnifiedPlanStrategy extends ASdpStrategy {
|
|
|
9950
10140
|
const transceiver = this._recvTransceiver[msid];
|
|
9951
10141
|
const { direction, mid } = transceiver;
|
|
9952
10142
|
if (mid) {
|
|
9953
|
-
if (direction === RtpTransceiverDirection.RECVONLY)
|
|
10143
|
+
if (direction === RtpTransceiverDirection.RECVONLY) ;
|
|
10144
|
+
if (direction === RtpTransceiverDirection.INACTIVE) {
|
|
9954
10145
|
sdpBuilder.clearnSsrcWithMid(mid, RtpTransceiverDirection.RECVONLY);
|
|
9955
10146
|
}
|
|
9956
|
-
if (direction === RtpTransceiverDirection.INACTIVE) ;
|
|
9957
10147
|
}
|
|
9958
10148
|
}
|
|
9959
10149
|
return sdpBuilder.stringify();
|
|
@@ -9961,35 +10151,38 @@ class UnifiedPlanStrategy extends ASdpStrategy {
|
|
|
9961
10151
|
// Answer SDP 中添加码率信息
|
|
9962
10152
|
resetAnswerSdp(sdp) {
|
|
9963
10153
|
var _a;
|
|
10154
|
+
// return sdp
|
|
9964
10155
|
const sdpBuilder = new UnifiedPlanSdpBuilder(sdp);
|
|
9965
10156
|
for (const msid in this._sendTransceiver) {
|
|
10157
|
+
/**
|
|
10158
|
+
* 通过 Sender 获取发布出去的资源
|
|
10159
|
+
* 对于已经发布出去的资源设置码率
|
|
10160
|
+
*/
|
|
9966
10161
|
const transceiver = this._sendTransceiver[msid];
|
|
9967
|
-
const {
|
|
10162
|
+
const { mid } = transceiver;
|
|
9968
10163
|
if (mid) {
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
}
|
|
9976
|
-
if (kind === RCTrackKind.VIDEO && bitrate) {
|
|
9977
|
-
sdpBuilder.setVideoBitrateWithMid(bitrate, mid);
|
|
9978
|
-
}
|
|
10164
|
+
const localTrack = this._localTracks[msid];
|
|
10165
|
+
const bitrate = localTrack === null || localTrack === void 0 ? void 0 : localTrack.getBitrate();
|
|
10166
|
+
const kind = (_a = transceiver.sender.track) === null || _a === void 0 ? void 0 : _a.kind;
|
|
10167
|
+
// 对于已经发布出去的资源设置码率
|
|
10168
|
+
if (kind === RCTrackKind.AUDIO && bitrate) {
|
|
10169
|
+
sdpBuilder.setAudioBitrateWithMid(bitrate, mid);
|
|
9979
10170
|
}
|
|
9980
|
-
if (
|
|
9981
|
-
sdpBuilder.
|
|
10171
|
+
if (kind === RCTrackKind.VIDEO && bitrate) {
|
|
10172
|
+
sdpBuilder.setVideoBitrateWithMid(bitrate, mid);
|
|
9982
10173
|
}
|
|
9983
10174
|
}
|
|
9984
10175
|
}
|
|
9985
|
-
for (const msid in this._recvTransceiver) {
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
}
|
|
10176
|
+
// for (const msid in this._recvTransceiver) {
|
|
10177
|
+
// const transceiver: RTCRtpTransceiver = this._recvTransceiver[msid]
|
|
10178
|
+
// const { direction, mid } = transceiver
|
|
10179
|
+
// if (mid) {
|
|
10180
|
+
// if (direction === RtpTransceiverDirection.RECVONLY) {
|
|
10181
|
+
// }
|
|
10182
|
+
// if (direction === RtpTransceiverDirection.INACTIVE) {
|
|
10183
|
+
// }
|
|
10184
|
+
// }
|
|
10185
|
+
// }
|
|
9993
10186
|
return sdpBuilder.stringify();
|
|
9994
10187
|
}
|
|
9995
10188
|
}
|
|
@@ -10251,13 +10444,13 @@ class RCRTCPeerConnection extends engine.EventEmitter {
|
|
|
10251
10444
|
getOutboundVideoInfo() {
|
|
10252
10445
|
return this._sdpStrategy.getOutboundVideoInfo();
|
|
10253
10446
|
}
|
|
10254
|
-
_onLocalTrackMuted(track) {
|
|
10447
|
+
_onLocalTrackMuted(track, resolve) {
|
|
10255
10448
|
// 修改已发布的小流状态
|
|
10256
10449
|
const tinyTrack = this.getLocalTrack(`${track.getTrackId()}_tiny`);
|
|
10257
10450
|
if (tinyTrack) {
|
|
10258
10451
|
tinyTrack.__innerGetMediaStreamTrack().enabled = !track.isLocalMuted();
|
|
10259
10452
|
}
|
|
10260
|
-
this.emit(RCLocalTrack.__INNER_EVENT_MUTED_CHANGE__, track);
|
|
10453
|
+
this.emit(RCLocalTrack.__INNER_EVENT_MUTED_CHANGE__, track, resolve);
|
|
10261
10454
|
engine.logger.info(RCLoggerTag.L_RTC_PEER_CONNECTION_LOCAL_TRACK_MUTED_O, {
|
|
10262
10455
|
status: RCLoggerStatus.SUCCESSED,
|
|
10263
10456
|
trackId: track.getTrackId()
|
|
@@ -10392,6 +10585,7 @@ class RCRTCPeerConnection extends engine.EventEmitter {
|
|
|
10392
10585
|
*/
|
|
10393
10586
|
__reportR3R4ToPolaris() {
|
|
10394
10587
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10588
|
+
clearTimeout(this._reportR3R4ToPolarisTimer);
|
|
10395
10589
|
yield this._sendR3R4Data();
|
|
10396
10590
|
this._reportR3R4ToPolarisTimer = setTimeout(this.__reportR3R4ToPolaris.bind(this), 2000);
|
|
10397
10591
|
});
|
|
@@ -10632,7 +10826,7 @@ class PolarisReporter {
|
|
|
10632
10826
|
* 加入房间
|
|
10633
10827
|
*/
|
|
10634
10828
|
sendR1() {
|
|
10635
|
-
const rtcVersion = "5.4.
|
|
10829
|
+
const rtcVersion = "5.4.7-alpha.2";
|
|
10636
10830
|
const imVersion = this._context.getCoreVersion();
|
|
10637
10831
|
const platform = 'web';
|
|
10638
10832
|
const pcName = navigator.platform;
|
|
@@ -12392,7 +12586,7 @@ class PublishCommand extends BaseCommand {
|
|
|
12392
12586
|
}
|
|
12393
12587
|
execute(store, invoker) {
|
|
12394
12588
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12395
|
-
|
|
12589
|
+
let tracks = this.tracks;
|
|
12396
12590
|
const roomId = store.roomId;
|
|
12397
12591
|
engine.logger.info(RCLoggerTag.L_ABSTRACT_ROOM_PUBLISH_T, {
|
|
12398
12592
|
trackIds: tracks.map(getTrackIdFromAttr)
|
|
@@ -12415,6 +12609,23 @@ class PublishCommand extends BaseCommand {
|
|
|
12415
12609
|
});
|
|
12416
12610
|
return { code: exports.RCRTCCode.PARAMS_ERROR };
|
|
12417
12611
|
}
|
|
12612
|
+
/**
|
|
12613
|
+
* 过滤已被销毁的 track
|
|
12614
|
+
*/
|
|
12615
|
+
tracks = tracks.filter((item) => {
|
|
12616
|
+
const { track } = item instanceof RCLocalTrack ? { track: item } : item;
|
|
12617
|
+
const isDestroyed = track.isDestroyed();
|
|
12618
|
+
if (isDestroyed) {
|
|
12619
|
+
engine.logger.warn(RCLoggerTag.L_ABSTRACT_ROOM_PUBLISH_R, {
|
|
12620
|
+
status: RCLoggerStatus.FAILED,
|
|
12621
|
+
msg: `track -> ${track.getTrackId()} has beem destroyed`
|
|
12622
|
+
});
|
|
12623
|
+
}
|
|
12624
|
+
return !isDestroyed;
|
|
12625
|
+
});
|
|
12626
|
+
if (!tracks.length) {
|
|
12627
|
+
return { code: exports.RCRTCCode.PUBLISH_TRACKS_IS_INVALID };
|
|
12628
|
+
}
|
|
12418
12629
|
/**
|
|
12419
12630
|
* 给创建的所有 peerConnection 添加事件
|
|
12420
12631
|
*/
|
|
@@ -12587,6 +12798,16 @@ class UnpublishCommand extends BaseCommand {
|
|
|
12587
12798
|
pubTasks.push(this.__unpublish(store, invoker, peerCList[index], tracks));
|
|
12588
12799
|
}
|
|
12589
12800
|
const unpubResList = yield Promise.all(pubTasks);
|
|
12801
|
+
// 如果所有资源都被发布过就不再发布消息
|
|
12802
|
+
let unpublistRes = unpubResList.length;
|
|
12803
|
+
unpubResList.forEach(item => {
|
|
12804
|
+
if (item.code === exports.RCRTCCode.SUCCESS && !item.data) {
|
|
12805
|
+
unpublistRes = unpublistRes - 1;
|
|
12806
|
+
}
|
|
12807
|
+
});
|
|
12808
|
+
if (unpublistRes === 0) {
|
|
12809
|
+
return { code: exports.RCRTCCode.SUCCESS };
|
|
12810
|
+
}
|
|
12590
12811
|
const result = yield this._mergeUnpublishRes(store, unpubResList);
|
|
12591
12812
|
engine.logger.info(RCLoggerTag.L_ABSTRACT_ROOM_UNPUBLISH_R, {
|
|
12592
12813
|
status: RCLoggerStatus.SUCCESSED,
|
|
@@ -12601,9 +12822,14 @@ class UnpublishCommand extends BaseCommand {
|
|
|
12601
12822
|
}
|
|
12602
12823
|
|
|
12603
12824
|
class LocalTrackMuteCommand extends BaseCommand {
|
|
12604
|
-
constructor(localTrack
|
|
12825
|
+
constructor(localTrack,
|
|
12826
|
+
/**
|
|
12827
|
+
* 接收 signal 扩散状态变更的结果
|
|
12828
|
+
*/
|
|
12829
|
+
_recvSignalResultFn) {
|
|
12605
12830
|
super();
|
|
12606
12831
|
this.localTrack = localTrack;
|
|
12832
|
+
this._recvSignalResultFn = _recvSignalResultFn;
|
|
12607
12833
|
}
|
|
12608
12834
|
execute(store, invoker) {
|
|
12609
12835
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -12630,6 +12856,8 @@ class LocalTrackMuteCommand extends BaseCommand {
|
|
|
12630
12856
|
}
|
|
12631
12857
|
}
|
|
12632
12858
|
const code = yield store.context.setRTCTotalRes(roomId, buildPlusMessage(RCRTCMessageType.MODIFY, plusList), buildTotalURIMessageContent(publishedList), RCRTCMessageType.TOTAL_CONTENT_RESOURCE);
|
|
12859
|
+
// 执行 localTrack.mute 传入的 resolve,并返回 signal 扩散的 code 码
|
|
12860
|
+
this._recvSignalResultFn(code);
|
|
12633
12861
|
if (code !== engine.ErrorCode.SUCCESS) {
|
|
12634
12862
|
logger.error('notice `track.enabled` change failed -> code: ' + code);
|
|
12635
12863
|
}
|
|
@@ -13696,9 +13924,9 @@ class RCAbstractRoom extends engine.EventEmitter {
|
|
|
13696
13924
|
* 本端流状态修改,需通知房间内其他成员
|
|
13697
13925
|
* @param localTrack
|
|
13698
13926
|
*/
|
|
13699
|
-
_onLocalTrackMuted(localTrack) {
|
|
13927
|
+
_onLocalTrackMuted(localTrack, resolve) {
|
|
13700
13928
|
return __awaiter(this, void 0, void 0, function* () {
|
|
13701
|
-
this._invoker.push(new LocalTrackMuteCommand(localTrack));
|
|
13929
|
+
this._invoker.push(new LocalTrackMuteCommand(localTrack, resolve));
|
|
13702
13930
|
});
|
|
13703
13931
|
}
|
|
13704
13932
|
/**
|
|
@@ -19081,9 +19309,9 @@ const installer = {
|
|
|
19081
19309
|
logger.error('Please use the https protocol or use `http://localhost` to open the page!');
|
|
19082
19310
|
return false;
|
|
19083
19311
|
}
|
|
19084
|
-
engine.VersionManage.add('plugin-rtc', "5.4.
|
|
19085
|
-
if (!engine.VersionManage.validEngine("
|
|
19086
|
-
logger.error(`The current engine version '${engine.VersionManage.getInfo().engine}' error, plugin-rtc required engine version at least '${"
|
|
19312
|
+
engine.VersionManage.add('plugin-rtc', "5.4.7-alpha.2");
|
|
19313
|
+
if (!engine.VersionManage.validEngine("5.3.4 - 5.4.5")) {
|
|
19314
|
+
logger.error(`The current engine version '${engine.VersionManage.getInfo().engine}' error, plugin-rtc required engine version at least '${"5.3.4 - 5.4.5"}'.`);
|
|
19087
19315
|
return false;
|
|
19088
19316
|
}
|
|
19089
19317
|
return true;
|
|
@@ -19092,7 +19320,7 @@ const installer = {
|
|
|
19092
19320
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
19093
19321
|
logger.setLogLevel(options.logLevel);
|
|
19094
19322
|
logger.setLogStdout(options.logStdout);
|
|
19095
|
-
logger.warn(`RCRTC Version: ${"5.4.
|
|
19323
|
+
logger.warn(`RCRTC Version: ${"5.4.7-alpha.2"}, Commit: ${"1826738708cea2e2c6cc7586713f2c24ab40e665"}`);
|
|
19096
19324
|
logger.warn(`browserInfo.browser -> ${browserInfo.browser}`);
|
|
19097
19325
|
logger.warn(`browserInfo.supportsUnifiedPlan -> ${browserInfo.supportsUnifiedPlan}`);
|
|
19098
19326
|
logger.warn(`browserInfo.version -> ${browserInfo.version}`);
|