feat(load-test) split to a separate repository
It now lives here https://github.com/jitsi/jitsi-meet-load-test
This commit is contained in:
parent
a7abe84479
commit
0b57bcb20b
|
@ -3,11 +3,9 @@ build/*
|
||||||
|
|
||||||
# Third-party source code which we (1) do not want to modify or (2) try to
|
# Third-party source code which we (1) do not want to modify or (2) try to
|
||||||
# modify as little as possible.
|
# modify as little as possible.
|
||||||
flow-typed/*
|
|
||||||
libs/*
|
libs/*
|
||||||
resources/*
|
resources/*
|
||||||
react/features/stream-effects/virtual-background/vendor/*
|
react/features/stream-effects/virtual-background/vendor/*
|
||||||
load-test/*
|
|
||||||
react/features/face-landmarks/resources/*
|
react/features/face-landmarks/resources/*
|
||||||
|
|
||||||
# ESLint will by default ignore its own configuration file. However, there does
|
# ESLint will by default ignore its own configuration file. However, there does
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -19,12 +19,9 @@ WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
|
||||||
|
|
||||||
all: compile deploy clean
|
all: compile deploy clean
|
||||||
|
|
||||||
compile: compile-load-test
|
compile:
|
||||||
$(WEBPACK)
|
$(WEBPACK)
|
||||||
|
|
||||||
compile-load-test:
|
|
||||||
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -fr $(BUILD_DIR)
|
rm -fr $(BUILD_DIR)
|
||||||
|
|
||||||
|
|
|
@ -14,5 +14,3 @@ resources/robots.txt /usr/share/jitsi-meet/
|
||||||
resources/*.sh /usr/share/jitsi-meet/scripts/
|
resources/*.sh /usr/share/jitsi-meet/scripts/
|
||||||
pwa-worker.js /usr/share/jitsi-meet/
|
pwa-worker.js /usr/share/jitsi-meet/
|
||||||
manifest.json /usr/share/jitsi-meet/
|
manifest.json /usr/share/jitsi-meet/
|
||||||
resources/load-test/*.html /usr/share/jitsi-meet/load-test/
|
|
||||||
resources/load-test/libs /usr/share/jitsi-meet/load-test/
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head lang="en">
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title></title>
|
|
||||||
<script><!--#include virtual="/config.js" --></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
|
||||||
<script src="../libs/lib-jitsi-meet.min.js?v=139"></script>
|
|
||||||
<script src="libs/load-test-participant.min.js" ></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div>Number of participants: <span id="participants">1</span></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,523 +0,0 @@
|
||||||
/* global $, config, JitsiMeetJS */
|
|
||||||
import 'jquery';
|
|
||||||
import { setConfigFromURLParams } from '../../react/features/base/config/functions';
|
|
||||||
import { parseURLParams } from '../../react/features/base/util/parseURLParams';
|
|
||||||
import { parseURIString } from '../../react/features/base/util/uri';
|
|
||||||
import { validateLastNLimits, limitLastN } from '../../react/features/base/lastn/functions';
|
|
||||||
|
|
||||||
setConfigFromURLParams(config, {}, {}, window.location);
|
|
||||||
|
|
||||||
const params = parseURLParams(window.location, false, 'hash');
|
|
||||||
const { isHuman = false } = params;
|
|
||||||
const {
|
|
||||||
localVideo = config.startWithVideoMuted !== true,
|
|
||||||
remoteVideo = isHuman,
|
|
||||||
remoteAudio = isHuman,
|
|
||||||
autoPlayVideo = config.testing.noAutoPlayVideo !== true,
|
|
||||||
stageView = config.disableTileView,
|
|
||||||
numClients = 1,
|
|
||||||
clientInterval = 100 // ms
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
let {
|
|
||||||
localAudio = config.startWithAudioMuted !== true,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const { room: roomName } = parseURIString(window.location.toString());
|
|
||||||
|
|
||||||
class LoadTestClient {
|
|
||||||
constructor(id) {
|
|
||||||
this.id = id;
|
|
||||||
this.connection = null;
|
|
||||||
this.connected = false;
|
|
||||||
this.room = null;
|
|
||||||
this.numParticipants = 1;
|
|
||||||
this.localTracks = [];
|
|
||||||
this.remoteTracks = {};
|
|
||||||
this.maxFrameHeight = 0;
|
|
||||||
this.selectedParticipant = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple emulation of jitsi-meet's screen layout behavior
|
|
||||||
*/
|
|
||||||
updateMaxFrameHeight() {
|
|
||||||
if (!this.connected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let newMaxFrameHeight;
|
|
||||||
|
|
||||||
if (stageView) {
|
|
||||||
newMaxFrameHeight = 2160;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (this.numParticipants <= 2) {
|
|
||||||
newMaxFrameHeight = 720;
|
|
||||||
} else if (this.numParticipants <= 4) {
|
|
||||||
newMaxFrameHeight = 360;
|
|
||||||
} else {
|
|
||||||
this.newMaxFrameHeight = 180;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.room && this.maxFrameHeight !== newMaxFrameHeight) {
|
|
||||||
this.maxFrameHeight = newMaxFrameHeight;
|
|
||||||
this.room.setReceiverVideoConstraint(this.maxFrameHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple emulation of jitsi-meet's lastN behavior
|
|
||||||
*/
|
|
||||||
updateLastN() {
|
|
||||||
if (!this.connected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastN = typeof config.channelLastN === 'undefined' ? -1 : config.channelLastN;
|
|
||||||
|
|
||||||
const limitedLastN = limitLastN(this.numParticipants, validateLastNLimits(config.lastNLimits));
|
|
||||||
|
|
||||||
if (limitedLastN !== undefined) {
|
|
||||||
lastN = lastN === -1 ? limitedLastN : Math.min(limitedLastN, lastN);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastN === this.room.getLastN()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.room.setLastN(lastN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to query whether a participant ID is a valid ID
|
|
||||||
* for stage view.
|
|
||||||
*/
|
|
||||||
isValidStageViewParticipant(id) {
|
|
||||||
return (id !== room.myUserId() && room.getParticipantById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple emulation of jitsi-meet's stage view participant selection behavior.
|
|
||||||
* Doesn't take into account pinning or screen sharing, and the initial behavior
|
|
||||||
* is slightly different.
|
|
||||||
* @returns Whether the selected participant changed.
|
|
||||||
*/
|
|
||||||
selectStageViewParticipant(selected, previous) {
|
|
||||||
let newSelectedParticipant;
|
|
||||||
|
|
||||||
if (this.isValidStageViewParticipant(selected)) {
|
|
||||||
newSelectedParticipant = selected;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newSelectedParticipant = previous.find(isValidStageViewParticipant);
|
|
||||||
}
|
|
||||||
if (newSelectedParticipant && newSelectedParticipant !== this.selectedParticipant) {
|
|
||||||
this.selectedParticipant = newSelectedParticipant;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple emulation of jitsi-meet's selectParticipants behavior
|
|
||||||
*/
|
|
||||||
selectParticipants() {
|
|
||||||
if (!this.connected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stageView) {
|
|
||||||
if (this.selectedParticipant) {
|
|
||||||
this.room.selectParticipants([this.selectedParticipant]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* jitsi-meet's current Tile View behavior. */
|
|
||||||
const ids = this.room.getParticipants().map(participant => participant.getId());
|
|
||||||
this.room.selectParticipants(ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when number of participants changes.
|
|
||||||
*/
|
|
||||||
setNumberOfParticipants() {
|
|
||||||
if (this.id === 0) {
|
|
||||||
$('#participants').text(this.numParticipants);
|
|
||||||
}
|
|
||||||
if (!stageView) {
|
|
||||||
this.selectParticipants();
|
|
||||||
this.updateMaxFrameHeight();
|
|
||||||
}
|
|
||||||
this.updateLastN();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when ICE connects
|
|
||||||
*/
|
|
||||||
onConnectionEstablished() {
|
|
||||||
this.connected = true;
|
|
||||||
|
|
||||||
this.selectParticipants();
|
|
||||||
this.updateMaxFrameHeight();
|
|
||||||
this.updateLastN();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles dominant speaker changed.
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
onDominantSpeakerChanged(selected, previous) {
|
|
||||||
if (this.selectStageViewParticipant(selected, previous)) {
|
|
||||||
this.selectParticipants();
|
|
||||||
}
|
|
||||||
this.updateMaxFrameHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles local tracks.
|
|
||||||
* @param tracks Array with JitsiTrack objects
|
|
||||||
*/
|
|
||||||
onLocalTracks(tracks = []) {
|
|
||||||
this.localTracks = tracks;
|
|
||||||
for (let i = 0; i < this.localTracks.length; i++) {
|
|
||||||
if (this.localTracks[i].getType() === 'video') {
|
|
||||||
if (this.id === 0) {
|
|
||||||
$('body').append(`<video ${autoPlayVideo ? 'autoplay="1" ' : ''}id='localVideo${i}' />`);
|
|
||||||
this.localTracks[i].attach($(`#localVideo${i}`)[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.room.addTrack(this.localTracks[i]);
|
|
||||||
} else {
|
|
||||||
if (localAudio) {
|
|
||||||
this.room.addTrack(this.localTracks[i]);
|
|
||||||
} else {
|
|
||||||
this.localTracks[i].mute();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.id === 0) {
|
|
||||||
$('body').append(
|
|
||||||
`<audio autoplay='1' muted='true' id='localAudio${i}' />`);
|
|
||||||
this.localTracks[i].attach($(`#localAudio${i}`)[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles remote tracks
|
|
||||||
* @param track JitsiTrack object
|
|
||||||
*/
|
|
||||||
onRemoteTrack(track) {
|
|
||||||
if (track.isLocal()
|
|
||||||
|| (track.getType() === 'video' && !remoteVideo) || (track.getType() === 'audio' && !remoteAudio)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const participant = track.getParticipantId();
|
|
||||||
|
|
||||||
if (!this.remoteTracks[participant]) {
|
|
||||||
this.remoteTracks[participant] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.id !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const idx = this.remoteTracks[participant].push(track);
|
|
||||||
const id = participant + track.getType() + idx;
|
|
||||||
|
|
||||||
if (track.getType() === 'video') {
|
|
||||||
$('body').append(`<video autoplay='1' id='${id}' />`);
|
|
||||||
} else {
|
|
||||||
$('body').append(`<audio autoplay='1' id='${id}' />`);
|
|
||||||
}
|
|
||||||
track.attach($(`#${id}`)[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* That function is executed when the conference is joined
|
|
||||||
*/
|
|
||||||
onConferenceJoined() {
|
|
||||||
console.log(`Participant ${this.id} Conference joined`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles start muted events, when audio and/or video are muted due to
|
|
||||||
* startAudioMuted or startVideoMuted policy.
|
|
||||||
*/
|
|
||||||
onStartMuted() {
|
|
||||||
// Give it some time, as it may be currently in the process of muting
|
|
||||||
setTimeout(() => {
|
|
||||||
const localAudioTrack = this.room.getLocalAudioTrack();
|
|
||||||
|
|
||||||
if (localAudio && localAudioTrack && localAudioTrack.isMuted()) {
|
|
||||||
localAudioTrack.unmute();
|
|
||||||
}
|
|
||||||
|
|
||||||
const localVideoTrack = this.room.getLocalVideoTrack();
|
|
||||||
|
|
||||||
if (localVideo && localVideoTrack && localVideoTrack.isMuted()) {
|
|
||||||
localVideoTrack.unmute();
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
onUserJoined(id) {
|
|
||||||
this.numParticipants++;
|
|
||||||
this.setNumberOfParticipants();
|
|
||||||
this.remoteTracks[id] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
onUserLeft(id) {
|
|
||||||
this.numParticipants--;
|
|
||||||
this.setNumberOfParticipants();
|
|
||||||
if (!this.remoteTracks[id]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.id !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tracks = this.remoteTracks[id];
|
|
||||||
|
|
||||||
for (let i = 0; i < tracks.length; i++) {
|
|
||||||
const container = $(`#${id}${tracks[i].getType()}${i + 1}`)[0];
|
|
||||||
|
|
||||||
if (container) {
|
|
||||||
tracks[i].detach(container);
|
|
||||||
container.parentElement.removeChild(container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles private messages.
|
|
||||||
*
|
|
||||||
* @param {string} id - The sender ID.
|
|
||||||
* @param {string} text - The message.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onPrivateMessage(id, text) {
|
|
||||||
switch (text) {
|
|
||||||
case 'video on':
|
|
||||||
this.onVideoOnMessage();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles 'video on' private messages.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onVideoOnMessage() {
|
|
||||||
console.debug(`Participant ${this.id}: Turning my video on!`);
|
|
||||||
|
|
||||||
const localVideoTrack = this.room.getLocalVideoTrack();
|
|
||||||
|
|
||||||
if (localVideoTrack && localVideoTrack.isMuted()) {
|
|
||||||
console.debug(`Participant ${this.id}: Unmuting existing video track.`);
|
|
||||||
localVideoTrack.unmute();
|
|
||||||
} else if (!localVideoTrack) {
|
|
||||||
JitsiMeetJS.createLocalTracks({ devices: ['video'] })
|
|
||||||
.then(([videoTrack]) => videoTrack)
|
|
||||||
.catch(console.error)
|
|
||||||
.then(videoTrack => {
|
|
||||||
return this.room.replaceTrack(null, videoTrack);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
console.debug(`Participant ${this.id}: Successfully added a new video track for unmute.`);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(`Participant ${this.id}: No-op! We are already video unmuted!`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called to connect.
|
|
||||||
*/
|
|
||||||
connect() {
|
|
||||||
this._onConnectionSuccess = this.onConnectionSuccess.bind(this)
|
|
||||||
this._onConnectionFailed = this.onConnectionFailed.bind(this)
|
|
||||||
this._disconnect = this.disconnect.bind(this)
|
|
||||||
|
|
||||||
this.connection = new JitsiMeetJS.JitsiConnection(null, null, config);
|
|
||||||
this.connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, this._onConnectionSuccess);
|
|
||||||
this.connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, this._onConnectionFailed);
|
|
||||||
this.connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, this._disconnect);
|
|
||||||
this.connection.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* That function is called when connection is established successfully
|
|
||||||
*/
|
|
||||||
onConnectionSuccess() {
|
|
||||||
this.room = this.connection.initJitsiConference(roomName.toLowerCase(), config);
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.STARTED_MUTED, this.onStartMuted.bind(this));
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.TRACK_ADDED, this.onRemoteTrack.bind(this));
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, this.onConferenceJoined.bind(this));
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.CONNECTION_ESTABLISHED, this.onConnectionEstablished.bind(this));
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.USER_JOINED, this.onUserJoined.bind(this));
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.USER_LEFT, this.onUserLeft.bind(this));
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.PRIVATE_MESSAGE_RECEIVED, this.onPrivateMessage.bind(this));
|
|
||||||
if (stageView) {
|
|
||||||
this.room.on(JitsiMeetJS.events.conference.DOMINANT_SPEAKER_CHANGED, this.onDominantSpeakerChanged.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
const devices = [];
|
|
||||||
|
|
||||||
if (localVideo) {
|
|
||||||
devices.push('video');
|
|
||||||
}
|
|
||||||
|
|
||||||
// we always create audio local tracks
|
|
||||||
devices.push('audio');
|
|
||||||
|
|
||||||
if (devices.length > 0) {
|
|
||||||
JitsiMeetJS.createLocalTracks({ devices })
|
|
||||||
.then(this.onLocalTracks.bind(this))
|
|
||||||
.then(() => {
|
|
||||||
this.room.join();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.room.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateMaxFrameHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called when the connection fail.
|
|
||||||
*/
|
|
||||||
onConnectionFailed() {
|
|
||||||
console.error(`Participant ${this.id}: Connection Failed!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called when we disconnect.
|
|
||||||
*/
|
|
||||||
disconnect() {
|
|
||||||
console.log('disconnect!');
|
|
||||||
this.connection.removeEventListener(
|
|
||||||
JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
|
|
||||||
this._onConnectionSuccess);
|
|
||||||
this.connection.removeEventListener(
|
|
||||||
JitsiMeetJS.events.connection.CONNECTION_FAILED,
|
|
||||||
this._onConnectionFailed);
|
|
||||||
this.connection.removeEventListener(
|
|
||||||
JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
|
|
||||||
this._disconnect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let clients = [];
|
|
||||||
|
|
||||||
window.APP = {
|
|
||||||
conference: {
|
|
||||||
getStats() {
|
|
||||||
return clients[0]?.room?.connectionQuality.getStats();
|
|
||||||
},
|
|
||||||
getConnectionState() {
|
|
||||||
return clients[0] && clients[0].room && room.getConnectionState();
|
|
||||||
},
|
|
||||||
muteAudio(mute) {
|
|
||||||
localAudio = mute;
|
|
||||||
for (let j = 0; j < clients.length; j++) {
|
|
||||||
for (let i = 0; i < clients[j].localTracks.length; i++) {
|
|
||||||
if (clients[j].localTracks[i].getType() === 'audio') {
|
|
||||||
if (mute) {
|
|
||||||
clients[j].localTracks[i].mute();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
clients[j].localTracks[i].unmute();
|
|
||||||
|
|
||||||
// if track was not added we need to add it to the peerconnection
|
|
||||||
if (!clients[j].room.getLocalAudioTrack()) {
|
|
||||||
clients[j].room.replaceTrack(null, clients[j].localTracks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get room() {
|
|
||||||
return clients[0]?.room;
|
|
||||||
},
|
|
||||||
get connection() {
|
|
||||||
return clients[0]?.connection;
|
|
||||||
},
|
|
||||||
get numParticipants() {
|
|
||||||
return clients[0]?.remoteParticipants;
|
|
||||||
},
|
|
||||||
get localTracks() {
|
|
||||||
return clients[0]?.localTracks;
|
|
||||||
},
|
|
||||||
get remoteTracks() {
|
|
||||||
return clients[0]?.remoteTracks;
|
|
||||||
},
|
|
||||||
get params() {
|
|
||||||
return {
|
|
||||||
roomName,
|
|
||||||
localAudio,
|
|
||||||
localVideo,
|
|
||||||
remoteVideo,
|
|
||||||
remoteAudio,
|
|
||||||
autoPlayVideo,
|
|
||||||
stageView
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function unload() {
|
|
||||||
for (let j = 0; j < clients.length; j++) {
|
|
||||||
for (let i = 0; i < clients[j].localTracks.length; i++) {
|
|
||||||
clients[j].localTracks[i].dispose();
|
|
||||||
}
|
|
||||||
clients[j].room.leave();
|
|
||||||
clients[j].connection.disconnect();
|
|
||||||
}
|
|
||||||
clients = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).bind('beforeunload', unload);
|
|
||||||
$(window).bind('unload', unload);
|
|
||||||
|
|
||||||
JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
|
|
||||||
|
|
||||||
JitsiMeetJS.init(config);
|
|
||||||
|
|
||||||
config.serviceUrl = config.bosh = `${config.websocket || config.bosh}?room=${roomName.toLowerCase()}`;
|
|
||||||
if (config.websocketKeepAliveUrl) {
|
|
||||||
config.websocketKeepAliveUrl += `?room=${roomName.toLowerCase()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startClient(i) {
|
|
||||||
clients[i] = new LoadTestClient(i);
|
|
||||||
clients[i].connect();
|
|
||||||
if (i + 1 < numClients) {
|
|
||||||
setTimeout(() => { startClient(i+1) }, clientInterval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numClients > 0) {
|
|
||||||
startClient(0)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,56 +0,0 @@
|
||||||
{
|
|
||||||
"name": "jitsi-meet-load-test",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "A load test participant",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/jitsi/jitsi-meet"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"jingle",
|
|
||||||
"webrtc",
|
|
||||||
"xmpp",
|
|
||||||
"browser"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"readmeFilename": "../README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"jquery": "3.5.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "7.5.5",
|
|
||||||
"@babel/plugin-proposal-class-properties": "7.1.0",
|
|
||||||
"@babel/plugin-proposal-export-default-from": "7.0.0",
|
|
||||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0",
|
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.4.4",
|
|
||||||
"@babel/plugin-proposal-optional-chaining": "7.2.0",
|
|
||||||
"@babel/plugin-transform-flow-strip-types": "7.0.0",
|
|
||||||
"@babel/preset-env": "7.1.0",
|
|
||||||
"@babel/preset-flow": "7.0.0",
|
|
||||||
"@babel/runtime": "7.5.5",
|
|
||||||
"babel-eslint": "10.0.1",
|
|
||||||
"babel-loader": "8.0.4",
|
|
||||||
"eslint": "5.6.1",
|
|
||||||
"eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#1.0.3",
|
|
||||||
"eslint-plugin-flowtype": "2.50.3",
|
|
||||||
"eslint-plugin-import": "2.20.2",
|
|
||||||
"eslint-plugin-jsdoc": "3.8.0",
|
|
||||||
"expose-loader": "0.7.5",
|
|
||||||
"flow-bin": "0.104.0",
|
|
||||||
"imports-loader": "0.7.1",
|
|
||||||
"lodash": "4.17.21",
|
|
||||||
"string-replace-loader": "2.1.1",
|
|
||||||
"style-loader": "0.19.0",
|
|
||||||
"webpack": "4.43.0",
|
|
||||||
"webpack-bundle-analyzer": "3.4.1",
|
|
||||||
"webpack-cli": "3.3.11"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.0.0",
|
|
||||||
"npm": ">=6.0.0"
|
|
||||||
},
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack -p"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
/* global __dirname */
|
|
||||||
|
|
||||||
const process = require('process');
|
|
||||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
|
||||||
const analyzeBundle = process.argv.indexOf('--analyze-bundle') !== -1;
|
|
||||||
const minimize
|
|
||||||
= process.argv.indexOf('-p') !== -1
|
|
||||||
|| process.argv.indexOf('--optimize-minimize') !== -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a Performance configuration object for the given size.
|
|
||||||
* See: https://webpack.js.org/configuration/performance/
|
|
||||||
*/
|
|
||||||
function getPerformanceHints(size) {
|
|
||||||
return {
|
|
||||||
hints: minimize ? 'error' : false,
|
|
||||||
maxAssetSize: size,
|
|
||||||
maxEntrypointSize: size
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// The base Webpack configuration to bundle the JavaScript artifacts of
|
|
||||||
// jitsi-meet such as app.bundle.js and external_api.js.
|
|
||||||
const config = {
|
|
||||||
devtool: 'source-map',
|
|
||||||
mode: minimize ? 'production' : 'development',
|
|
||||||
module: {
|
|
||||||
rules: [ {
|
|
||||||
// Transpile ES2015 (aka ES6) to ES5. Accept the JSX syntax by React
|
|
||||||
// as well.
|
|
||||||
|
|
||||||
exclude: [
|
|
||||||
new RegExp(`${__dirname}/node_modules/(?!js-utils)`)
|
|
||||||
],
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options: {
|
|
||||||
// XXX The require.resolve bellow solves failures to locate the
|
|
||||||
// presets when lib-jitsi-meet, for example, is npm linked in
|
|
||||||
// jitsi-meet.
|
|
||||||
plugins: [
|
|
||||||
require.resolve('@babel/plugin-transform-flow-strip-types'),
|
|
||||||
require.resolve('@babel/plugin-proposal-class-properties'),
|
|
||||||
require.resolve('@babel/plugin-proposal-export-default-from'),
|
|
||||||
require.resolve('@babel/plugin-proposal-export-namespace-from'),
|
|
||||||
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
|
|
||||||
require.resolve('@babel/plugin-proposal-optional-chaining')
|
|
||||||
],
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
require.resolve('@babel/preset-env'),
|
|
||||||
|
|
||||||
// Tell babel to avoid compiling imports into CommonJS
|
|
||||||
// so that webpack may do tree shaking.
|
|
||||||
{
|
|
||||||
modules: false,
|
|
||||||
|
|
||||||
// Specify our target browsers so no transpiling is
|
|
||||||
// done unnecessarily. For browsers not specified
|
|
||||||
// here, the ES2015+ profile will be used.
|
|
||||||
targets: {
|
|
||||||
chrome: 58,
|
|
||||||
electron: 2,
|
|
||||||
firefox: 54,
|
|
||||||
safari: 11
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
],
|
|
||||||
require.resolve('@babel/preset-flow'),
|
|
||||||
require.resolve('@babel/preset-react')
|
|
||||||
]
|
|
||||||
},
|
|
||||||
test: /\.jsx?$/
|
|
||||||
}, {
|
|
||||||
// Expose jquery as the globals $ and jQuery because it is expected
|
|
||||||
// to be available in such a form by multiple jitsi-meet
|
|
||||||
// dependencies including lib-jitsi-meet.
|
|
||||||
|
|
||||||
loader: 'expose-loader?$!expose-loader?jQuery',
|
|
||||||
test: /\/node_modules\/jquery\/.*\.js$/
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
// Allow the use of the real filename of the module being executed. By
|
|
||||||
// default Webpack does not leak path-related information and provides a
|
|
||||||
// value that is a mock (/index.js).
|
|
||||||
__filename: true
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
concatenateModules: minimize,
|
|
||||||
minimize
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: `[name]${minimize ? '.min' : ''}.js`,
|
|
||||||
path: `${__dirname}/libs`,
|
|
||||||
publicPath: 'load-test/libs/',
|
|
||||||
sourceMapFilename: `[name].${minimize ? 'min' : 'js'}.map`
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
analyzeBundle
|
|
||||||
&& new BundleAnalyzerPlugin({
|
|
||||||
analyzerMode: 'disabled',
|
|
||||||
generateStatsFile: true
|
|
||||||
})
|
|
||||||
].filter(Boolean),
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
jquery: `jquery/dist/jquery${minimize ? '.min' : ''}.js`
|
|
||||||
},
|
|
||||||
aliasFields: [
|
|
||||||
'browser'
|
|
||||||
],
|
|
||||||
extensions: [
|
|
||||||
'.web.js',
|
|
||||||
|
|
||||||
// Webpack defaults:
|
|
||||||
'.js',
|
|
||||||
'.json'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = [
|
|
||||||
Object.assign({}, config, {
|
|
||||||
entry: {
|
|
||||||
'load-test-participant': './load-test-participant.js'
|
|
||||||
},
|
|
||||||
performance: getPerformanceHints(3 * 1024 * 1024)
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
Loading…
Reference in New Issue