@vpalmisano/webrtcperf 4.1.7 → 4.1.10
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/README.md +14 -9
- package/app.min.js +1 -1
- package/build/src/app.js +1 -1
- package/build/src/app.js.map +1 -1
- package/build/src/config.d.ts +4 -4
- package/build/src/config.js +65 -34
- package/build/src/config.js.map +1 -1
- package/build/src/server.js +4 -4
- package/build/src/server.js.map +1 -1
- package/build/src/session.d.ts +3 -3
- package/build/src/session.js +4 -4
- package/build/src/session.js.map +1 -1
- package/build/src/stats.d.ts +1 -1
- package/build/src/stats.js +24 -4
- package/build/src/stats.js.map +1 -1
- package/build/src/utils.d.ts +5 -3
- package/build/src/utils.js +9 -10
- package/build/src/utils.js.map +1 -1
- package/build/src/vmaf.js +10 -9
- package/build/src/vmaf.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -21
- package/src/app.ts +1 -1
- package/src/config.ts +67 -35
- package/src/server.ts +4 -4
- package/src/session.ts +7 -6
- package/src/stats.ts +25 -4
- package/src/utils.ts +14 -13
- package/src/vmaf.ts +18 -11
package/src/config.ts
CHANGED
|
@@ -3,11 +3,14 @@ import { ipaddress, url } from 'convict-format-with-validator'
|
|
|
3
3
|
import { existsSync } from 'fs'
|
|
4
4
|
import os from 'os'
|
|
5
5
|
import { join } from 'path'
|
|
6
|
+
import json5 from 'json5'
|
|
7
|
+
import yaml from 'yaml'
|
|
8
|
+
import toml from 'toml'
|
|
6
9
|
|
|
7
10
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
11
|
const puppeteer = require('puppeteer-core')
|
|
9
12
|
|
|
10
|
-
import { logger } from './utils'
|
|
13
|
+
import { downloadUrl, logger } from './utils'
|
|
11
14
|
const log = logger('webrtcperf:config')
|
|
12
15
|
|
|
13
16
|
const float = {
|
|
@@ -47,6 +50,12 @@ const index = {
|
|
|
47
50
|
|
|
48
51
|
addFormats({ ipaddress, url, float, index })
|
|
49
52
|
|
|
53
|
+
convict.addParser([
|
|
54
|
+
{ extension: 'json', parse: json5.parse },
|
|
55
|
+
{ extension: ['yml', 'yaml'], parse: yaml.parse },
|
|
56
|
+
{ extension: 'toml', parse: toml.parse },
|
|
57
|
+
])
|
|
58
|
+
|
|
50
59
|
// config schema
|
|
51
60
|
const configSchema = convict({
|
|
52
61
|
url: {
|
|
@@ -70,9 +79,16 @@ session, \`$i\` the tab absolute index.`,
|
|
|
70
79
|
},
|
|
71
80
|
customUrlHandler: {
|
|
72
81
|
doc: `This argument specifies the file path for the custom page URL handler that will be exported by default. \
|
|
73
|
-
The custom page URL handler allows you to define custom URLs that can be used to open your application
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
The custom page URL handler allows you to define custom URLs that can be used to open your application. \
|
|
83
|
+
The handler function will be called with the following variables: \
|
|
84
|
+
- sessions: the total number of sessions; \
|
|
85
|
+
- tabsPerSession: the total number of tabs per session; \
|
|
86
|
+
- id: the session global index (0-indexed); \
|
|
87
|
+
- index: the tab global index (0-indexed); \
|
|
88
|
+
- tabIndex: the tab index in the current session (0-indexed); \
|
|
89
|
+
- pid: the process pid; \
|
|
90
|
+
- env: the environment variables object; \
|
|
91
|
+
- params: the script parameters object. \
|
|
76
92
|
You can use these variables to create custom URL schemes that suit your application's needs.`,
|
|
77
93
|
format: String,
|
|
78
94
|
default: '',
|
|
@@ -183,8 +199,8 @@ seconds.`,
|
|
|
183
199
|
},
|
|
184
200
|
randomAudioPeriod: {
|
|
185
201
|
doc: `If not zero, it specifies the maximum period in seconds after which \
|
|
186
|
-
a new random active
|
|
187
|
-
that
|
|
202
|
+
a new random active session is selected, enabling the getUserMedia audio tracks in \
|
|
203
|
+
that session and disabling all of the others.`,
|
|
188
204
|
format: 'nat',
|
|
189
205
|
default: 0,
|
|
190
206
|
env: 'RANDOM_AUDIO_PERIOD',
|
|
@@ -199,10 +215,11 @@ the selected audio will be activated (value: 0-100).`,
|
|
|
199
215
|
arg: 'random-audio-probability',
|
|
200
216
|
},
|
|
201
217
|
randomAudioRange: {
|
|
202
|
-
doc: `When using random audio period, it defines the
|
|
203
|
-
to be included into the random selection.`,
|
|
204
|
-
format: '
|
|
205
|
-
default:
|
|
218
|
+
doc: `When using random audio period, it defines the session indexes \
|
|
219
|
+
to be included into the random selection (default: include all the sessions).`,
|
|
220
|
+
format: 'index',
|
|
221
|
+
default: 'true',
|
|
222
|
+
nullable: true,
|
|
206
223
|
env: 'RANDOM_AUDIO_RANGE',
|
|
207
224
|
arg: 'random-audio-range',
|
|
208
225
|
},
|
|
@@ -274,12 +291,13 @@ The total decoders count is stored into the virtual file \`/dev/shm/chromium-vid
|
|
|
274
291
|
env: 'MAX_VIDEO_DECODERS',
|
|
275
292
|
arg: 'max-video-decoders',
|
|
276
293
|
},
|
|
277
|
-
|
|
278
|
-
doc: `
|
|
279
|
-
format:
|
|
280
|
-
default:
|
|
281
|
-
|
|
282
|
-
|
|
294
|
+
maxVideoDecodersRange: {
|
|
295
|
+
doc: `It applies the max video decoders option to the sessions included into this list (default: include all the sessions)`,
|
|
296
|
+
format: 'index',
|
|
297
|
+
default: 'true',
|
|
298
|
+
nullable: true,
|
|
299
|
+
env: 'MAX_VIDEO_DECODERS_RANGE',
|
|
300
|
+
arg: 'max-video-decoders-range',
|
|
283
301
|
},
|
|
284
302
|
incognito: {
|
|
285
303
|
doc: `Runs the browser in incognito mode.`,
|
|
@@ -289,13 +307,12 @@ The total decoders count is stored into the virtual file \`/dev/shm/chromium-vid
|
|
|
289
307
|
arg: 'incognito',
|
|
290
308
|
},
|
|
291
309
|
display: {
|
|
292
|
-
doc: `If unset, the browser will run in headless mode.
|
|
310
|
+
doc: `If unset, the browser will run in headless mode, otherwise it will run in normal windowed mode.
|
|
293
311
|
When running on MacOS or Windows, set it to any not-empty string.
|
|
294
312
|
On Linux, set it to a valid X server \`DISPLAY\` string (e.g. \`:0\`).`,
|
|
295
313
|
format: String,
|
|
296
314
|
default: '',
|
|
297
315
|
nullable: true,
|
|
298
|
-
env: 'DISPLAY',
|
|
299
316
|
arg: 'display',
|
|
300
317
|
},
|
|
301
318
|
/* audioRedForOpus: {
|
|
@@ -337,7 +354,7 @@ calculated using \`Date.now()\``,
|
|
|
337
354
|
enableDetailedStats: {
|
|
338
355
|
doc: `If detailed participant metrics values should be collected.`,
|
|
339
356
|
format: 'index',
|
|
340
|
-
default: '',
|
|
357
|
+
default: '0-24',
|
|
341
358
|
nullable: true,
|
|
342
359
|
env: 'ENABLE_DETAILED_STATS',
|
|
343
360
|
arg: 'enable-detailed-stats',
|
|
@@ -358,7 +375,7 @@ calculated using \`Date.now()\``,
|
|
|
358
375
|
},
|
|
359
376
|
pageLogFilter: {
|
|
360
377
|
doc: `If set, only the logs with the matching text will be printed \
|
|
361
|
-
on console. Regexp string allowed.`,
|
|
378
|
+
on the console. Regexp string allowed.`,
|
|
362
379
|
format: String,
|
|
363
380
|
default: '',
|
|
364
381
|
nullable: true,
|
|
@@ -366,17 +383,25 @@ on console. Regexp string allowed.`,
|
|
|
366
383
|
arg: 'page-log-filter',
|
|
367
384
|
},
|
|
368
385
|
pageLogPath: {
|
|
369
|
-
doc: `If set, page console logs will be saved on the selected file path.`,
|
|
386
|
+
doc: `If set, the page console logs will be saved on the selected file path.`,
|
|
370
387
|
format: String,
|
|
371
388
|
default: '',
|
|
372
389
|
nullable: true,
|
|
373
390
|
env: 'PAGE_LOG_PATH',
|
|
374
391
|
arg: 'page-log-path',
|
|
375
392
|
},
|
|
393
|
+
enableBrowserLogging: {
|
|
394
|
+
doc: `It enables the Chromium browser logging for the specified session indexes. It requires the page log path option to be set. `,
|
|
395
|
+
format: 'index',
|
|
396
|
+
nullable: true,
|
|
397
|
+
default: '',
|
|
398
|
+
env: 'ENABLE_BROWSER_LOGGING',
|
|
399
|
+
arg: 'enable-browser-logging',
|
|
400
|
+
},
|
|
376
401
|
userAgent: {
|
|
377
402
|
doc: `The user agent override.`,
|
|
378
403
|
format: String,
|
|
379
|
-
default: '',
|
|
404
|
+
default: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
|
|
380
405
|
nullable: true,
|
|
381
406
|
env: 'USER_AGENT',
|
|
382
407
|
arg: 'user-agent',
|
|
@@ -445,14 +470,6 @@ use the host X server instance.`,
|
|
|
445
470
|
env: 'ENABLE_GPU',
|
|
446
471
|
arg: 'enable-gpu',
|
|
447
472
|
},
|
|
448
|
-
enableBrowserLogging: {
|
|
449
|
-
doc: `It enables the Chromium browser logging for the specified session indexes.`,
|
|
450
|
-
format: 'index',
|
|
451
|
-
nullable: true,
|
|
452
|
-
default: '',
|
|
453
|
-
env: 'ENABLE_BROWSER_LOGGING',
|
|
454
|
-
arg: 'enable-browser-logging',
|
|
455
|
-
},
|
|
456
473
|
blockedUrls: {
|
|
457
474
|
doc: `A comma-separated list of request URLs that will be automatically \
|
|
458
475
|
blocked.`,
|
|
@@ -846,15 +863,30 @@ export type Config = typeof _schemaProperties
|
|
|
846
863
|
* Loads the config object.
|
|
847
864
|
*/
|
|
848
865
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
849
|
-
export function loadConfig(filePath?: string, values?: any): Config {
|
|
850
|
-
if (filePath
|
|
851
|
-
|
|
852
|
-
|
|
866
|
+
export async function loadConfig(filePath?: string, values?: any): Promise<Config> {
|
|
867
|
+
if (filePath) {
|
|
868
|
+
if (filePath.startsWith('http')) {
|
|
869
|
+
log.debug(`Loading config from url: ${filePath}`)
|
|
870
|
+
const res = await downloadUrl(filePath)
|
|
871
|
+
if (!res?.data) {
|
|
872
|
+
throw new Error(`Failed to download configuration from: ${filePath}`)
|
|
873
|
+
}
|
|
874
|
+
const values =
|
|
875
|
+
res.contentType === 'application/x-yaml'
|
|
876
|
+
? yaml.parse(res.data)
|
|
877
|
+
: res.contentType === 'application/toml'
|
|
878
|
+
? toml.parse(res.data)
|
|
879
|
+
: json5.parse(res.data)
|
|
880
|
+
configSchema.load(values)
|
|
881
|
+
} else if (existsSync(filePath)) {
|
|
882
|
+
log.debug(`Loading config from local file: ${filePath}`)
|
|
883
|
+
configSchema.loadFile(filePath)
|
|
884
|
+
}
|
|
853
885
|
} else if (values) {
|
|
854
886
|
log.debug('Loading config from values.')
|
|
855
887
|
configSchema.load(values)
|
|
856
888
|
} else {
|
|
857
|
-
log.debug('
|
|
889
|
+
log.debug('Loading config from default values.')
|
|
858
890
|
configSchema.load({})
|
|
859
891
|
}
|
|
860
892
|
|
package/src/server.ts
CHANGED
|
@@ -119,14 +119,14 @@ export class Server {
|
|
|
119
119
|
log.error(`mkdir ${this.serverData} error: ${err.message}`)
|
|
120
120
|
})
|
|
121
121
|
this.app.get('/data', this.getDataArchive.bind(this))
|
|
122
|
-
this.app.get('/data
|
|
122
|
+
this.app.get('/data/:path', this.getData.bind(this))
|
|
123
123
|
}
|
|
124
124
|
if (this.videoCachePath) {
|
|
125
125
|
log.debug(`using videoCachePath: ${this.videoCachePath}`)
|
|
126
126
|
fs.promises.mkdir(this.videoCachePath, { recursive: true }).catch(err => {
|
|
127
127
|
log.error(`mkdir ${this.videoCachePath} error: ${err.message}`)
|
|
128
128
|
})
|
|
129
|
-
this.app.get('/cache
|
|
129
|
+
this.app.get('/cache/:path', this.getCache.bind(this))
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
this.app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
@@ -429,7 +429,7 @@ export class Server {
|
|
|
429
429
|
* content in tar.gz format.
|
|
430
430
|
*/
|
|
431
431
|
private getData(req: express.Request, res: express.Response, next: express.NextFunction): void {
|
|
432
|
-
const paramPath = path.normalize(req.params
|
|
432
|
+
const paramPath = path.normalize(req.params.path).replace(/^(\.\.(\/|\\|$))+/, '')
|
|
433
433
|
log.debug(`GET /data/${paramPath}`, req.query)
|
|
434
434
|
const fpath = path.resolve(this.serverData, paramPath)
|
|
435
435
|
if (!fs.existsSync(fpath)) {
|
|
@@ -453,7 +453,7 @@ export class Server {
|
|
|
453
453
|
}
|
|
454
454
|
|
|
455
455
|
private getCache(req: express.Request, res: express.Response, next: express.NextFunction): void {
|
|
456
|
-
const paramPath = path.normalize(req.params
|
|
456
|
+
const paramPath = path.normalize(req.params.path).replace(/^(\.\.(\/|\\|$))+/, '')
|
|
457
457
|
log.debug(`GET /cache/${paramPath}`, req.query)
|
|
458
458
|
const fpath = path.resolve(this.videoCachePath, paramPath)
|
|
459
459
|
if (!fs.existsSync(fpath)) {
|
package/src/session.ts
CHANGED
|
@@ -15,6 +15,7 @@ import puppeteer, {
|
|
|
15
15
|
CDPSession,
|
|
16
16
|
CookieParam,
|
|
17
17
|
ElementHandle,
|
|
18
|
+
ImageFormat,
|
|
18
19
|
KeyInput,
|
|
19
20
|
Metrics,
|
|
20
21
|
Page,
|
|
@@ -176,7 +177,7 @@ export interface SessionParams {
|
|
|
176
177
|
debuggingAddress: string
|
|
177
178
|
randomAudioPeriod: number
|
|
178
179
|
maxVideoDecoders: number
|
|
179
|
-
|
|
180
|
+
maxVideoDecodersRange: string
|
|
180
181
|
incognito: boolean
|
|
181
182
|
serverPort: number
|
|
182
183
|
serverSecret: string
|
|
@@ -260,7 +261,7 @@ export class Session extends EventEmitter {
|
|
|
260
261
|
private readonly debuggingAddress: string
|
|
261
262
|
private readonly randomAudioPeriod: number
|
|
262
263
|
private readonly maxVideoDecoders: number
|
|
263
|
-
private readonly
|
|
264
|
+
private readonly maxVideoDecodersRange: string
|
|
264
265
|
private readonly incognito: boolean
|
|
265
266
|
private readonly serverPort: number
|
|
266
267
|
private readonly serverSecret: string
|
|
@@ -390,7 +391,7 @@ export class Session extends EventEmitter {
|
|
|
390
391
|
debuggingAddress,
|
|
391
392
|
randomAudioPeriod,
|
|
392
393
|
maxVideoDecoders,
|
|
393
|
-
|
|
394
|
+
maxVideoDecodersRange,
|
|
394
395
|
incognito,
|
|
395
396
|
serverPort,
|
|
396
397
|
serverSecret,
|
|
@@ -464,7 +465,7 @@ export class Session extends EventEmitter {
|
|
|
464
465
|
this.userAgent = userAgent
|
|
465
466
|
this.randomAudioPeriod = randomAudioPeriod
|
|
466
467
|
this.maxVideoDecoders = maxVideoDecoders
|
|
467
|
-
this.
|
|
468
|
+
this.maxVideoDecodersRange = maxVideoDecodersRange
|
|
468
469
|
this.incognito = incognito
|
|
469
470
|
this.serverPort = serverPort
|
|
470
471
|
this.serverSecret = serverSecret
|
|
@@ -596,7 +597,7 @@ export class Session extends EventEmitter {
|
|
|
596
597
|
env.CHROME_LOG_FILE = path.resolve(pageLogDir, `chrome-${this.id}.log`)
|
|
597
598
|
}
|
|
598
599
|
|
|
599
|
-
if (this.maxVideoDecoders !== -1 && this.id
|
|
600
|
+
if (this.maxVideoDecoders !== -1 && enabledForSession(this.id, this.maxVideoDecodersRange)) {
|
|
600
601
|
fieldTrials = `WebRTC-MaxVideoDecoders/${this.maxVideoDecoders}/` + fieldTrials
|
|
601
602
|
}
|
|
602
603
|
if (fieldTrials.length) {
|
|
@@ -1921,7 +1922,7 @@ webrtcperf.config.AUDIO_URL = "http${this.serverUseHttps ? 's' : ''}://localhost
|
|
|
1921
1922
|
if (!page) {
|
|
1922
1923
|
throw new Error(`Page ${index} not found`)
|
|
1923
1924
|
}
|
|
1924
|
-
const filePath = `/tmp/screenshot-${index}.${format}`
|
|
1925
|
+
const filePath = `/tmp/screenshot-${index}.${format}` as `${string}.${ImageFormat}`
|
|
1925
1926
|
await page.screenshot({
|
|
1926
1927
|
path: filePath,
|
|
1927
1928
|
fullPage: true,
|
package/src/stats.ts
CHANGED
|
@@ -795,10 +795,12 @@ export class Stats extends events.EventEmitter {
|
|
|
795
795
|
this.collectedStatsConfig.pages = 0
|
|
796
796
|
this.collectedStatsConfig.startTime = this.startTimestamp
|
|
797
797
|
// Reset collectedStats object.
|
|
798
|
-
|
|
798
|
+
const prevByParticipantAndTrackStats = {} as Record<string, Set<string>>
|
|
799
|
+
Object.entries(this.collectedStats).forEach(([name, stats]) => {
|
|
799
800
|
stats.all.reset()
|
|
800
801
|
Object.values(stats.byHost).forEach(s => s.reset())
|
|
801
802
|
Object.values(stats.byCodec).forEach(s => s.reset())
|
|
803
|
+
prevByParticipantAndTrackStats[name] = new Set(Object.keys(stats.byParticipantAndTrack))
|
|
802
804
|
stats.byParticipantAndTrack = {}
|
|
803
805
|
})
|
|
804
806
|
for (const [sessionId, session] of this.sessions.entries()) {
|
|
@@ -812,6 +814,7 @@ export class Stats extends events.EventEmitter {
|
|
|
812
814
|
//log.log(name, obj)
|
|
813
815
|
try {
|
|
814
816
|
const collectedStats = this.collectedStats[name]
|
|
817
|
+
const prevByParticipantAndTrack = prevByParticipantAndTrackStats[name]
|
|
815
818
|
if (typeof obj === 'number' && isFinite(obj)) {
|
|
816
819
|
collectedStats.all.push(obj)
|
|
817
820
|
} else {
|
|
@@ -828,7 +831,9 @@ export class Stats extends events.EventEmitter {
|
|
|
828
831
|
stats.push(value)
|
|
829
832
|
// Push participant and track values.
|
|
830
833
|
if (enabledForSession(sessionId, this.enableDetailedStats) && participantName) {
|
|
831
|
-
|
|
834
|
+
const label = `${participantName}:${trackId || ''}`
|
|
835
|
+
collectedStats.byParticipantAndTrack[label] = value
|
|
836
|
+
prevByParticipantAndTrack.delete(label)
|
|
832
837
|
}
|
|
833
838
|
} else if (typeof value === 'string') {
|
|
834
839
|
// Codec stats.
|
|
@@ -869,6 +874,7 @@ export class Stats extends events.EventEmitter {
|
|
|
869
874
|
return
|
|
870
875
|
}
|
|
871
876
|
const collectedStats = this.collectedStats[name]
|
|
877
|
+
const prevByParticipantAndTrack = prevByParticipantAndTrackStats[name]
|
|
872
878
|
collectedStats.all.push(stats.all)
|
|
873
879
|
Object.entries(stats.byHost).forEach(([host, values]) => {
|
|
874
880
|
if (!collectedStats.byHost[host]) {
|
|
@@ -884,6 +890,7 @@ export class Stats extends events.EventEmitter {
|
|
|
884
890
|
})
|
|
885
891
|
Object.entries(stats.byParticipantAndTrack).forEach(([label, value]) => {
|
|
886
892
|
collectedStats.byParticipantAndTrack[label] = value
|
|
893
|
+
prevByParticipantAndTrack.delete(label)
|
|
887
894
|
})
|
|
888
895
|
})
|
|
889
896
|
}
|
|
@@ -930,7 +937,7 @@ export class Stats extends events.EventEmitter {
|
|
|
930
937
|
await Promise.allSettled([
|
|
931
938
|
this.writeStats(),
|
|
932
939
|
this.writeDetailedStats(),
|
|
933
|
-
this.sendToPushGateway(),
|
|
940
|
+
this.sendToPushGateway(prevByParticipantAndTrackStats),
|
|
934
941
|
this.writeAlertRulesReport(),
|
|
935
942
|
])
|
|
936
943
|
}
|
|
@@ -1062,7 +1069,7 @@ export class Stats extends events.EventEmitter {
|
|
|
1062
1069
|
/**
|
|
1063
1070
|
* sendToPushGateway
|
|
1064
1071
|
*/
|
|
1065
|
-
async sendToPushGateway(): Promise<void> {
|
|
1072
|
+
async sendToPushGateway(removedByParticipantAndTrackStats: Record<string, Set<string>>): Promise<void> {
|
|
1066
1073
|
if (!this.gateway || !this.running) {
|
|
1067
1074
|
return
|
|
1068
1075
|
}
|
|
@@ -1109,6 +1116,20 @@ export class Stats extends events.EventEmitter {
|
|
|
1109
1116
|
})
|
|
1110
1117
|
}
|
|
1111
1118
|
|
|
1119
|
+
// Remove metrics for removed participants and tracks.
|
|
1120
|
+
const removedByParticipantAndTrack = removedByParticipantAndTrackStats[name]
|
|
1121
|
+
if (removedByParticipantAndTrack) {
|
|
1122
|
+
for (const label of removedByParticipantAndTrack) {
|
|
1123
|
+
const [participantName, trackId] = label.split(':', 2)
|
|
1124
|
+
metric.value?.remove({
|
|
1125
|
+
participantName,
|
|
1126
|
+
trackId,
|
|
1127
|
+
datetime,
|
|
1128
|
+
...this.customMetricsLabels,
|
|
1129
|
+
})
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1112
1133
|
// Set alerts metrics.
|
|
1113
1134
|
if (this.alertRules && this.alertRules[name]) {
|
|
1114
1135
|
const rule = this.alertRules[name]
|
package/src/utils.ts
CHANGED
|
@@ -20,7 +20,7 @@ import os, { networkInterfaces } from 'os'
|
|
|
20
20
|
import path, { dirname } from 'path'
|
|
21
21
|
import pidtree from 'pidtree'
|
|
22
22
|
import pidusage from 'pidusage'
|
|
23
|
-
import puppeteer, { Page } from 'puppeteer-core'
|
|
23
|
+
import puppeteer, { ImageFormat, Page } from 'puppeteer-core'
|
|
24
24
|
|
|
25
25
|
import { Session } from './session'
|
|
26
26
|
|
|
@@ -255,7 +255,7 @@ export function startRandomActivateAudio(
|
|
|
255
255
|
sessions: Map<number, Session>,
|
|
256
256
|
randomAudioPeriod: number,
|
|
257
257
|
randomAudioProbability: number,
|
|
258
|
-
randomAudioRange:
|
|
258
|
+
randomAudioRange: string,
|
|
259
259
|
): void {
|
|
260
260
|
if (randomActivateAudioRunning) return
|
|
261
261
|
randomActivateAudioRunning = true
|
|
@@ -272,13 +272,13 @@ export function stopRandomActivateAudio(): void {
|
|
|
272
272
|
* @param sessions The sessions Map
|
|
273
273
|
* @param randomAudioPeriod If set, the function will be called in loop
|
|
274
274
|
* @param randomAudioProbability The activation probability
|
|
275
|
-
* @param randomAudioRange The
|
|
275
|
+
* @param randomAudioRange The page indexes to include into the automation
|
|
276
276
|
*/
|
|
277
277
|
export async function randomActivateAudio(
|
|
278
278
|
sessions: Map<number, Session>,
|
|
279
279
|
randomAudioPeriod: number,
|
|
280
280
|
randomAudioProbability: number,
|
|
281
|
-
randomAudioRange:
|
|
281
|
+
randomAudioRange: string,
|
|
282
282
|
): Promise<void> {
|
|
283
283
|
if (!randomAudioPeriod || !randomActivateAudioRunning) {
|
|
284
284
|
return
|
|
@@ -287,13 +287,9 @@ export async function randomActivateAudio(
|
|
|
287
287
|
let pages: (Page | null)[] = []
|
|
288
288
|
for (const session of sessions.values()) {
|
|
289
289
|
const sessionPages = [...session.pages.values()]
|
|
290
|
-
if (randomAudioRange) {
|
|
291
|
-
|
|
292
|
-
break
|
|
293
|
-
}
|
|
294
|
-
sessionPages.splice(randomAudioRange - session.id)
|
|
290
|
+
if (enabledForSession(session.id, randomAudioRange)) {
|
|
291
|
+
pages = pages.concat(sessionPages)
|
|
295
292
|
}
|
|
296
|
-
pages = pages.concat(sessionPages)
|
|
297
293
|
}
|
|
298
294
|
// Remove pages with no audio tracks.
|
|
299
295
|
for (const [i, page] of pages.entries()) {
|
|
@@ -380,6 +376,8 @@ export interface DownloadData {
|
|
|
380
376
|
end: number
|
|
381
377
|
/** Total returned size. */
|
|
382
378
|
total: number
|
|
379
|
+
/** Content type. */
|
|
380
|
+
contentType: string
|
|
383
381
|
}
|
|
384
382
|
|
|
385
383
|
/**
|
|
@@ -452,12 +450,13 @@ export async function downloadUrl(
|
|
|
452
450
|
} else {
|
|
453
451
|
/* log.debug(`downloadUrl ${response.data.length} bytes, headers=${
|
|
454
452
|
JSON.stringify(response.headers)}`); */
|
|
453
|
+
const contentType = response.headers['content-type']
|
|
455
454
|
let start = 0
|
|
456
455
|
let end = 0
|
|
457
456
|
let total = 0
|
|
458
457
|
if (response.headers['content-range']) {
|
|
459
|
-
log.debug(`downloadUrl ${response.data.length} bytes, content-range=${response.headers['content-range']}`)
|
|
460
458
|
const contentRange = response.headers['content-range'].split('/')
|
|
459
|
+
log.debug(`downloadUrl ${response.data.length} bytes, contentType=${contentType}, contentRange=${contentRange}`)
|
|
461
460
|
const rangeParts = contentRange[0].split('-')
|
|
462
461
|
total = parseInt(contentRange[1])
|
|
463
462
|
if (rangeParts.length === 2) {
|
|
@@ -475,6 +474,7 @@ export async function downloadUrl(
|
|
|
475
474
|
start,
|
|
476
475
|
end,
|
|
477
476
|
total,
|
|
477
|
+
contentType,
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
480
|
}
|
|
@@ -613,7 +613,7 @@ SIGNALS.forEach(event =>
|
|
|
613
613
|
export async function checkChromeExecutable(): Promise<string> {
|
|
614
614
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
615
615
|
const { loadConfig } = require('./config')
|
|
616
|
-
const config = loadConfig()
|
|
616
|
+
const config = await loadConfig()
|
|
617
617
|
const cacheDir = path.join(os.homedir(), '.webrtcperf/chrome')
|
|
618
618
|
|
|
619
619
|
const fixSemVer = (v: string) => v.split('.').slice(0, 3).join('.')
|
|
@@ -1037,7 +1037,8 @@ export async function pageScreenshot(
|
|
|
1037
1037
|
if (!element) {
|
|
1038
1038
|
throw new Error(`pageScreenshot selector "${selector}" not found`)
|
|
1039
1039
|
}
|
|
1040
|
-
|
|
1040
|
+
const path = filePath as `${string}.${ImageFormat}`
|
|
1041
|
+
await element.screenshot({ path })
|
|
1041
1042
|
} catch (err) {
|
|
1042
1043
|
log.error(`pageScreenshot error: ${(err as Error).message}`)
|
|
1043
1044
|
} finally {
|
package/src/vmaf.ts
CHANGED
|
@@ -587,8 +587,19 @@ ${splitFilter(['ref_vmaf', 'ref_psnr', preview ? 'ref_preview' : ''])};\
|
|
|
587
587
|
}
|
|
588
588
|
|
|
589
589
|
async function writeGraph(vmafLogPath: string) {
|
|
590
|
+
const {
|
|
591
|
+
CategoryScale,
|
|
592
|
+
Chart,
|
|
593
|
+
LinearScale,
|
|
594
|
+
LineController,
|
|
595
|
+
LineElement,
|
|
596
|
+
PointElement,
|
|
597
|
+
Legend,
|
|
598
|
+
Title,
|
|
599
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
600
|
+
} = require('chart.js')
|
|
590
601
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
591
|
-
const {
|
|
602
|
+
const { Canvas } = require('skia-canvas')
|
|
592
603
|
|
|
593
604
|
const vmafLog = JSON.parse(await fs.promises.readFile(vmafLogPath, 'utf-8')) as {
|
|
594
605
|
frames: {
|
|
@@ -625,13 +636,9 @@ async function writeGraph(vmafLogPath: string) {
|
|
|
625
636
|
)
|
|
626
637
|
.map(d => ({ x: d.x, y: d.y / d.count }))
|
|
627
638
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
backgroundColour: 'white',
|
|
632
|
-
})
|
|
633
|
-
|
|
634
|
-
const buffer = await chartJSNodeCanvas.renderToBuffer({
|
|
639
|
+
Chart.register([CategoryScale, LineController, LineElement, LinearScale, PointElement, Legend, Title])
|
|
640
|
+
const canvas = new Canvas(1280, 720)
|
|
641
|
+
const chart = new Chart(canvas, {
|
|
635
642
|
type: 'line',
|
|
636
643
|
data: {
|
|
637
644
|
labels: data.map(d => d.x),
|
|
@@ -652,7 +659,7 @@ async function writeGraph(vmafLogPath: string) {
|
|
|
652
659
|
plugins: {
|
|
653
660
|
title: {
|
|
654
661
|
display: true,
|
|
655
|
-
text: path.basename(
|
|
662
|
+
text: path.basename(path.dirname(vmafLogPath)).replace(/_/g, ' '),
|
|
656
663
|
},
|
|
657
664
|
},
|
|
658
665
|
scales: {
|
|
@@ -663,8 +670,8 @@ async function writeGraph(vmafLogPath: string) {
|
|
|
663
670
|
},
|
|
664
671
|
},
|
|
665
672
|
})
|
|
666
|
-
|
|
667
|
-
|
|
673
|
+
await canvas.saveAs(fpath, { format: 'png', matte: 'white' })
|
|
674
|
+
chart.destroy()
|
|
668
675
|
}
|
|
669
676
|
|
|
670
677
|
interface Crop {
|