@webex/plugin-meetings 2.31.3 → 2.32.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.
- package/LICENSE +2 -0
- package/dist/constants.js +60 -36
- package/dist/constants.js.map +1 -1
- package/dist/media/properties.js +1 -1
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +72 -30
- package/dist/meeting/index.js.map +1 -1
- package/dist/peer-connection-manager/index.js +9 -11
- package/dist/peer-connection-manager/index.js.map +1 -1
- package/dist/peer-connection-manager/util.js +99 -3
- package/dist/peer-connection-manager/util.js.map +1 -1
- package/package.json +19 -18
- package/src/constants.ts +45 -19
- package/src/media/properties.js +1 -1
- package/src/meeting/index.js +28 -5
- package/src/peer-connection-manager/index.js +11 -12
- package/src/peer-connection-manager/util.ts +117 -0
- package/test/unit/spec/meeting/index.js +56 -50
- package/test/unit/spec/peerconnection-manager/utils.js +30 -7
- package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +389 -0
- package/src/peer-connection-manager/util.js +0 -19
package/src/meeting/index.js
CHANGED
|
@@ -125,6 +125,7 @@ export const MEDIA_UPDATE_TYPE = {
|
|
|
125
125
|
* @property {String} audio.deviceId
|
|
126
126
|
* @property {Object} video
|
|
127
127
|
* @property {String} video.deviceId
|
|
128
|
+
* @property {String} video.localVideoQuality // [240p, 360p, 480p, 720p, 1080p]
|
|
128
129
|
*/
|
|
129
130
|
|
|
130
131
|
/**
|
|
@@ -2742,6 +2743,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2742
2743
|
aspectRatio, frameRate, height, width, deviceId
|
|
2743
2744
|
} = videoTrack.getSettings();
|
|
2744
2745
|
|
|
2746
|
+
const {localQualityLevel} = this.mediaProperties;
|
|
2747
|
+
|
|
2748
|
+
if (Number(localQualityLevel.slice(0, -1)) > height) {
|
|
2749
|
+
LoggerProxy.logger.error(`Meeting:index#setLocalVideoTrack --> Local video quality of ${localQualityLevel} not supported,
|
|
2750
|
+
downscaling to highest possible resolution of ${height}p`);
|
|
2751
|
+
|
|
2752
|
+
this.mediaProperties.setLocalQualityLevel(`${height}p`);
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2745
2755
|
this.mediaProperties.setLocalVideoTrack(videoTrack);
|
|
2746
2756
|
if (this.video) this.video.applyClientStateLocally(this);
|
|
2747
2757
|
|
|
@@ -4004,6 +4014,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4004
4014
|
LoggerProxy.logger.warn('Meeting:index#getMediaStreams --> Please use `meeting.shareScreen()` to manually start the screen share after successfully joining the meeting');
|
|
4005
4015
|
}
|
|
4006
4016
|
|
|
4017
|
+
if (!audioVideo.video) {
|
|
4018
|
+
audioVideo = {...audioVideo, video: {...audioVideo.video, ...VIDEO_RESOLUTIONS[this.mediaProperties.localQualityLevel].video}};
|
|
4019
|
+
}
|
|
4007
4020
|
// extract deviceId if exists otherwise default to null.
|
|
4008
4021
|
const {deviceId: preferredVideoDevice} = (audioVideo && audioVideo.video || {deviceId: null});
|
|
4009
4022
|
const lastVideoDeviceId = this.mediaProperties.getVideoDeviceId();
|
|
@@ -5342,7 +5355,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5342
5355
|
/**
|
|
5343
5356
|
* Sets the quality of the local video stream
|
|
5344
5357
|
* @param {String} level {LOW|MEDIUM|HIGH}
|
|
5345
|
-
* @returns {Promise}
|
|
5358
|
+
* @returns {Promise<MediaStream>} localStream
|
|
5346
5359
|
*/
|
|
5347
5360
|
setLocalVideoQuality(level) {
|
|
5348
5361
|
LoggerProxy.logger.log(`Meeting:index#setLocalVideoQuality --> Setting quality to ${level}`);
|
|
@@ -5371,13 +5384,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5371
5384
|
sendShare: this.mediaProperties.mediaDirection.sendShare
|
|
5372
5385
|
};
|
|
5373
5386
|
|
|
5387
|
+
// When changing local video quality level
|
|
5388
|
+
// Need to stop current track first as chrome doesn't support resolution upscaling(for eg. changing 480p to 720p)
|
|
5389
|
+
// Without feeding it a new track
|
|
5390
|
+
// open bug link: https://bugs.chromium.org/p/chromium/issues/detail?id=943469
|
|
5391
|
+
if (isBrowser('chrome') && this.mediaProperties.videoTrack) Media.stopTracks(this.mediaProperties.videoTrack);
|
|
5392
|
+
|
|
5374
5393
|
return this.getMediaStreams(mediaDirection, VIDEO_RESOLUTIONS[level])
|
|
5375
|
-
.then(([localStream]) =>
|
|
5376
|
-
this.updateVideo({
|
|
5394
|
+
.then(async ([localStream]) => {
|
|
5395
|
+
await this.updateVideo({
|
|
5377
5396
|
sendVideo: true,
|
|
5378
5397
|
receiveVideo: true,
|
|
5379
5398
|
stream: localStream
|
|
5380
|
-
})
|
|
5399
|
+
});
|
|
5400
|
+
|
|
5401
|
+
return localStream;
|
|
5402
|
+
});
|
|
5381
5403
|
}
|
|
5382
5404
|
|
|
5383
5405
|
/**
|
|
@@ -5410,9 +5432,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5410
5432
|
}
|
|
5411
5433
|
|
|
5412
5434
|
/**
|
|
5413
|
-
*
|
|
5435
|
+
* This is deprecated, please use setLocalVideoQuality for setting local and setRemoteQualityLevel for remote
|
|
5414
5436
|
* @param {String} level {LOW|MEDIUM|HIGH}
|
|
5415
5437
|
* @returns {Promise}
|
|
5438
|
+
* @deprecated After FHD support
|
|
5416
5439
|
*/
|
|
5417
5440
|
setMeetingQuality(level) {
|
|
5418
5441
|
LoggerProxy.logger.log(`Meeting:index#setMeetingQuality --> Setting quality to ${level}`);
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
PEER_CONNECTION_STATE,
|
|
21
21
|
OFFER,
|
|
22
22
|
QUALITY_LEVELS,
|
|
23
|
-
|
|
23
|
+
REMOTE_VIDEO_CONSTRAINTS
|
|
24
24
|
} from '../constants';
|
|
25
25
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
26
26
|
import {error, eventType} from '../metrics/config';
|
|
@@ -70,18 +70,16 @@ const insertBandwidthLimit = (sdpLines, index) => {
|
|
|
70
70
|
* @param {String} [level=QUALITY_LEVELS.HIGH] quality level for max-fs
|
|
71
71
|
* @returns {String}
|
|
72
72
|
*/
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const setRemoteVideoConstraints = (sdp, level = QUALITY_LEVELS.HIGH) => {
|
|
74
|
+
const maxFs = REMOTE_VIDEO_CONSTRAINTS.MAX_FS[level];
|
|
75
|
+
|
|
76
|
+
if (!maxFs) {
|
|
77
|
+
throw new ParameterError(`setRemoteVideoConstraints: unable to set max framesize, value for level "${level}" is not defined`);
|
|
76
78
|
}
|
|
77
|
-
// eslint-disable-next-line no-warning-comments
|
|
78
|
-
// TODO convert with sdp parser, no munging
|
|
79
|
-
let replaceSdp = sdp;
|
|
80
|
-
const maxFsLine = `${SDP.MAX_FS}${MAX_FRAMESIZES[level]}`;
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
const modifiedSdp = PeerConnectionUtils.adjustH264Profile(sdp, maxFs);
|
|
83
81
|
|
|
84
|
-
return
|
|
82
|
+
return modifiedSdp;
|
|
85
83
|
};
|
|
86
84
|
|
|
87
85
|
|
|
@@ -188,8 +186,8 @@ pc.iceCandidate = (peerConnection, {remoteQualityLevel}) =>
|
|
|
188
186
|
const miliseconds = parseInt(Math.abs(Date.now() - now), 4);
|
|
189
187
|
|
|
190
188
|
peerConnection.sdp = limitBandwidth(peerConnection.localDescription.sdp);
|
|
191
|
-
peerConnection.sdp = setMaxFs(peerConnection.sdp, remoteQualityLevel);
|
|
192
189
|
peerConnection.sdp = PeerConnectionUtils.convertCLineToIpv4(peerConnection.sdp);
|
|
190
|
+
peerConnection.sdp = setRemoteVideoConstraints(peerConnection.sdp, remoteQualityLevel);
|
|
193
191
|
|
|
194
192
|
const invalidSdpPresent = isSdpInvalid(peerConnection.sdp);
|
|
195
193
|
|
|
@@ -540,8 +538,9 @@ pc.createAnswer = (params, {meetingId, remoteQualityLevel}) => {
|
|
|
540
538
|
.then(() => pc.iceCandidate(peerConnection, {remoteQualityLevel}))
|
|
541
539
|
.then(() => {
|
|
542
540
|
peerConnection.sdp = limitBandwidth(peerConnection.localDescription.sdp);
|
|
543
|
-
peerConnection.sdp = setMaxFs(peerConnection.sdp, remoteQualityLevel);
|
|
544
541
|
peerConnection.sdp = PeerConnectionUtils.convertCLineToIpv4(peerConnection.sdp);
|
|
542
|
+
peerConnection.sdp = setRemoteVideoConstraints(peerConnection.sdp, remoteQualityLevel);
|
|
543
|
+
|
|
545
544
|
if (!checkH264Support(peerConnection.sdp)) {
|
|
546
545
|
throw new MediaError('openH264 is downloading please Wait. Upload logs if not working on second try');
|
|
547
546
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { parse } from '@webex/ts-sdp';
|
|
2
|
+
|
|
3
|
+
interface IPeerConnectionUtils {
|
|
4
|
+
convertCLineToIpv4: (sdp: string) => string;
|
|
5
|
+
adjustH264Profile: (sdp: string, maxFsValue: number) => string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const PeerConnectionUtils = {} as IPeerConnectionUtils;
|
|
9
|
+
|
|
10
|
+
// max-fs values for all H264 profile levels
|
|
11
|
+
const maxFsForProfileLevel = {
|
|
12
|
+
10: 99,
|
|
13
|
+
11: 396,
|
|
14
|
+
12: 396,
|
|
15
|
+
13: 396,
|
|
16
|
+
20: 396,
|
|
17
|
+
21: 792,
|
|
18
|
+
22: 1620,
|
|
19
|
+
30: 1620,
|
|
20
|
+
31: 3600,
|
|
21
|
+
32: 5120,
|
|
22
|
+
40: 8192,
|
|
23
|
+
41: 8192,
|
|
24
|
+
42: 8704,
|
|
25
|
+
50: 22080,
|
|
26
|
+
51: 36864,
|
|
27
|
+
52: 36864,
|
|
28
|
+
60: 139264,
|
|
29
|
+
61: 139264,
|
|
30
|
+
62: 139264,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const framesPerSecond = 30;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Convert C line to IPv4
|
|
37
|
+
* @param {string} sdp
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
PeerConnectionUtils.convertCLineToIpv4 = (sdp: string) => {
|
|
41
|
+
let replaceSdp = sdp;
|
|
42
|
+
|
|
43
|
+
// TODO: remove this once linus supports Ipv6 c line.currently linus rejects SDP with c line having ipv6 candidates we are
|
|
44
|
+
// mocking ipv6 to ipv4 candidates
|
|
45
|
+
// https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-299232
|
|
46
|
+
replaceSdp = replaceSdp.replace(/c=IN IP6 .*/gi, 'c=IN IP4 0.0.0.0');
|
|
47
|
+
|
|
48
|
+
return replaceSdp;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* estimate profile levels for max-fs & max-mbps values
|
|
53
|
+
* @param {string} sdp
|
|
54
|
+
* @param {number} maxFsValue
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
PeerConnectionUtils.adjustH264Profile = (sdp: string, maxFsValue: number) => {
|
|
58
|
+
// converting with ts-sdp parser, no munging
|
|
59
|
+
const parsedSdp = parse(sdp);
|
|
60
|
+
|
|
61
|
+
parsedSdp.avMedia.forEach((media) => {
|
|
62
|
+
if (media.type === 'video') {
|
|
63
|
+
media.codecs.forEach((codec) => {
|
|
64
|
+
if (codec.name?.toUpperCase() === 'H264') {
|
|
65
|
+
// there should really be just 1 fmtp line, but just in case, we process all of them
|
|
66
|
+
codec.fmtParams = codec.fmtParams.map((fmtp) => {
|
|
67
|
+
const parsedRegex = fmtp.match(/(.*)profile-level-id=(\w{4})(\w{2})(.*)/);
|
|
68
|
+
|
|
69
|
+
if (parsedRegex && parsedRegex.length === 5) {
|
|
70
|
+
const stuffBeforeProfileLevelId = parsedRegex[1];
|
|
71
|
+
const profile = parsedRegex[2].toLowerCase();
|
|
72
|
+
const levelId = parseInt(parsedRegex[3], 16);
|
|
73
|
+
const stuffAfterProfileLevelId = parsedRegex[4];
|
|
74
|
+
|
|
75
|
+
if (!maxFsForProfileLevel[levelId]) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`found unsupported h264 profile level id value in the SDP: ${levelId}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (maxFsForProfileLevel[levelId] === maxFsValue) {
|
|
82
|
+
// profile level already matches our desired max-fs value, so we don't need to do anything
|
|
83
|
+
return fmtp;
|
|
84
|
+
}
|
|
85
|
+
if (maxFsForProfileLevel[levelId] < maxFsValue) {
|
|
86
|
+
// profile level has too low max-fs, so we need to override it (this is upgrading)
|
|
87
|
+
return `${fmtp};max-fs=${maxFsValue};max-mbps=${maxFsValue * framesPerSecond}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// profile level has too high max-fs value, so we need to use a lower level
|
|
91
|
+
|
|
92
|
+
// find highest level that has the matching maxFs
|
|
93
|
+
const newLevelId = Object.keys(maxFsForProfileLevel)
|
|
94
|
+
.reverse()
|
|
95
|
+
.find((key) => maxFsForProfileLevel[key] === maxFsValue);
|
|
96
|
+
|
|
97
|
+
if (newLevelId) {
|
|
98
|
+
// Object.keys returns keys as strings, so we need to parse it to an int again and then convert to hex
|
|
99
|
+
const newLevelIdHex = parseInt(newLevelId, 10).toString(16);
|
|
100
|
+
|
|
101
|
+
return `${stuffBeforeProfileLevelId}profile-level-id=${profile}${newLevelIdHex};max-mbps=${maxFsValue * framesPerSecond}${stuffAfterProfileLevelId}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new Error(`unsupported maxFsValue: ${maxFsValue}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return fmtp;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return parsedSdp.toString();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default PeerConnectionUtils;
|
|
@@ -671,7 +671,7 @@ describe('plugin-meetings', () => {
|
|
|
671
671
|
const audioVideoSettings = {};
|
|
672
672
|
|
|
673
673
|
sinon.stub(meeting.mediaProperties, 'videoDeviceId').value(videoDevice);
|
|
674
|
-
|
|
674
|
+
sinon.stub(meeting.mediaProperties, 'localQualityLevel').value('480p');
|
|
675
675
|
await meeting.getMediaStreams(mediaDirection, audioVideoSettings);
|
|
676
676
|
|
|
677
677
|
assert.calledWith(Media.getUserMedia, {
|
|
@@ -680,6 +680,8 @@ describe('plugin-meetings', () => {
|
|
|
680
680
|
},
|
|
681
681
|
{
|
|
682
682
|
video: {
|
|
683
|
+
width: {max: 640, ideal: 640},
|
|
684
|
+
height: {max: 480, ideal: 480},
|
|
683
685
|
deviceId: videoDevice
|
|
684
686
|
}
|
|
685
687
|
});
|
|
@@ -701,6 +703,30 @@ describe('plugin-meetings', () => {
|
|
|
701
703
|
assert.calledWith(meeting.mediaProperties.setVideoDeviceId, newVideoDevice);
|
|
702
704
|
});
|
|
703
705
|
|
|
706
|
+
it('uses the passed custom video resolution', async () => {
|
|
707
|
+
const mediaDirection = {sendAudio: true, sendVideo: true, sendShare: false};
|
|
708
|
+
const customAudioVideoSettings = {
|
|
709
|
+
video: {
|
|
710
|
+
width: {
|
|
711
|
+
max: 400,
|
|
712
|
+
ideal: 400
|
|
713
|
+
},
|
|
714
|
+
height: {
|
|
715
|
+
max: 200,
|
|
716
|
+
ideal: 200
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
sinon.stub(meeting.mediaProperties, 'localQualityLevel').value('200p');
|
|
722
|
+
await meeting.getMediaStreams(mediaDirection, customAudioVideoSettings);
|
|
723
|
+
|
|
724
|
+
assert.calledWith(Media.getUserMedia, {
|
|
725
|
+
...mediaDirection,
|
|
726
|
+
isSharing: false
|
|
727
|
+
},
|
|
728
|
+
customAudioVideoSettings);
|
|
729
|
+
});
|
|
704
730
|
it('should not access camera if sendVideo is false ', async () => {
|
|
705
731
|
await meeting.getMediaStreams({sendAudio: true, sendVideo: false});
|
|
706
732
|
|
|
@@ -2155,11 +2181,19 @@ describe('plugin-meetings', () => {
|
|
|
2155
2181
|
describe('#setLocalVideoQuality', () => {
|
|
2156
2182
|
let mediaDirection;
|
|
2157
2183
|
|
|
2184
|
+
const fakeTrack = {getSettings: () => ({height: 720})};
|
|
2185
|
+
const USER_AGENT_CHROME_MAC =
|
|
2186
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
|
|
2187
|
+
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36';
|
|
2188
|
+
|
|
2158
2189
|
beforeEach(() => {
|
|
2159
2190
|
mediaDirection = {sendAudio: true, sendVideo: true, sendShare: false};
|
|
2160
2191
|
meeting.getMediaStreams = sinon.stub().returns(Promise.resolve([]));
|
|
2161
|
-
meeting.updateVideo = sinon.stub().returns(Promise.resolve());
|
|
2162
2192
|
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
2193
|
+
meeting.canUpdateMedia = sinon.stub().returns(true);
|
|
2194
|
+
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve());
|
|
2195
|
+
MeetingUtil.updateTransceiver = sinon.stub().returns(Promise.resolve());
|
|
2196
|
+
sinon.stub(MeetingUtil, 'getTrack').returns({videoTrack: fakeTrack});
|
|
2163
2197
|
});
|
|
2164
2198
|
|
|
2165
2199
|
it('should have #setLocalVideoQuality', () => {
|
|
@@ -2167,15 +2201,35 @@ describe('plugin-meetings', () => {
|
|
|
2167
2201
|
});
|
|
2168
2202
|
|
|
2169
2203
|
it('should call getMediaStreams with the proper level', () => meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
|
|
2204
|
+
delete mediaDirection.receiveVideo;
|
|
2170
2205
|
assert.calledWith(meeting.getMediaStreams,
|
|
2171
2206
|
mediaDirection,
|
|
2172
2207
|
CONSTANTS.VIDEO_RESOLUTIONS[CONSTANTS.QUALITY_LEVELS.LOW]);
|
|
2173
2208
|
}));
|
|
2174
2209
|
|
|
2210
|
+
it('when browser is chrome then it should stop previous video track', () => {
|
|
2211
|
+
meeting.mediaProperties.videoTrack = fakeTrack;
|
|
2212
|
+
assert.equal(
|
|
2213
|
+
BrowserDetection(USER_AGENT_CHROME_MAC).getBrowserName(),
|
|
2214
|
+
'Chrome'
|
|
2215
|
+
);
|
|
2216
|
+
meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW)
|
|
2217
|
+
.then(() => {
|
|
2218
|
+
assert.calledWith(Media.stopTracks, fakeTrack);
|
|
2219
|
+
});
|
|
2220
|
+
});
|
|
2221
|
+
|
|
2175
2222
|
it('should set mediaProperty with the proper level', () => meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
|
|
2176
2223
|
assert.equal(meeting.mediaProperties.localQualityLevel, CONSTANTS.QUALITY_LEVELS.LOW);
|
|
2177
2224
|
}));
|
|
2178
2225
|
|
|
2226
|
+
it('when device does not support 1080p then it should set localQualityLevel with highest possible resolution', () => {
|
|
2227
|
+
meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS['1080p'])
|
|
2228
|
+
.then(() => {
|
|
2229
|
+
assert.equal(meeting.mediaProperties.localQualityLevel, CONSTANTS.QUALITY_LEVELS['720p']);
|
|
2230
|
+
});
|
|
2231
|
+
});
|
|
2232
|
+
|
|
2179
2233
|
it('should error if set to a invalid level', () => {
|
|
2180
2234
|
assert.isRejected(meeting.setLocalVideoQuality('invalid'));
|
|
2181
2235
|
});
|
|
@@ -2217,54 +2271,6 @@ describe('plugin-meetings', () => {
|
|
|
2217
2271
|
});
|
|
2218
2272
|
});
|
|
2219
2273
|
|
|
2220
|
-
describe('#setMeetingQuality', () => {
|
|
2221
|
-
let mediaDirection;
|
|
2222
|
-
|
|
2223
|
-
beforeEach(() => {
|
|
2224
|
-
mediaDirection = {
|
|
2225
|
-
receiveAudio: true, receiveVideo: true, receiveShare: false, sendVideo: true
|
|
2226
|
-
};
|
|
2227
|
-
meeting.setRemoteQualityLevel = sinon.stub().returns(Promise.resolve());
|
|
2228
|
-
meeting.setLocalVideoQuality = sinon.stub().returns(Promise.resolve());
|
|
2229
|
-
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
2230
|
-
});
|
|
2231
|
-
|
|
2232
|
-
it('should have #setMeetingQuality', () => {
|
|
2233
|
-
assert.exists(meeting.setMeetingQuality);
|
|
2234
|
-
});
|
|
2235
|
-
|
|
2236
|
-
it('should call setRemoteQualityLevel', () => meeting.setMeetingQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
|
|
2237
|
-
assert.calledOnce(meeting.setRemoteQualityLevel);
|
|
2238
|
-
}));
|
|
2239
|
-
|
|
2240
|
-
it('should not call setRemoteQualityLevel when receiveVideo and receiveAudio are false', () => {
|
|
2241
|
-
mediaDirection.receiveAudio = false;
|
|
2242
|
-
mediaDirection.receiveVideo = false;
|
|
2243
|
-
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
2244
|
-
|
|
2245
|
-
return meeting.setMeetingQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
|
|
2246
|
-
assert.notCalled(meeting.setRemoteQualityLevel);
|
|
2247
|
-
});
|
|
2248
|
-
});
|
|
2249
|
-
|
|
2250
|
-
it('should call setLocalVideoQuality', () => meeting.setMeetingQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
|
|
2251
|
-
assert.calledOnce(meeting.setLocalVideoQuality);
|
|
2252
|
-
}));
|
|
2253
|
-
|
|
2254
|
-
it('should not call setLocalVideoQuality when sendVideo is false', () => {
|
|
2255
|
-
mediaDirection.sendVideo = false;
|
|
2256
|
-
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
2257
|
-
|
|
2258
|
-
return meeting.setMeetingQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
|
|
2259
|
-
assert.notCalled(meeting.setLocalVideoQuality);
|
|
2260
|
-
});
|
|
2261
|
-
});
|
|
2262
|
-
|
|
2263
|
-
it('should error if set to a invalid level', () => {
|
|
2264
|
-
assert.isRejected(meeting.setMeetingQuality('invalid'));
|
|
2265
|
-
});
|
|
2266
|
-
});
|
|
2267
|
-
|
|
2268
2274
|
describe('#usePhoneAudio', () => {
|
|
2269
2275
|
beforeEach(() => {
|
|
2270
2276
|
meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.resolve({body: {locus: 'testData'}}));
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
|
|
2
1
|
import {assert} from '@webex/test-helper-chai';
|
|
3
2
|
import PeerConnectionUtils from '@webex/plugin-meetings/src/peer-connection-manager/util';
|
|
4
3
|
|
|
4
|
+
import {
|
|
5
|
+
SDP_MULTIPLE_VIDEO_CODECS,
|
|
6
|
+
SDP_MULTIPLE_VIDEO_CODECS_WITH_LOWERED_H264_PROFILE_LEVEL,
|
|
7
|
+
SDP_MULTIPLE_VIDEO_CODECS_WITH_MAX_FS
|
|
8
|
+
} from './utils.test-fixtures';
|
|
9
|
+
|
|
5
10
|
describe('Peerconnection Manager', () => {
|
|
6
11
|
describe('Utils', () => {
|
|
7
12
|
describe('convertCLineToIpv4', () => {
|
|
8
13
|
it('changes ipv6 to ipv4 default', () => {
|
|
9
14
|
const localSdp = 'v=0\r\n' +
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
'm=video 5004 UDP/TLS/RTP/SAVPF 102 127 97 99\r\n' +
|
|
16
|
+
'c=IN IP6 2607:fb90:d27c:b314:211a:32dd:c47f:ffe\r\n' +
|
|
17
|
+
'a=rtpmap:127 H264/90000\r\n';
|
|
13
18
|
const resultSdp = 'v=0\r\n' +
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
'm=video 5004 UDP/TLS/RTP/SAVPF 102 127 97 99\r\n' +
|
|
20
|
+
'c=IN IP4 0.0.0.0\r\n' +
|
|
21
|
+
'a=rtpmap:127 H264/90000\r\n';
|
|
17
22
|
|
|
18
23
|
|
|
19
24
|
const temp = PeerConnectionUtils.convertCLineToIpv4(localSdp);
|
|
@@ -21,5 +26,23 @@ describe('Peerconnection Manager', () => {
|
|
|
21
26
|
assert.equal(temp, resultSdp);
|
|
22
27
|
});
|
|
23
28
|
});
|
|
29
|
+
|
|
30
|
+
describe('adjustH264Profile', () => {
|
|
31
|
+
it('appends max-fs and max-mbps to h264 fmtp lines when h264MaxFs value is higher than the value from the profile', () => {
|
|
32
|
+
const modifiedSdp = PeerConnectionUtils.adjustH264Profile(SDP_MULTIPLE_VIDEO_CODECS, 8192);
|
|
33
|
+
|
|
34
|
+
assert.equal(modifiedSdp, SDP_MULTIPLE_VIDEO_CODECS_WITH_MAX_FS);
|
|
35
|
+
});
|
|
36
|
+
it('keeps fmtp lines the same when h264MaxFs value matches the value from the profile', () => {
|
|
37
|
+
const modifiedSdp = PeerConnectionUtils.adjustH264Profile(SDP_MULTIPLE_VIDEO_CODECS, 3600);
|
|
38
|
+
|
|
39
|
+
assert.equal(modifiedSdp, SDP_MULTIPLE_VIDEO_CODECS);
|
|
40
|
+
});
|
|
41
|
+
it('changes the profile level in h264 fmtp lines when h264MaxFs value is lower than the value from the profile', () => {
|
|
42
|
+
const modifiedSdp = PeerConnectionUtils.adjustH264Profile(SDP_MULTIPLE_VIDEO_CODECS, 1620);
|
|
43
|
+
|
|
44
|
+
assert.equal(modifiedSdp, SDP_MULTIPLE_VIDEO_CODECS_WITH_LOWERED_H264_PROFILE_LEVEL);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
24
47
|
});
|
|
25
48
|
});
|