@vpalmisano/webrtcperf 4.2.2 → 4.3.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/app.min.js +1 -1
- package/build/src/app.d.ts +13 -4
- package/build/src/app.js +145 -119
- package/build/src/app.js.map +1 -1
- package/build/src/config.d.ts +1 -1
- package/build/src/config.js +30 -24
- package/build/src/config.js.map +1 -1
- package/build/src/server.js +28 -10
- package/build/src/server.js.map +1 -1
- package/build/src/stats.d.ts +8 -21
- package/build/src/stats.js +48 -17
- package/build/src/stats.js.map +1 -1
- package/build/src/utils.d.ts +1 -1
- package/build/src/utils.js +9 -10
- package/build/src/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/app.ts +154 -130
- package/src/config.ts +28 -23
- package/src/server.ts +33 -13
- package/src/stats.ts +56 -25
- package/src/utils.ts +10 -12
package/src/app.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { calculateVisqolScore } from './visqol'
|
|
|
23
23
|
import { calculateVmafScore, convertToIvf, prepareVideo } from './vmaf'
|
|
24
24
|
import path from 'path'
|
|
25
25
|
import { markedTerminal } from 'marked-terminal'
|
|
26
|
+
import { EventEmitter } from 'events'
|
|
26
27
|
|
|
27
28
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
29
|
const { marked } = require('marked')
|
|
@@ -56,141 +57,167 @@ Default value: \`${value.default}\`
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
export class Application extends EventEmitter {
|
|
61
|
+
readonly config: Config
|
|
62
|
+
readonly stats: Stats
|
|
63
|
+
readonly server?: Server
|
|
64
|
+
private mediaPaths: MediaPath[] = []
|
|
65
|
+
|
|
66
|
+
constructor(config: Config) {
|
|
67
|
+
super()
|
|
68
|
+
if (!config.startTimestamp) {
|
|
69
|
+
config.startTimestamp = Date.now()
|
|
70
|
+
}
|
|
71
|
+
this.config = config
|
|
72
|
+
this.stats = new Stats(config)
|
|
73
|
+
if (config.serverPort) {
|
|
74
|
+
this.server = new Server(config, this.stats)
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
await
|
|
75
|
-
} catch (err: unknown) {
|
|
76
|
-
log.error(`visqol score error: ${(err as Error).stack}`)
|
|
78
|
+
async start() {
|
|
79
|
+
log.debug(`start (runDuration: ${this.config.runDuration})`)
|
|
80
|
+
await this.stats.start()
|
|
81
|
+
if (this.server) {
|
|
82
|
+
await this.server.start()
|
|
77
83
|
}
|
|
78
|
-
|
|
79
|
-
}
|
|
84
|
+
const config = this.config
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
// Handle vmaf commands.
|
|
87
|
+
if (config.vmafPrepareVideo) {
|
|
88
|
+
await prepareVideo(config, true)
|
|
89
|
+
}
|
|
90
|
+
if (config.vmafProcessVideo) {
|
|
91
|
+
await convertToIvf(
|
|
92
|
+
config.vmafProcessVideo,
|
|
93
|
+
config.vmafVideoCrop,
|
|
94
|
+
config.vmafKeepSourceFiles,
|
|
95
|
+
config.vmafSkipDuplicated,
|
|
96
|
+
)
|
|
97
|
+
}
|
|
85
98
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
// Handle sessions.
|
|
100
|
+
if (config.sessions > 0) {
|
|
101
|
+
// Prepare fake video and audio.
|
|
102
|
+
if (config.videoPath && !this.mediaPaths.length) {
|
|
103
|
+
for (const videoPath of config.videoPath.split(',')) {
|
|
104
|
+
const ret = await prepareFakeMedia({ ...config, videoPath })
|
|
105
|
+
this.mediaPaths.push(ret)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
89
108
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
await server.start()
|
|
95
|
-
}
|
|
109
|
+
// Network throttle.
|
|
110
|
+
if (config.throttleConfig) {
|
|
111
|
+
await startThrottle(config.throttleConfig)
|
|
112
|
+
}
|
|
96
113
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const mediaPaths: MediaPath[] = []
|
|
101
|
-
if (config.videoPath) {
|
|
102
|
-
for (const videoPath of config.videoPath.split(',')) {
|
|
103
|
-
const ret = await prepareFakeMedia({ ...config, videoPath })
|
|
104
|
-
mediaPaths.push(ret)
|
|
114
|
+
// Download browser if necessary.
|
|
115
|
+
if (!config.chromiumUrl && !config.chromiumPath) {
|
|
116
|
+
await checkChromeExecutable()
|
|
105
117
|
}
|
|
106
|
-
}
|
|
107
118
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
// Start the local sessions.
|
|
120
|
+
if (config.randomAudioPeriod) {
|
|
121
|
+
startRandomActivateAudio(
|
|
122
|
+
this.stats.sessions,
|
|
123
|
+
config.randomAudioPeriod,
|
|
124
|
+
config.randomAudioProbability,
|
|
125
|
+
config.randomAudioRange,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
const spawnPeriod = 1000 / config.spawnRate
|
|
129
|
+
log.debug(`Starting ${config.sessions} sessions (spawnPeriod: ${spawnPeriod}ms)`)
|
|
130
|
+
const startTime = Date.now()
|
|
131
|
+
for (let i = 0; i < config.sessions; i += 1) {
|
|
132
|
+
const id = this.stats.consumeSessionId(config.tabsPerSession)
|
|
133
|
+
await this.startSession(id, spawnPeriod)
|
|
134
|
+
// If not the last session, sleep.
|
|
135
|
+
if (i < config.sessions - 1) {
|
|
136
|
+
await sleep(spawnPeriod)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000)
|
|
140
|
+
const spawnRate = (config.sessions * config.tabsPerSession) / elapsed
|
|
141
|
+
log.debug(`${config.sessions * config.tabsPerSession} pages started in ${elapsed}s (${spawnRate.toFixed(2)}/s)`)
|
|
111
142
|
}
|
|
112
143
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
await checkChromeExecutable()
|
|
144
|
+
if (config.runDuration || config.vmafPath || config.visqolPath) {
|
|
145
|
+
setTimeout(() => this.stop(), config.runDuration * 1000)
|
|
116
146
|
}
|
|
147
|
+
}
|
|
117
148
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
private async startSession(id: number, spawnPeriod: number) {
|
|
150
|
+
log.debug(`startSession ${id}`)
|
|
151
|
+
const throttleIndex = getSessionThrottleIndex(id)
|
|
152
|
+
const mediaPath = this.mediaPaths.length ? this.mediaPaths[id % this.mediaPaths.length] : undefined
|
|
153
|
+
const session = new Session({
|
|
154
|
+
...this.config,
|
|
155
|
+
mediaPath,
|
|
156
|
+
spawnPeriod,
|
|
157
|
+
id,
|
|
158
|
+
throttleIndex,
|
|
159
|
+
})
|
|
160
|
+
session.once('stop', () => {
|
|
161
|
+
console.warn(`Session ${id} stopped, reloading...`)
|
|
162
|
+
setTimeout(() => this.startSession(id, spawnPeriod), spawnPeriod)
|
|
163
|
+
})
|
|
164
|
+
this.stats.addSession(session)
|
|
165
|
+
await session.start()
|
|
166
|
+
}
|
|
136
167
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
private async postTest() {
|
|
169
|
+
log.debug('postTest')
|
|
170
|
+
|
|
171
|
+
// vmaf score.
|
|
172
|
+
if (this.config.vmafPath) {
|
|
173
|
+
console.log('Calculating VMAF score...')
|
|
174
|
+
try {
|
|
175
|
+
await calculateVmafScore(this.config)
|
|
176
|
+
} catch (err: unknown) {
|
|
177
|
+
log.error(`vmaf score error: ${(err as Error).stack}`)
|
|
178
|
+
}
|
|
145
179
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
await sleep(spawnPeriod)
|
|
180
|
+
|
|
181
|
+
// visqol score
|
|
182
|
+
if (this.config.visqolPath) {
|
|
183
|
+
console.log('Calculating Visqol score...')
|
|
184
|
+
try {
|
|
185
|
+
await calculateVisqolScore(this.config)
|
|
186
|
+
} catch (err: unknown) {
|
|
187
|
+
log.error(`visqol score error: ${(err as Error).stack}`)
|
|
155
188
|
}
|
|
156
189
|
}
|
|
157
|
-
const elapsed = Math.round((Date.now() - startTime) / 1000)
|
|
158
|
-
const spawnRate = (config.sessions * config.tabsPerSession) / elapsed
|
|
159
|
-
log.debug(`${config.sessions * config.tabsPerSession} pages started in ${elapsed}s (${spawnRate.toFixed(2)}/s)`)
|
|
160
190
|
}
|
|
161
191
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
stop: async (): Promise<void> => {
|
|
165
|
-
log.debug('Stopping')
|
|
192
|
+
async stop(canceled = false) {
|
|
193
|
+
log.debug(`stop (canceled: ${canceled})`)
|
|
166
194
|
|
|
167
|
-
|
|
195
|
+
stopRandomActivateAudio()
|
|
168
196
|
|
|
169
|
-
|
|
197
|
+
await this.stats.stop()
|
|
170
198
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
199
|
+
if (this.config.throttleConfig) {
|
|
200
|
+
await stopThrottle()
|
|
201
|
+
}
|
|
174
202
|
|
|
175
|
-
|
|
203
|
+
stopTimers()
|
|
176
204
|
|
|
177
|
-
|
|
205
|
+
await this.postTest()
|
|
178
206
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
207
|
+
// Copy docker logs to data directory.
|
|
208
|
+
if (this.config.pageLogPath) {
|
|
209
|
+
try {
|
|
210
|
+
const logPath = await getDockerLogsPath()
|
|
211
|
+
const dataDir = path.dirname(this.config.pageLogPath)
|
|
212
|
+
await fs.promises.cp(logPath, path.resolve(dataDir, 'docker.log'))
|
|
213
|
+
} catch (err: unknown) {
|
|
214
|
+
log.debug(`docker logs not found: ${(err as Error).message}`)
|
|
188
215
|
}
|
|
216
|
+
}
|
|
189
217
|
|
|
190
|
-
|
|
218
|
+
this.server?.stop()
|
|
191
219
|
|
|
192
|
-
|
|
193
|
-
},
|
|
220
|
+
this.emit('stop', canceled)
|
|
194
221
|
}
|
|
195
222
|
}
|
|
196
223
|
|
|
@@ -200,7 +227,7 @@ export async function setupApplication(config: Config): Promise<{ stats: Stats;
|
|
|
200
227
|
async function main(): Promise<void> {
|
|
201
228
|
showHelpOrVersion()
|
|
202
229
|
|
|
203
|
-
let
|
|
230
|
+
let configs: Config[]
|
|
204
231
|
|
|
205
232
|
if (process.argv.slice(2).includes('--prompt')) {
|
|
206
233
|
const params = await loadConfigFromPrompt(
|
|
@@ -213,39 +240,36 @@ async function main(): Promise<void> {
|
|
|
213
240
|
console.log(json5.stringify(params, null, 2))
|
|
214
241
|
process.exit(0)
|
|
215
242
|
}
|
|
216
|
-
|
|
243
|
+
configs = await loadConfig(undefined, params)
|
|
217
244
|
} else {
|
|
218
|
-
|
|
245
|
+
configs = await loadConfig(process.argv[2])
|
|
219
246
|
}
|
|
220
247
|
|
|
221
|
-
if (
|
|
222
|
-
await prepareVideo(config, true)
|
|
223
|
-
process.exit(0)
|
|
224
|
-
}
|
|
248
|
+
if (!configs.length) throw new Error('No configuration found')
|
|
225
249
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
config.vmafVideoCrop,
|
|
230
|
-
config.vmafKeepSourceFiles,
|
|
231
|
-
config.vmafSkipDuplicated,
|
|
232
|
-
)
|
|
233
|
-
process.exit(0)
|
|
234
|
-
}
|
|
250
|
+
let application: Application
|
|
251
|
+
const runNext = () => {
|
|
252
|
+
const config = configs.splice(0, 1)[0]
|
|
235
253
|
|
|
236
|
-
|
|
254
|
+
application = new Application(config)
|
|
255
|
+
application.once('stop', canceled => {
|
|
256
|
+
if (!canceled && configs.length) {
|
|
257
|
+
log.info(`Application stopped, running next (${configs.length} left)...`)
|
|
258
|
+
runNext()
|
|
259
|
+
} else {
|
|
260
|
+
process.exit(0)
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
return application.start()
|
|
264
|
+
}
|
|
237
265
|
|
|
238
|
-
const stop = async ()
|
|
266
|
+
const stop = async () => {
|
|
239
267
|
console.log('Exiting...')
|
|
240
|
-
|
|
241
|
-
await stopApplication()
|
|
242
|
-
|
|
243
|
-
process.exit(0)
|
|
268
|
+
await application.stop(true)
|
|
244
269
|
}
|
|
245
270
|
registerExitHandler(() => stop())
|
|
246
271
|
|
|
247
|
-
|
|
248
|
-
setTimeout(stop, config.runDuration * 1000)
|
|
272
|
+
await runNext()
|
|
249
273
|
|
|
250
274
|
// Command line interface.
|
|
251
275
|
if (process.stdin && process.stdin.setRawMode) {
|
package/src/config.ts
CHANGED
|
@@ -6,6 +6,7 @@ import path, { join } from 'path'
|
|
|
6
6
|
import json5 from 'json5'
|
|
7
7
|
import yaml from 'yaml'
|
|
8
8
|
import toml from 'toml'
|
|
9
|
+
import fs from 'fs'
|
|
9
10
|
|
|
10
11
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
11
12
|
const puppeteer = require('puppeteer-core')
|
|
@@ -56,8 +57,7 @@ convict.addParser([
|
|
|
56
57
|
{ extension: 'toml', parse: toml.parse },
|
|
57
58
|
])
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
const configSchema = convict({
|
|
60
|
+
const configSchema = {
|
|
61
61
|
url: {
|
|
62
62
|
doc: `The page url to load.`,
|
|
63
63
|
format: String,
|
|
@@ -859,7 +859,7 @@ E.g. \`{ w: "iw-10", h: "ih-5", x: "10", y: '5' }\``,
|
|
|
859
859
|
env: 'VISQOL_KEEP_SOURCE_FILES',
|
|
860
860
|
arg: 'visqol-keep-source-files',
|
|
861
861
|
},
|
|
862
|
-
}
|
|
862
|
+
}
|
|
863
863
|
|
|
864
864
|
type ConfigDocs = Record<string, { doc: string; format: string; default: string }>
|
|
865
865
|
|
|
@@ -897,10 +897,10 @@ function formatDocs(
|
|
|
897
897
|
* It returns the formatted configuration docs.
|
|
898
898
|
*/
|
|
899
899
|
export function getConfigDocs(): ConfigDocs {
|
|
900
|
-
return formatDocs({}, null, configSchema.getSchema())
|
|
900
|
+
return formatDocs({}, null, convict(configSchema).getSchema())
|
|
901
901
|
}
|
|
902
902
|
|
|
903
|
-
const _schemaProperties = configSchema.getProperties()
|
|
903
|
+
const _schemaProperties = convict(configSchema).getProperties()
|
|
904
904
|
|
|
905
905
|
/** [[include:config.md]] */
|
|
906
906
|
export type Config = typeof _schemaProperties
|
|
@@ -909,7 +909,8 @@ export type Config = typeof _schemaProperties
|
|
|
909
909
|
* Loads the config object.
|
|
910
910
|
*/
|
|
911
911
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
912
|
-
export async function loadConfig(filePath?: string, values?: any): Promise<Config> {
|
|
912
|
+
export async function loadConfig(filePath?: string, values?: any): Promise<Config[]> {
|
|
913
|
+
const configs: Config[] = []
|
|
913
914
|
if (filePath) {
|
|
914
915
|
if (filePath.startsWith('http')) {
|
|
915
916
|
log.debug(`Loading config from url: ${filePath}`)
|
|
@@ -917,41 +918,45 @@ export async function loadConfig(filePath?: string, values?: any): Promise<Confi
|
|
|
917
918
|
if (!res?.data) {
|
|
918
919
|
throw new Error(`Failed to download configuration from: ${filePath}`)
|
|
919
920
|
}
|
|
920
|
-
|
|
921
|
+
values =
|
|
921
922
|
res.contentType === 'application/x-yaml'
|
|
922
923
|
? yaml.parse(res.data)
|
|
923
924
|
: res.contentType === 'application/toml'
|
|
924
925
|
? toml.parse(res.data)
|
|
925
926
|
: json5.parse(res.data)
|
|
926
|
-
configSchema.load(values)
|
|
927
927
|
} else if (existsSync(filePath)) {
|
|
928
928
|
log.debug(`Loading config from local file: ${filePath}`)
|
|
929
929
|
if (filePath.endsWith('.js') || filePath.endsWith('.mjs')) {
|
|
930
930
|
const module = await import(/* webpackIgnore: true */ path.resolve(filePath))
|
|
931
|
-
|
|
931
|
+
values = await module.default()
|
|
932
932
|
} else {
|
|
933
|
-
|
|
933
|
+
const data = String(await fs.promises.readFile(filePath))
|
|
934
|
+
values =
|
|
935
|
+
filePath.endsWith('.yml') || filePath.endsWith('.yaml')
|
|
936
|
+
? yaml.parse(data)
|
|
937
|
+
: filePath.endsWith('.toml')
|
|
938
|
+
? toml.parse(data)
|
|
939
|
+
: json5.parse(data)
|
|
934
940
|
}
|
|
935
941
|
}
|
|
936
|
-
} else if (values) {
|
|
937
|
-
log.debug('Loading config from values.')
|
|
938
|
-
configSchema.load(values)
|
|
939
|
-
} else {
|
|
940
|
-
log.debug('Loading config from default values.')
|
|
941
|
-
configSchema.load({})
|
|
942
942
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
943
|
+
if (!Array.isArray(values)) {
|
|
944
|
+
values = [values || {}]
|
|
945
|
+
}
|
|
946
|
+
for (const value of values) {
|
|
947
|
+
const schema = convict(configSchema)
|
|
948
|
+
schema.load(value || {})
|
|
949
|
+
schema.validate({ allowed: 'strict' })
|
|
950
|
+
configs.push(schema.getProperties())
|
|
951
|
+
}
|
|
952
|
+
log.debug('Using config:', configs)
|
|
953
|
+
return configs
|
|
949
954
|
}
|
|
950
955
|
|
|
951
956
|
function getFunctionDeclaration() {
|
|
952
957
|
const properties: Record<string, { type: string; description: string; nullable?: boolean }> = {}
|
|
953
958
|
const required: string[] = []
|
|
954
|
-
const schema = configSchema.getSchema()
|
|
959
|
+
const schema = convict(configSchema).getSchema()
|
|
955
960
|
|
|
956
961
|
Object.entries(schema._cvtProperties).forEach(([name, value]) => {
|
|
957
962
|
const { format, doc, nullable } = value as SchemaObj
|
package/src/server.ts
CHANGED
|
@@ -11,10 +11,12 @@ import { WebSocketServer } from 'ws'
|
|
|
11
11
|
import zlib from 'zlib'
|
|
12
12
|
import auth from 'basic-auth'
|
|
13
13
|
|
|
14
|
-
import { loadConfig } from './config'
|
|
14
|
+
import { Config, loadConfig } from './config'
|
|
15
15
|
import { Session, SessionParams } from './session'
|
|
16
16
|
import { Stats } from './stats'
|
|
17
17
|
import { logger, runShellCommand, getDockerLogsPath } from './utils'
|
|
18
|
+
import { getSessionThrottleIndex } from '@vpalmisano/throttler'
|
|
19
|
+
import { MediaPath, prepareFakeMedia } from './media'
|
|
18
20
|
|
|
19
21
|
const log = logger('webrtcperf:server')
|
|
20
22
|
|
|
@@ -272,7 +274,8 @@ export class Server {
|
|
|
272
274
|
private async putSession(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
|
273
275
|
log.debug(`PUT /session`, req.body)
|
|
274
276
|
try {
|
|
275
|
-
const
|
|
277
|
+
const config = req.body as Config
|
|
278
|
+
const id = this.stats.consumeSessionId(config.tabsPerSession)
|
|
276
279
|
await this.startLocalSession(id, req.body)
|
|
277
280
|
res.json({
|
|
278
281
|
message: `Session created`,
|
|
@@ -293,10 +296,10 @@ export class Server {
|
|
|
293
296
|
private async putSessions(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
|
294
297
|
log.debug(`PUT /sessions`, req.body)
|
|
295
298
|
try {
|
|
296
|
-
const { sessions } = req.body
|
|
299
|
+
const { sessions, tabsPerSession } = req.body as Config
|
|
297
300
|
const sessionsIds = []
|
|
298
301
|
for (let i = 0; i < sessions; i++) {
|
|
299
|
-
const id = this.stats.
|
|
302
|
+
const id = this.stats.consumeSessionId(tabsPerSession)
|
|
300
303
|
await this.startLocalSession(id, req.body)
|
|
301
304
|
sessionsIds.push(id)
|
|
302
305
|
}
|
|
@@ -471,12 +474,25 @@ export class Server {
|
|
|
471
474
|
* @param config The session configuration.
|
|
472
475
|
*/
|
|
473
476
|
private async startLocalSession(id: number, config: SessionParams): Promise<Session> {
|
|
474
|
-
|
|
475
|
-
const sessionConfig =
|
|
476
|
-
const
|
|
477
|
+
const configs = await loadConfig(undefined, config)
|
|
478
|
+
const sessionConfig = configs[0]
|
|
479
|
+
const throttleIndex = getSessionThrottleIndex(id)
|
|
480
|
+
const spawnPeriod = 1000 / sessionConfig.spawnRate
|
|
481
|
+
|
|
482
|
+
// Prepare fake video and audio.
|
|
483
|
+
const mediaPaths: MediaPath[] = []
|
|
484
|
+
if (sessionConfig.videoPath) {
|
|
485
|
+
for (const videoPath of sessionConfig.videoPath.split(',')) {
|
|
486
|
+
const ret = await prepareFakeMedia({ ...sessionConfig, videoPath })
|
|
487
|
+
mediaPaths.push(ret)
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const mediaPath = mediaPaths.length ? mediaPaths[id % mediaPaths.length] : undefined
|
|
491
|
+
|
|
492
|
+
const session = new Session({ ...sessionConfig, throttleIndex, spawnPeriod, mediaPath, id })
|
|
477
493
|
session.once('stop', () => {
|
|
478
494
|
console.warn(`Session ${id} stopped, reloading...`)
|
|
479
|
-
setTimeout(this.startLocalSession.bind(this),
|
|
495
|
+
setTimeout(this.startLocalSession.bind(this), spawnPeriod, id, config)
|
|
480
496
|
})
|
|
481
497
|
this.stats.addSession(session)
|
|
482
498
|
try {
|
|
@@ -510,13 +526,17 @@ export class Server {
|
|
|
510
526
|
log.debug('start')
|
|
511
527
|
if (this.serverUseHttps) {
|
|
512
528
|
const destDir = path.join(os.homedir(), '.webrtcperf/ssl')
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
)
|
|
529
|
+
const keyPath = path.join(destDir, 'domain.key')
|
|
530
|
+
const crtPath = path.join(destDir, 'domain.crt')
|
|
531
|
+
if (!fs.existsSync(keyPath) || !fs.existsSync(crtPath)) {
|
|
532
|
+
await runShellCommand(
|
|
533
|
+
`mkdir -p ${destDir} && openssl req -newkey rsa:2048 -nodes -keyout ${keyPath} -x509 -days 365 -out ${crtPath} -subj "/C=EU/ST=London/L=London/O=Global Security/OU=IT Department/CN=example.com"`,
|
|
534
|
+
)
|
|
535
|
+
}
|
|
516
536
|
this.server = _createServer(
|
|
517
537
|
{
|
|
518
|
-
key: fs.readFileSync(
|
|
519
|
-
cert: fs.readFileSync(
|
|
538
|
+
key: fs.readFileSync(keyPath),
|
|
539
|
+
cert: fs.readFileSync(crtPath),
|
|
520
540
|
},
|
|
521
541
|
this.app,
|
|
522
542
|
)
|