@vpalmisano/webrtcperf 4.1.10 → 4.2.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/README.md +685 -235
- package/app.min.js +1 -1
- package/build/src/app.js +3 -1
- package/build/src/app.js.map +1 -1
- package/build/src/config.d.ts +91 -1
- package/build/src/config.js +114 -10
- package/build/src/config.js.map +1 -1
- package/build/src/server.d.ts +1 -1
- package/build/src/server.js +4 -4
- package/build/src/server.js.map +1 -1
- package/build/src/stats.d.ts +3 -3
- package/build/src/stats.js +8 -8
- package/build/src/stats.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -24
- package/src/app.ts +5 -2
- package/src/config.ts +117 -11
- package/src/server.ts +4 -4
- package/src/stats.ts +9 -9
package/src/config.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import convict, { addFormats } from 'convict'
|
|
1
|
+
import convict, { addFormats, SchemaObj } from 'convict'
|
|
2
2
|
import { ipaddress, url } from 'convict-format-with-validator'
|
|
3
3
|
import { existsSync } from 'fs'
|
|
4
4
|
import os from 'os'
|
|
5
|
-
import { join } from 'path'
|
|
5
|
+
import path, { join } from 'path'
|
|
6
6
|
import json5 from 'json5'
|
|
7
7
|
import yaml from 'yaml'
|
|
8
8
|
import toml from 'toml'
|
|
@@ -110,7 +110,7 @@ The temporary files containing the raw video and audio will be stored at \
|
|
|
110
110
|
\`\${VIDEO_CACHE_PATH}/video.\${VIDEO_FORMAT}\` and \
|
|
111
111
|
\`\${VIDEO_CACHE_PATH}/audio.wav\`.`,
|
|
112
112
|
format: String,
|
|
113
|
-
default: 'https://github.com/vpalmisano/webrtcperf/releases/download/
|
|
113
|
+
default: 'https://github.com/vpalmisano/webrtcperf/releases/download/videos-1.0/kt.mp4',
|
|
114
114
|
env: 'VIDEO_PATH',
|
|
115
115
|
arg: 'video-path',
|
|
116
116
|
},
|
|
@@ -190,7 +190,51 @@ seconds.`,
|
|
|
190
190
|
arg: 'run-duration',
|
|
191
191
|
},
|
|
192
192
|
throttleConfig: {
|
|
193
|
-
doc: `A JSON5 string with a valid
|
|
193
|
+
doc: `A JSON5 string with a valid throttler configuration (https://github.com/vpalmisano/throttler). \
|
|
194
|
+
Example: \
|
|
195
|
+
|
|
196
|
+
\`\`\`javascript
|
|
197
|
+
[{
|
|
198
|
+
sessions: '0-1',
|
|
199
|
+
device: 'eth0',
|
|
200
|
+
protocol: 'udp',
|
|
201
|
+
skipSourcePorts: "443",
|
|
202
|
+
skipDestinationPorts: "443",
|
|
203
|
+
filter: "--sports 443 --dports 443",
|
|
204
|
+
match: 'nbyte("ababa" at 12 layer 1)',
|
|
205
|
+
capture: 'capture.pcap',
|
|
206
|
+
up: {
|
|
207
|
+
rate: 1000,
|
|
208
|
+
delay: 50,
|
|
209
|
+
loss: 5,
|
|
210
|
+
queue: 10,
|
|
211
|
+
},
|
|
212
|
+
down: [
|
|
213
|
+
{ rate: 2000, delay: 50, delayJitter: 10, delayJitterCorrelation: 25, loss: 2, lossBurst: 2, queue: 20 },
|
|
214
|
+
{ rate: 1000, delay: 50, loss: 2, queue: 20, at: 60 },
|
|
215
|
+
]
|
|
216
|
+
}]
|
|
217
|
+
\`\`\`
|
|
218
|
+
- The sessions field represents the sessions IDs range that will be affected by the rule, e.g.: "0-10", "2,4" or simply "2".
|
|
219
|
+
- The device, protocol, up, down fields are optional. When device is not set, the default route device will be used. If protocol is specified ('udp' or 'tcp'), \
|
|
220
|
+
only the packets with the specified protocol will be affected by the shaping rules.
|
|
221
|
+
- The capture field is optional and specifies the pcap file to save the captured packets.
|
|
222
|
+
- With skipSourcePorts and skipDestinationPorts you can specify a comma-separated list of ports that will not be affected by the shaping rules.
|
|
223
|
+
- The filter field is optional and specifies the additional IPTables filter to apply for filtering the packets.
|
|
224
|
+
- The match field is optional and specifies the additional match rule to apply for filtering the packets (https://man7.org/linux/man-pages/man8/tc-ematch.8.html).
|
|
225
|
+
- The up and down fields are optional and they specify the upstream and downstream shaping rules. The possible options for the up and down rules could be:
|
|
226
|
+
- rate: the shaping rate in Kbps;
|
|
227
|
+
- delay: the shaping delay in milliseconds;
|
|
228
|
+
- delayJitter: the shaping delay jitter in milliseconds;
|
|
229
|
+
- delayJitterCorrelation: the shaping delay jitter correlation in milliseconds;
|
|
230
|
+
- loss: the packet loss percentage;
|
|
231
|
+
- lossBurst: the packet loss burst percentage;
|
|
232
|
+
- queue: the shaping queue size in packets;
|
|
233
|
+
- at: the time in seconds when the shaping rule will be applied (default: 0).
|
|
234
|
+
The up and down rules can be specified as a single object or an array of objects.
|
|
235
|
+
When using an array of objects, specify a different "at" value for each of them, in order to apply a sequence of actions; please note that only the specified properties will override previous ones, so you can omit the values that you don't want to change. \
|
|
236
|
+
\
|
|
237
|
+
`,
|
|
194
238
|
format: String,
|
|
195
239
|
nullable: true,
|
|
196
240
|
default: '',
|
|
@@ -367,7 +411,7 @@ calculated using \`Date.now()\``,
|
|
|
367
411
|
arg: 'spawn-rate',
|
|
368
412
|
},
|
|
369
413
|
showPageLog: {
|
|
370
|
-
doc: `If \`true\`, the pages console logs will be shown on console.`,
|
|
414
|
+
doc: `If \`true\`, the pages console logs will be shown on console. Set to false to disable the page logs.`,
|
|
371
415
|
format: 'Boolean',
|
|
372
416
|
default: true,
|
|
373
417
|
env: 'SHOW_PAGE_LOG',
|
|
@@ -657,13 +701,13 @@ service (example: "http://127.0.0.1:9091").`,
|
|
|
657
701
|
env: 'ALERT_RULES',
|
|
658
702
|
arg: 'alert-rules',
|
|
659
703
|
},
|
|
660
|
-
|
|
661
|
-
doc: `The alert rules report output filename.`,
|
|
704
|
+
alertRulesOutput: {
|
|
705
|
+
doc: `The alert rules report output filename. If the file ends with .log extension, a detailed log will be generated, otherwise a JSON report will be generated.`,
|
|
662
706
|
format: String,
|
|
663
707
|
nullable: true,
|
|
664
708
|
default: '',
|
|
665
|
-
env: '
|
|
666
|
-
arg: 'alert-rules-
|
|
709
|
+
env: 'ALERT_RULES_OUTPUT',
|
|
710
|
+
arg: 'alert-rules-output',
|
|
667
711
|
},
|
|
668
712
|
alertRulesFailPercentile: {
|
|
669
713
|
doc: `The alert rules report fails percentile (0-100). With the default value the \
|
|
@@ -762,7 +806,7 @@ the reference and degraded versions.`,
|
|
|
762
806
|
vmafCrop: {
|
|
763
807
|
doc: `If set, the reference and degraded videos will be cropped using the specified configuration in JSON5 format. \
|
|
764
808
|
Crop configuration should be expressed using the ffmpeg crop filter syntax (https://ffmpeg.org/ffmpeg-filters.html#crop). \
|
|
765
|
-
E.g. \`{ "Participant-000001_recv-by_Participant-000000
|
|
809
|
+
E.g. \`{ "Participant-000001_recv-by_Participant-000000": { ref: { w: "iw-10", h: "ih-5" }, deg: { w: "200", h: "200" } } }\``,
|
|
766
810
|
format: String,
|
|
767
811
|
nullable: true,
|
|
768
812
|
default: '',
|
|
@@ -880,7 +924,12 @@ export async function loadConfig(filePath?: string, values?: any): Promise<Confi
|
|
|
880
924
|
configSchema.load(values)
|
|
881
925
|
} else if (existsSync(filePath)) {
|
|
882
926
|
log.debug(`Loading config from local file: ${filePath}`)
|
|
883
|
-
|
|
927
|
+
if (filePath.endsWith('.js') || filePath.endsWith('.mjs')) {
|
|
928
|
+
const module = await import(/* webpackIgnore: true */ path.resolve(filePath))
|
|
929
|
+
configSchema.load(await module.default())
|
|
930
|
+
} else {
|
|
931
|
+
configSchema.loadFile(filePath)
|
|
932
|
+
}
|
|
884
933
|
}
|
|
885
934
|
} else if (values) {
|
|
886
935
|
log.debug('Loading config from values.')
|
|
@@ -896,3 +945,60 @@ export async function loadConfig(filePath?: string, values?: any): Promise<Confi
|
|
|
896
945
|
log.debug('Using config:', config)
|
|
897
946
|
return config
|
|
898
947
|
}
|
|
948
|
+
|
|
949
|
+
function getFunctionDeclaration() {
|
|
950
|
+
const properties: Record<string, { type: string; description: string; nullable?: boolean }> = {}
|
|
951
|
+
const required: string[] = []
|
|
952
|
+
const schema = configSchema.getSchema()
|
|
953
|
+
|
|
954
|
+
Object.entries(schema._cvtProperties).forEach(([name, value]) => {
|
|
955
|
+
const { format, doc, nullable } = value as SchemaObj
|
|
956
|
+
properties[name] = {
|
|
957
|
+
type: format as string,
|
|
958
|
+
description: doc as string,
|
|
959
|
+
nullable,
|
|
960
|
+
}
|
|
961
|
+
})
|
|
962
|
+
|
|
963
|
+
return {
|
|
964
|
+
name: 'webrtcperf',
|
|
965
|
+
description: 'Starts a webrtcperf test.',
|
|
966
|
+
parameters: {
|
|
967
|
+
type: 'object',
|
|
968
|
+
properties,
|
|
969
|
+
required,
|
|
970
|
+
},
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
975
|
+
const { GoogleGenAI } = require('@google/genai')
|
|
976
|
+
|
|
977
|
+
export async function loadConfigFromPrompt(prompt: string) {
|
|
978
|
+
log.debug(`loadConfigFromPrompt: "${prompt}"`)
|
|
979
|
+
if (!process.env.GEMINI_API_KEY) {
|
|
980
|
+
throw new Error('GEMINI_API_KEY environment variable is not set. Please set it to use the Google GenAI API.')
|
|
981
|
+
}
|
|
982
|
+
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY })
|
|
983
|
+
const response = await ai.models.generateContent({
|
|
984
|
+
model: 'gemini-2.5-flash',
|
|
985
|
+
contents: prompt,
|
|
986
|
+
config: {
|
|
987
|
+
tools: [
|
|
988
|
+
{
|
|
989
|
+
functionDeclarations: [getFunctionDeclaration()],
|
|
990
|
+
},
|
|
991
|
+
],
|
|
992
|
+
thinkingConfig: {
|
|
993
|
+
thinkingBudget: 0,
|
|
994
|
+
},
|
|
995
|
+
},
|
|
996
|
+
})
|
|
997
|
+
if (response.functionCalls && response.functionCalls.length > 0) {
|
|
998
|
+
const functionCall = response.functionCalls[0]
|
|
999
|
+
log.info('Using function call:', functionCall.name, functionCall.args)
|
|
1000
|
+
return loadConfig(undefined, functionCall.args)
|
|
1001
|
+
} else {
|
|
1002
|
+
throw new Error('No function call found in the response. Please check the prompt and try again.')
|
|
1003
|
+
}
|
|
1004
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -392,14 +392,14 @@ export class Server {
|
|
|
392
392
|
/**
|
|
393
393
|
* GET /download/alert-rules endpoint.
|
|
394
394
|
*
|
|
395
|
-
* Downloads the alert rules report stored into the {@link Stats.
|
|
395
|
+
* Downloads the alert rules report stored into the {@link Stats.alertRulesOutput}.
|
|
396
396
|
*/
|
|
397
397
|
private getAlertRules(req: express.Request, res: express.Response, next: express.NextFunction): void {
|
|
398
398
|
log.debug(`GET /download/alert-rules`, req.query)
|
|
399
|
-
if (!this.stats.
|
|
400
|
-
return next(new Error('Stats
|
|
399
|
+
if (!this.stats.alertRulesOutput) {
|
|
400
|
+
return next(new Error('Stats alertRulesOutput not set'))
|
|
401
401
|
}
|
|
402
|
-
res.download(this.stats.
|
|
402
|
+
res.download(this.stats.alertRulesOutput)
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
/**
|
package/src/stats.ts
CHANGED
|
@@ -331,7 +331,7 @@ export class Stats extends events.EventEmitter {
|
|
|
331
331
|
private scheduler?: Scheduler
|
|
332
332
|
|
|
333
333
|
private alertRules: Record<string, AlertRule> | null = null
|
|
334
|
-
readonly
|
|
334
|
+
readonly alertRulesOutput: string
|
|
335
335
|
private readonly alertRulesFailPercentile: number
|
|
336
336
|
private readonly pushStatsUrl: string
|
|
337
337
|
private readonly pushStatsId: string
|
|
@@ -414,7 +414,7 @@ export class Stats extends events.EventEmitter {
|
|
|
414
414
|
rtcStatsTimeout,
|
|
415
415
|
customMetrics,
|
|
416
416
|
alertRules,
|
|
417
|
-
|
|
417
|
+
alertRulesOutput,
|
|
418
418
|
alertRulesFailPercentile,
|
|
419
419
|
pushStatsUrl,
|
|
420
420
|
pushStatsId,
|
|
@@ -436,7 +436,7 @@ export class Stats extends events.EventEmitter {
|
|
|
436
436
|
rtcStatsTimeout: number
|
|
437
437
|
customMetrics: string
|
|
438
438
|
alertRules: string
|
|
439
|
-
|
|
439
|
+
alertRulesOutput: string
|
|
440
440
|
alertRulesFailPercentile: number
|
|
441
441
|
pushStatsUrl: string
|
|
442
442
|
pushStatsId: string
|
|
@@ -487,7 +487,7 @@ export class Stats extends events.EventEmitter {
|
|
|
487
487
|
this.alertRules = json5.parse(alertRules)
|
|
488
488
|
log.debug(`using alertRules: ${JSON.stringify(this.alertRules, undefined, 2)}`)
|
|
489
489
|
}
|
|
490
|
-
this.
|
|
490
|
+
this.alertRulesOutput = alertRulesOutput
|
|
491
491
|
this.alertRulesFailPercentile = alertRulesFailPercentile
|
|
492
492
|
this.pushStatsUrl = pushStatsUrl
|
|
493
493
|
this.pushStatsId = pushStatsId
|
|
@@ -1616,12 +1616,12 @@ export class Stats extends events.EventEmitter {
|
|
|
1616
1616
|
* writeAlertRulesReport
|
|
1617
1617
|
*/
|
|
1618
1618
|
async writeAlertRulesReport(): Promise<void> {
|
|
1619
|
-
if (!this.alertRules || !this.
|
|
1619
|
+
if (!this.alertRules || !this.alertRulesOutput || !this.running) {
|
|
1620
1620
|
return
|
|
1621
1621
|
}
|
|
1622
|
-
log.debug(`writeAlertRulesReport writing in ${this.
|
|
1622
|
+
log.debug(`writeAlertRulesReport writing in ${this.alertRulesOutput}`)
|
|
1623
1623
|
try {
|
|
1624
|
-
const ext = this.
|
|
1624
|
+
const ext = this.alertRulesOutput.split('.').slice(-1)[0]
|
|
1625
1625
|
const report = this.formatAlertRulesReport(ext)
|
|
1626
1626
|
if (!report.length) {
|
|
1627
1627
|
return
|
|
@@ -1641,10 +1641,10 @@ export class Stats extends events.EventEmitter {
|
|
|
1641
1641
|
} else {
|
|
1642
1642
|
out = report
|
|
1643
1643
|
}
|
|
1644
|
-
await fs.promises.mkdir(path.dirname(this.
|
|
1644
|
+
await fs.promises.mkdir(path.dirname(this.alertRulesOutput), {
|
|
1645
1645
|
recursive: true,
|
|
1646
1646
|
})
|
|
1647
|
-
await fs.promises.writeFile(this.
|
|
1647
|
+
await fs.promises.writeFile(this.alertRulesOutput, out)
|
|
1648
1648
|
} catch (err) {
|
|
1649
1649
|
log.error(`writeAlertRulesReport error: ${(err as Error).stack}`)
|
|
1650
1650
|
}
|