@vpalmisano/webrtcperf 4.4.9 → 4.4.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/app.min.js +1 -1
- package/build/src/docker.js +25 -11
- package/build/src/docker.js.map +1 -1
- package/build/src/index.d.ts +2 -0
- package/build/src/index.js +2 -0
- package/build/src/index.js.map +1 -1
- package/build/src/scenarios.d.ts +123 -0
- package/build/src/scenarios.js +133 -0
- package/build/src/scenarios.js.map +1 -0
- package/build/src/utils.d.ts +0 -18
- package/build/src/utils.js +0 -59
- package/build/src/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -15
- package/src/docker.ts +26 -11
- package/src/index.ts +2 -0
- package/src/scenarios.ts +148 -0
- package/src/utils.ts +0 -73
package/src/scenarios.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { FastStats } from './stats'
|
|
4
|
+
import { ThrottleConfig, ThrottleRule } from '@vpalmisano/throttler'
|
|
5
|
+
import { Config } from './config'
|
|
6
|
+
|
|
7
|
+
export async function parseStatsFile(filePath: string) {
|
|
8
|
+
const fileData = await fs.promises.readFile(filePath, 'utf-8')
|
|
9
|
+
const lines = fileData.split('\n')
|
|
10
|
+
const headers = lines[0].split(',')
|
|
11
|
+
const data = lines.slice(1).map(line =>
|
|
12
|
+
line.split(',').reduce(
|
|
13
|
+
(acc, value, index) => {
|
|
14
|
+
if (value !== '') {
|
|
15
|
+
acc[headers[index]] = isNaN(Number(value)) ? value : Number(value)
|
|
16
|
+
}
|
|
17
|
+
return acc
|
|
18
|
+
},
|
|
19
|
+
{} as Record<string, string | number>,
|
|
20
|
+
),
|
|
21
|
+
)
|
|
22
|
+
return data
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function aggregateStatsSummary({
|
|
26
|
+
dirPath = 'logs',
|
|
27
|
+
senderParticipantName = 'Participant-000001',
|
|
28
|
+
receiverParticipantName = 'Participant-000000',
|
|
29
|
+
nameParser = (name: string) => {
|
|
30
|
+
const [_, id, scenario] = name.split('_')
|
|
31
|
+
return { id, scenario }
|
|
32
|
+
},
|
|
33
|
+
}) {
|
|
34
|
+
const stats = [] as {
|
|
35
|
+
timestamp: number
|
|
36
|
+
id: string
|
|
37
|
+
scenario: string
|
|
38
|
+
videoRecvBitratePerPixel: FastStats
|
|
39
|
+
videoRecvFps: FastStats
|
|
40
|
+
videoSentFps: FastStats
|
|
41
|
+
}[]
|
|
42
|
+
const results = await fs.promises.readdir(dirPath)
|
|
43
|
+
for (const test of results) {
|
|
44
|
+
const filePath = path.join(dirPath, test, 'detailed-stats-summary.csv')
|
|
45
|
+
if (!fs.existsSync(filePath)) continue
|
|
46
|
+
const timestamp = fs.statSync(path.join(dirPath, test)).ctime.getTime()
|
|
47
|
+
const data = await parseStatsFile(filePath)
|
|
48
|
+
const { id, scenario } = nameParser(test)
|
|
49
|
+
|
|
50
|
+
const aggregated = {
|
|
51
|
+
timestamp,
|
|
52
|
+
id,
|
|
53
|
+
scenario,
|
|
54
|
+
videoRecvBitratePerPixel: new FastStats(),
|
|
55
|
+
videoRecvFps: new FastStats(),
|
|
56
|
+
videoSentFps: new FastStats(),
|
|
57
|
+
}
|
|
58
|
+
data.forEach(v => {
|
|
59
|
+
const { participantName, trackId } = v as { participantName: string; trackId: string }
|
|
60
|
+
const metrics = v as Record<string, number>
|
|
61
|
+
if (participantName === receiverParticipantName) {
|
|
62
|
+
if (trackId?.endsWith('-v') && metrics.videoRecvFrames > 0) {
|
|
63
|
+
const videoRecvBitratePerPixel =
|
|
64
|
+
metrics.videoRecvBitrates / (metrics.videoRecvWidth * metrics.videoRecvHeight)
|
|
65
|
+
if (!isNaN(videoRecvBitratePerPixel)) aggregated.videoRecvBitratePerPixel.push(videoRecvBitratePerPixel)
|
|
66
|
+
if (!isNaN(metrics.videoRecvFps)) aggregated.videoRecvFps.push(metrics.videoRecvFps)
|
|
67
|
+
}
|
|
68
|
+
} else if (participantName === senderParticipantName) {
|
|
69
|
+
if (trackId?.endsWith('-v') && metrics.videoSentFrames > 0) {
|
|
70
|
+
if (!isNaN(metrics.videoSentFps)) aggregated.videoSentFps.push(metrics.videoSentFps)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
stats.push(aggregated)
|
|
75
|
+
}
|
|
76
|
+
return stats.sort((a, b) => a.timestamp - b.timestamp)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type ThrottleDirection = 'up' | 'down' | 'bidi'
|
|
80
|
+
|
|
81
|
+
export function formatThrottleRule(throttleRule: ThrottleRule, direction: ThrottleDirection) {
|
|
82
|
+
const { rate, loss, delay } = throttleRule
|
|
83
|
+
return `${direction}-r${rate}-l${loss}-d${delay}`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function parseThrottleRule(throttleDesc: string) {
|
|
87
|
+
const match = throttleDesc.match(/(up|down|bidi)-r(\d+)-l([\d.]+)-d(\d+)/)
|
|
88
|
+
if (!match) throw new Error(`Invalid throttle description: ${throttleDesc}`)
|
|
89
|
+
const direction = match[1] as ThrottleDirection
|
|
90
|
+
const rate = parseInt(match[2])
|
|
91
|
+
const loss = parseInt(match[3])
|
|
92
|
+
const delay = parseInt(match[4])
|
|
93
|
+
return { direction, rate, loss, delay }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function simpleTestWithRateLossDelay(
|
|
97
|
+
id: string,
|
|
98
|
+
{ rate, loss, delay, direction }: { rate: number; loss: number; delay: number; direction: ThrottleDirection },
|
|
99
|
+
repeat: 1,
|
|
100
|
+
) {
|
|
101
|
+
const throttle: ThrottleConfig = {}
|
|
102
|
+
const queue = 25
|
|
103
|
+
if (direction === 'down' || direction === 'bidi') {
|
|
104
|
+
throttle.down = [
|
|
105
|
+
{ rate: 20000, loss: 0, delay: 0, queue },
|
|
106
|
+
{ rate, loss, delay, queue, at: 30 },
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
if (direction === 'up' || direction === 'bidi') {
|
|
110
|
+
throttle.up = [
|
|
111
|
+
{ rate: 20000, loss: 0, delay, queue },
|
|
112
|
+
{ rate, loss, delay, queue, at: 30 },
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
const throttleDesc = formatThrottleRule({ rate, loss, delay }, direction)
|
|
116
|
+
const now = Date.now()
|
|
117
|
+
const ret: Partial<Config>[] = []
|
|
118
|
+
for (let i = 0; i < repeat; i++) {
|
|
119
|
+
const basePath = `logs/${now}-${i + 1}_${id}_${throttleDesc}`
|
|
120
|
+
const sessions = direction === 'bidi' ? '0-1' : direction === 'down' ? '0' : '1'
|
|
121
|
+
ret.push({
|
|
122
|
+
sessions: 2,
|
|
123
|
+
runDuration: 60 * 3,
|
|
124
|
+
debuggingPort: 9000,
|
|
125
|
+
prometheusPushgateway: 'http://localhost:9091',
|
|
126
|
+
prometheusPushgatewayJobName: id,
|
|
127
|
+
statsPath: `${basePath}/stats.csv`,
|
|
128
|
+
detailedStatsPath: `${basePath}/detailed-stats.csv`,
|
|
129
|
+
showPageLog: false,
|
|
130
|
+
showStats: false,
|
|
131
|
+
statsInterval: 5,
|
|
132
|
+
scriptParams: JSON.stringify({
|
|
133
|
+
enableMic: '0-1',
|
|
134
|
+
enableCam: '1',
|
|
135
|
+
}),
|
|
136
|
+
throttleConfig: JSON.stringify([
|
|
137
|
+
{
|
|
138
|
+
sessions,
|
|
139
|
+
protocol: 'udp',
|
|
140
|
+
skipSourcePorts: '53,80,443',
|
|
141
|
+
skipDestinationPorts: '53,80,443',
|
|
142
|
+
...throttle,
|
|
143
|
+
},
|
|
144
|
+
]),
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
return ret
|
|
148
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -23,7 +23,6 @@ import pidusage from 'pidusage'
|
|
|
23
23
|
import puppeteer, { ImageFormat, Page } from 'puppeteer-core'
|
|
24
24
|
|
|
25
25
|
import { Session } from './session'
|
|
26
|
-
import { FastStats } from './stats'
|
|
27
26
|
|
|
28
27
|
// eslint-disable-next-line
|
|
29
28
|
const ps = require('pidusage/lib/ps')
|
|
@@ -1256,75 +1255,3 @@ export async function getDockerLogsPath(): Promise<string> {
|
|
|
1256
1255
|
}
|
|
1257
1256
|
return logPath
|
|
1258
1257
|
}
|
|
1259
|
-
|
|
1260
|
-
export async function parseStatsFile(filePath: string) {
|
|
1261
|
-
const fileData = await fs.promises.readFile(filePath, 'utf-8')
|
|
1262
|
-
const lines = fileData.split('\n')
|
|
1263
|
-
const headers = lines[0].split(',')
|
|
1264
|
-
const data = lines.slice(1).map(line =>
|
|
1265
|
-
line.split(',').reduce(
|
|
1266
|
-
(acc, value, index) => {
|
|
1267
|
-
if (value !== '') {
|
|
1268
|
-
acc[headers[index]] = isNaN(Number(value)) ? value : Number(value)
|
|
1269
|
-
}
|
|
1270
|
-
return acc
|
|
1271
|
-
},
|
|
1272
|
-
{} as Record<string, string | number>,
|
|
1273
|
-
),
|
|
1274
|
-
)
|
|
1275
|
-
return data
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
export async function aggregateStatsSummary({
|
|
1279
|
-
dirPath = 'logs',
|
|
1280
|
-
senderParticipantName = 'Participant-000001',
|
|
1281
|
-
receiverParticipantName = 'Participant-000000',
|
|
1282
|
-
nameParser = (name: string) => {
|
|
1283
|
-
const [destination, scenario] = name.split('_')
|
|
1284
|
-
return { destination, scenario }
|
|
1285
|
-
},
|
|
1286
|
-
}) {
|
|
1287
|
-
const stats = [] as {
|
|
1288
|
-
timestamp: number
|
|
1289
|
-
destination: string
|
|
1290
|
-
scenario: string
|
|
1291
|
-
videoRecvBitratePerPixel: FastStats
|
|
1292
|
-
videoRecvFps: FastStats
|
|
1293
|
-
videoSentFps: FastStats
|
|
1294
|
-
}[]
|
|
1295
|
-
const results = await fs.promises.readdir(dirPath)
|
|
1296
|
-
for (const test of results) {
|
|
1297
|
-
const filePath = path.join(dirPath, test, 'detailed-stats-summary.csv')
|
|
1298
|
-
if (!fs.existsSync(filePath)) continue
|
|
1299
|
-
const timestamp = fs.statSync(path.join(dirPath, test)).ctime.getTime()
|
|
1300
|
-
const data = await parseStatsFile(filePath)
|
|
1301
|
-
const { destination, scenario } = nameParser(test)
|
|
1302
|
-
|
|
1303
|
-
const aggregated = {
|
|
1304
|
-
timestamp,
|
|
1305
|
-
destination,
|
|
1306
|
-
scenario,
|
|
1307
|
-
videoRecvBitratePerPixel: new FastStats(),
|
|
1308
|
-
videoRecvFps: new FastStats(),
|
|
1309
|
-
videoSentFps: new FastStats(),
|
|
1310
|
-
}
|
|
1311
|
-
data.forEach(v => {
|
|
1312
|
-
const { participantName, trackId } = v as { participantName: string; trackId: string }
|
|
1313
|
-
const metrics = v as Record<string, number>
|
|
1314
|
-
if (participantName === receiverParticipantName) {
|
|
1315
|
-
if (trackId?.endsWith('-v') && metrics.videoRecvFrames > 0) {
|
|
1316
|
-
const videoRecvBitratePerPixel =
|
|
1317
|
-
metrics.videoRecvBitrates / (metrics.videoRecvWidth * metrics.videoRecvHeight)
|
|
1318
|
-
if (!isNaN(videoRecvBitratePerPixel)) aggregated.videoRecvBitratePerPixel.push(videoRecvBitratePerPixel)
|
|
1319
|
-
if (!isNaN(metrics.videoRecvFps)) aggregated.videoRecvFps.push(metrics.videoRecvFps)
|
|
1320
|
-
}
|
|
1321
|
-
} else if (participantName === senderParticipantName) {
|
|
1322
|
-
if (trackId?.endsWith('-v') && metrics.videoSentFrames > 0) {
|
|
1323
|
-
if (!isNaN(metrics.videoSentFps)) aggregated.videoSentFps.push(metrics.videoSentFps)
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
})
|
|
1327
|
-
stats.push(aggregated)
|
|
1328
|
-
}
|
|
1329
|
-
return stats.sort((a, b) => a.timestamp - b.timestamp)
|
|
1330
|
-
}
|