@vpalmisano/webrtcperf 4.0.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.
Files changed (53) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +296 -0
  3. package/app.min.js +2 -0
  4. package/build/src/app.d.ts +6 -0
  5. package/build/src/app.js +207 -0
  6. package/build/src/app.js.map +1 -0
  7. package/build/src/config.d.ts +104 -0
  8. package/build/src/config.js +880 -0
  9. package/build/src/config.js.map +1 -0
  10. package/build/src/generate-config-docs.d.ts +1 -0
  11. package/build/src/generate-config-docs.js +41 -0
  12. package/build/src/generate-config-docs.js.map +1 -0
  13. package/build/src/index.d.ts +9 -0
  14. package/build/src/index.js +26 -0
  15. package/build/src/index.js.map +1 -0
  16. package/build/src/media.d.ts +33 -0
  17. package/build/src/media.js +113 -0
  18. package/build/src/media.js.map +1 -0
  19. package/build/src/rtcstats.d.ts +302 -0
  20. package/build/src/rtcstats.js +418 -0
  21. package/build/src/rtcstats.js.map +1 -0
  22. package/build/src/server.d.ts +173 -0
  23. package/build/src/server.js +639 -0
  24. package/build/src/server.js.map +1 -0
  25. package/build/src/session.d.ts +277 -0
  26. package/build/src/session.js +1552 -0
  27. package/build/src/session.js.map +1 -0
  28. package/build/src/stats.d.ts +243 -0
  29. package/build/src/stats.js +1383 -0
  30. package/build/src/stats.js.map +1 -0
  31. package/build/src/utils.d.ts +249 -0
  32. package/build/src/utils.js +1220 -0
  33. package/build/src/utils.js.map +1 -0
  34. package/build/src/visqol.d.ts +6 -0
  35. package/build/src/visqol.js +61 -0
  36. package/build/src/visqol.js.map +1 -0
  37. package/build/src/vmaf.d.ts +83 -0
  38. package/build/src/vmaf.js +624 -0
  39. package/build/src/vmaf.js.map +1 -0
  40. package/build/tsconfig.tsbuildinfo +1 -0
  41. package/package.json +129 -0
  42. package/src/app.ts +241 -0
  43. package/src/config.ts +852 -0
  44. package/src/generate-config-docs.ts +47 -0
  45. package/src/index.ts +9 -0
  46. package/src/media.ts +151 -0
  47. package/src/rtcstats.ts +507 -0
  48. package/src/server.ts +645 -0
  49. package/src/session.ts +1908 -0
  50. package/src/stats.ts +1668 -0
  51. package/src/utils.ts +1295 -0
  52. package/src/visqol.ts +62 -0
  53. package/src/vmaf.ts +771 -0
package/src/app.ts ADDED
@@ -0,0 +1,241 @@
1
+ import { getSessionThrottleIndex, startThrottle, stopThrottle } from '@vpalmisano/throttler'
2
+ import { paramCase } from 'change-case'
3
+ import fs from 'fs'
4
+ import json5 from 'json5'
5
+ import wrap from 'word-wrap'
6
+
7
+ import { Config, getConfigDocs, loadConfig } from './config'
8
+ import { MediaPath, prepareFakeMedia } from './media'
9
+ import { Server } from './server'
10
+ import { Session } from './session'
11
+ import { Stats } from './stats'
12
+ import {
13
+ checkChromeExecutable,
14
+ getDockerLogsPath,
15
+ logger,
16
+ registerExitHandler,
17
+ resolvePackagePath,
18
+ sleep,
19
+ startRandomActivateAudio,
20
+ stopRandomActivateAudio,
21
+ stopTimers,
22
+ } from './utils'
23
+ import { calculateVisqolScore } from './visqol'
24
+ import { calculateVmafScore, convertToIvf, prepareVideo } from './vmaf'
25
+ import path from 'path'
26
+
27
+ const log = logger('webrtcperf')
28
+
29
+ function showHelpOrVersion(): void {
30
+ if (process.argv.findIndex(a => a.localeCompare('--help') === 0) !== -1) {
31
+ const docs = getConfigDocs()
32
+ let out = `Params:\n --version\n It shows the package version.\n`
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ Object.entries(docs).forEach(([name, value]: [string, any]) => {
35
+ out += ` --${paramCase(name)}
36
+ ${wrap(value.doc, { width: 72, indent: ' ' })}
37
+ Default: ${value.default}\n`
38
+ })
39
+ console.log(out)
40
+ process.exit(0)
41
+ } else if (process.argv.findIndex(a => a.localeCompare('--version') === 0) !== -1) {
42
+ const version = json5.parse(fs.readFileSync(resolvePackagePath('package.json')).toString()).version
43
+ console.log(version)
44
+ process.exit(0)
45
+ }
46
+ }
47
+
48
+ export async function setupApplication(config: Config): Promise<{ stats: Stats; stop: () => Promise<void> }> {
49
+ if (!config.startTimestamp) {
50
+ config.startTimestamp = Date.now()
51
+ }
52
+
53
+ // Stats.
54
+ const stats = new Stats(config)
55
+ await stats.start()
56
+
57
+ // Control server.
58
+ let server: Server
59
+ if (config.serverPort) {
60
+ server = new Server(config, stats)
61
+ await server.start()
62
+ }
63
+
64
+ // Prepare fake video and audio.
65
+ const mediaPaths: MediaPath[] = []
66
+ if (config.videoPath) {
67
+ for (const videoPath of config.videoPath.split(',')) {
68
+ const ret = await prepareFakeMedia({ ...config, videoPath })
69
+ mediaPaths.push(ret)
70
+ }
71
+ }
72
+
73
+ // Network throttle.
74
+ if (config.throttleConfig) {
75
+ await startThrottle(config.throttleConfig)
76
+ }
77
+
78
+ // Download browser if necessary.
79
+ if (!config.chromiumUrl && !config.chromiumPath) {
80
+ await checkChromeExecutable()
81
+ }
82
+
83
+ // Start session function.
84
+ const startLocalSession = async (id: number, spawnPeriod: number): Promise<void> => {
85
+ const throttleIndex = getSessionThrottleIndex(id)
86
+ const mediaPath = mediaPaths.length ? mediaPaths[id % mediaPaths.length] : undefined
87
+ const session = new Session({
88
+ ...config,
89
+ mediaPath,
90
+ spawnPeriod,
91
+ id,
92
+ throttleIndex,
93
+ })
94
+ session.once('stop', () => {
95
+ console.warn(`Session ${id} stopped, reloading...`)
96
+ setTimeout(startLocalSession, spawnPeriod, id)
97
+ })
98
+ stats.addSession(session)
99
+ await session.start()
100
+ }
101
+
102
+ // Start the local sessions.
103
+ if (config.sessions > 0) {
104
+ if (config.randomAudioPeriod) {
105
+ startRandomActivateAudio(
106
+ stats.sessions,
107
+ config.randomAudioPeriod,
108
+ config.randomAudioProbability,
109
+ config.randomAudioRange,
110
+ )
111
+ }
112
+ const spawnPeriod = 1000 / config.spawnRate
113
+ log.debug(`Starting ${config.sessions} sessions (spawnPeriod: ${spawnPeriod}ms)`)
114
+ const startTime = Date.now()
115
+ for (let i = 0; i < config.sessions; i += 1) {
116
+ const id = stats.consumeSessionId(config.tabsPerSession)
117
+ await startLocalSession(id, spawnPeriod)
118
+ // If not the last session, sleep
119
+ if (i < config.sessions - 1) {
120
+ await sleep(spawnPeriod)
121
+ }
122
+ }
123
+ const elapsed = Math.round((Date.now() - startTime) / 1000)
124
+ const spawnRate = (config.sessions * config.tabsPerSession) / elapsed
125
+ log.debug(`${config.sessions * config.tabsPerSession} pages started in ${elapsed}s (${spawnRate.toFixed(2)}/s)`)
126
+ }
127
+
128
+ return {
129
+ stats,
130
+ stop: async (): Promise<void> => {
131
+ log.debug('Stopping')
132
+
133
+ stopRandomActivateAudio()
134
+
135
+ await stats.stop()
136
+
137
+ if (config.throttleConfig) {
138
+ await stopThrottle()
139
+ }
140
+
141
+ stopTimers()
142
+
143
+ // vmaf score.
144
+ if (config.vmafPath) {
145
+ try {
146
+ await calculateVmafScore(config)
147
+ } catch (err: unknown) {
148
+ log.error(`vmaf score error: ${(err as Error).stack}`)
149
+ }
150
+ }
151
+
152
+ // visqol score
153
+ if (config.visqolPath) {
154
+ try {
155
+ await calculateVisqolScore(config)
156
+ } catch (err: unknown) {
157
+ log.error(`visqol score error: ${(err as Error).stack}`)
158
+ }
159
+ }
160
+
161
+ // Copy docker logs to data directory.
162
+ if (config.pageLogPath) {
163
+ try {
164
+ const logPath = await getDockerLogsPath()
165
+ const dataDir = path.dirname(config.pageLogPath)
166
+ await fs.promises.cp(logPath, path.resolve(dataDir, 'docker.log'))
167
+ } catch (err: unknown) {
168
+ log.debug(`docker logs not found: ${(err as Error).message}`)
169
+ }
170
+ }
171
+
172
+ if (server) {
173
+ server.stop()
174
+ }
175
+
176
+ log.debug('Stopped')
177
+ },
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Main function
183
+ */
184
+ async function main(): Promise<void> {
185
+ showHelpOrVersion()
186
+
187
+ const config = loadConfig(process.argv[2])
188
+
189
+ if (config.vmafPrepareVideo) {
190
+ await prepareVideo(config, true)
191
+ process.exit(0)
192
+ }
193
+
194
+ if (config.vmafProcessVideo) {
195
+ await convertToIvf(config.vmafProcessVideo, config.vmafVideoCrop, false)
196
+ process.exit(0)
197
+ }
198
+
199
+ const { stop: stopApplication } = await setupApplication(config)
200
+
201
+ const stop = async (): Promise<void> => {
202
+ console.log('Exiting...')
203
+
204
+ await stopApplication()
205
+
206
+ process.exit(0)
207
+ }
208
+ registerExitHandler(() => stop())
209
+
210
+ // Stop after a configured duration.
211
+ if (config.runDuration > 0) {
212
+ setTimeout(stop, config.runDuration * 1000)
213
+ }
214
+
215
+ // Command line interface.
216
+ if (process.stdin && process.stdin.setRawMode) {
217
+ console.log('Press [q] to quit')
218
+ process.stdin.setRawMode(true)
219
+ process.stdin.resume()
220
+ process.stdin.on('data', async data => {
221
+ log.debug('[stdin]', data[0])
222
+ if (data[0] === 'q'.charCodeAt(0)) {
223
+ try {
224
+ await stop()
225
+ } catch (err: unknown) {
226
+ log.error(`stop error: ${(err as Error).stack}`)
227
+ process.exit(1)
228
+ }
229
+ } else if (data[0] === 'x'.charCodeAt(0)) {
230
+ process.exit(1)
231
+ }
232
+ })
233
+ }
234
+ }
235
+
236
+ if (require.main === module) {
237
+ main().catch(err => {
238
+ console.error(err)
239
+ process.exit(-1)
240
+ })
241
+ }