@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/build/src/docker.js
CHANGED
|
@@ -9,29 +9,40 @@ const utils_1 = require("./utils");
|
|
|
9
9
|
const config_1 = require("./config");
|
|
10
10
|
const throttler_1 = require("@vpalmisano/throttler");
|
|
11
11
|
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
13
|
const log = (0, utils_1.logger)('webrtcperf:docker');
|
|
13
14
|
async function runWithDocker(argv) {
|
|
14
15
|
const docker = new dockerode_1.default();
|
|
15
16
|
const configPath = argv[0];
|
|
16
17
|
if (!configPath)
|
|
17
18
|
throw new Error('No configuration file specified');
|
|
18
|
-
const
|
|
19
|
+
const configs = await (0, config_1.loadConfig)(configPath);
|
|
20
|
+
if (!configs.length)
|
|
21
|
+
throw new Error('Failed to load configuration file');
|
|
19
22
|
const startTimestamp = Date.now();
|
|
20
23
|
const dataDir = process.cwd();
|
|
21
|
-
const
|
|
24
|
+
const tmpDir = os_1.default.tmpdir();
|
|
25
|
+
const jsonConfigPath = `${tmpDir}/webrtcperf-config-${startTimestamp}.json`;
|
|
26
|
+
await fs_1.default.promises.writeFile(jsonConfigPath, JSON.stringify(configs), 'utf-8');
|
|
27
|
+
const binds = [
|
|
28
|
+
'/dev/shm:/dev/shm',
|
|
29
|
+
`${dataDir}:/data`,
|
|
30
|
+
`${tmpDir}/webrtcperf-cache:/root/.webrtcperf`,
|
|
31
|
+
`${jsonConfigPath}:/tmp/config.json:ro`,
|
|
32
|
+
];
|
|
22
33
|
if (process.env.DEBUG_SRC) {
|
|
23
34
|
binds.push(`${(0, utils_1.resolvePackagePath)('app.min.js')}:/app/app.min.js:ro`);
|
|
24
35
|
}
|
|
25
36
|
const portBindings = {};
|
|
26
37
|
const exposedPorts = {};
|
|
27
|
-
if (
|
|
28
|
-
for (let i = 0; i <
|
|
29
|
-
const port = `${
|
|
30
|
-
portBindings[port] = [{ HostPort: `${
|
|
38
|
+
if (configs[0].debuggingPort) {
|
|
39
|
+
for (let i = 0; i < configs[0].sessions; i++) {
|
|
40
|
+
const port = `${configs[0].debuggingPort + i}/tcp`;
|
|
41
|
+
portBindings[port] = [{ HostPort: `${configs[0].debuggingPort + i}` }];
|
|
31
42
|
exposedPorts[port] = {};
|
|
32
43
|
}
|
|
33
44
|
}
|
|
34
|
-
if (
|
|
45
|
+
if (configs[0].throttleConfig && os_1.default.platform() === 'linux') {
|
|
35
46
|
await (0, throttler_1.runShellCommand)('sudo modprobe ifb numifbs=1');
|
|
36
47
|
}
|
|
37
48
|
const env = [
|
|
@@ -43,19 +54,21 @@ async function runWithDocker(argv) {
|
|
|
43
54
|
'SERVER_DATA=/data',
|
|
44
55
|
`START_TIMESTAMP=${startTimestamp}`,
|
|
45
56
|
];
|
|
46
|
-
if (
|
|
57
|
+
if (configs[0].prometheusPushgateway.startsWith('http://localhost')) {
|
|
47
58
|
env.push('PROMETHEUS_PUSHGATEWAY=http://pushgateway:9091');
|
|
48
59
|
}
|
|
49
60
|
const containerConfig = {
|
|
50
61
|
Image: 'ghcr.io/vpalmisano/webrtcperf:devel',
|
|
51
62
|
name: 'webrtcperf',
|
|
52
63
|
WorkingDir: '/data',
|
|
53
|
-
Cmd:
|
|
64
|
+
Cmd: ['/tmp/config.json'],
|
|
54
65
|
HostConfig: {
|
|
55
66
|
Binds: binds,
|
|
56
67
|
PortBindings: portBindings,
|
|
57
|
-
CapAdd:
|
|
58
|
-
NetworkMode:
|
|
68
|
+
CapAdd: configs[0].throttleConfig && os_1.default.platform() === 'linux' ? ['NET_ADMIN'] : [],
|
|
69
|
+
NetworkMode: configs[0].prometheusPushgateway.startsWith('http://localhost')
|
|
70
|
+
? 'prometheus-stack_default'
|
|
71
|
+
: 'bridge',
|
|
59
72
|
ExtraHosts: process.env.EXTRA_HOSTS ? process.env.EXTRA_HOSTS.split(',').map(h => h.trim()) : [],
|
|
60
73
|
},
|
|
61
74
|
Env: env,
|
|
@@ -103,5 +116,6 @@ async function runWithDocker(argv) {
|
|
|
103
116
|
log.error('Docker operation failed:', error);
|
|
104
117
|
throw error;
|
|
105
118
|
}
|
|
119
|
+
await fs_1.default.promises.unlink(jsonConfigPath);
|
|
106
120
|
}
|
|
107
121
|
//# sourceMappingURL=docker.js.map
|
package/build/src/docker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/docker.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/docker.ts"],"names":[],"mappings":"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","sourcesContent":["import Docker from 'dockerode'\nimport { logger, resolvePackagePath } from './utils'\nimport { loadConfig } from './config'\nimport { runShellCommand } from '@vpalmisano/throttler'\nimport os from 'os'\nimport fs from 'fs'\n\nconst log = logger('webrtcperf:docker')\n\nexport async function runWithDocker(argv: string[]) {\n const docker = new Docker()\n const configPath = argv[0]\n if (!configPath) throw new Error('No configuration file specified')\n const configs = await loadConfig(configPath)\n if (!configs.length) throw new Error('Failed to load configuration file')\n\n const startTimestamp = Date.now()\n const dataDir = process.cwd()\n const tmpDir = os.tmpdir()\n\n const jsonConfigPath = `${tmpDir}/webrtcperf-config-${startTimestamp}.json`\n await fs.promises.writeFile(jsonConfigPath, JSON.stringify(configs), 'utf-8')\n\n const binds: string[] = [\n '/dev/shm:/dev/shm',\n `${dataDir}:/data`,\n `${tmpDir}/webrtcperf-cache:/root/.webrtcperf`,\n `${jsonConfigPath}:/tmp/config.json:ro`,\n ]\n\n if (process.env.DEBUG_SRC) {\n binds.push(`${resolvePackagePath('app.min.js')}:/app/app.min.js:ro`)\n }\n\n const portBindings: Docker.PortMap = {}\n const exposedPorts: { [portAndProtocol: string]: object } = {}\n if (configs[0].debuggingPort) {\n for (let i = 0; i < configs[0].sessions; i++) {\n const port = `${configs[0].debuggingPort + i}/tcp`\n portBindings[port] = [{ HostPort: `${configs[0].debuggingPort + i}` }]\n exposedPorts[port] = {}\n }\n }\n\n if (configs[0].throttleConfig && os.platform() === 'linux') {\n await runShellCommand('sudo modprobe ifb numifbs=1')\n }\n\n const env = [\n `DEBUG_LEVEL=${process.env.DEBUG_LEVEL || 'info'}`,\n 'SHOW_PAGE_LOG=false',\n 'SHOW_STATS=false',\n 'SERVER_PORT=5000',\n 'SERVER_USE_HTTPS=true',\n 'SERVER_DATA=/data',\n `START_TIMESTAMP=${startTimestamp}`,\n ]\n\n if (configs[0].prometheusPushgateway.startsWith('http://localhost')) {\n env.push('PROMETHEUS_PUSHGATEWAY=http://pushgateway:9091')\n }\n\n const containerConfig: Docker.ContainerCreateOptions = {\n Image: 'ghcr.io/vpalmisano/webrtcperf:devel',\n name: 'webrtcperf',\n WorkingDir: '/data',\n Cmd: ['/tmp/config.json'],\n HostConfig: {\n Binds: binds,\n PortBindings: portBindings,\n CapAdd: configs[0].throttleConfig && os.platform() === 'linux' ? ['NET_ADMIN'] : [],\n NetworkMode: configs[0].prometheusPushgateway.startsWith('http://localhost')\n ? 'prometheus-stack_default'\n : 'bridge',\n ExtraHosts: process.env.EXTRA_HOSTS ? process.env.EXTRA_HOSTS.split(',').map(h => h.trim()) : [],\n },\n Env: env,\n AttachStdin: true,\n AttachStdout: true,\n AttachStderr: true,\n Tty: true,\n OpenStdin: true,\n StdinOnce: true,\n ExposedPorts: exposedPorts,\n }\n\n try {\n if (!process.env.DEBUG_SRC) {\n log.info('Pulling latest webrtcperf image...')\n await docker.pull('ghcr.io/vpalmisano/webrtcperf:devel')\n }\n\n try {\n const existingContainer = await docker.getContainer('webrtcperf')\n await existingContainer.remove({ force: true })\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (err: unknown) {\n // Container doesn't exist, continue\n }\n\n const container = await docker.createContainer(containerConfig)\n await container.start()\n\n const stream = await container.attach({\n stream: true,\n stdin: true,\n stdout: true,\n stderr: true,\n })\n\n process.stdin.pipe(stream)\n stream.pipe(process.stdout)\n\n await new Promise(resolve => {\n container.wait((err: Error, data: { StatusCode: number }) => {\n if (err) log.error('Error waiting for container:', data, err.stack)\n resolve(data)\n })\n })\n\n await container.remove()\n } catch (error) {\n log.error('Docker operation failed:', error)\n throw error\n }\n\n await fs.promises.unlink(jsonConfigPath)\n}\n"]}
|
package/build/src/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './app';
|
|
2
2
|
export * from './config';
|
|
3
|
+
export * from './docker';
|
|
3
4
|
export * from './media';
|
|
4
5
|
export * from './rtcstats';
|
|
5
6
|
export * from './server';
|
|
@@ -7,3 +8,4 @@ export * from './session';
|
|
|
7
8
|
export * from './stats';
|
|
8
9
|
export * from './utils';
|
|
9
10
|
export * from './vmaf';
|
|
11
|
+
export * from './scenarios';
|
package/build/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./app"), exports);
|
|
18
18
|
__exportStar(require("./config"), exports);
|
|
19
|
+
__exportStar(require("./docker"), exports);
|
|
19
20
|
__exportStar(require("./media"), exports);
|
|
20
21
|
__exportStar(require("./rtcstats"), exports);
|
|
21
22
|
__exportStar(require("./server"), exports);
|
|
@@ -23,4 +24,5 @@ __exportStar(require("./session"), exports);
|
|
|
23
24
|
__exportStar(require("./stats"), exports);
|
|
24
25
|
__exportStar(require("./utils"), exports);
|
|
25
26
|
__exportStar(require("./vmaf"), exports);
|
|
27
|
+
__exportStar(require("./scenarios"), exports);
|
|
26
28
|
//# sourceMappingURL=index.js.map
|
package/build/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wCAAqB;AACrB,2CAAwB;AACxB,0CAAuB;AACvB,6CAA0B;AAC1B,2CAAwB;AACxB,4CAAyB;AACzB,0CAAuB;AACvB,0CAAuB;AACvB,yCAAsB","sourcesContent":["export * from './app'\nexport * from './config'\nexport * from './media'\nexport * from './rtcstats'\nexport * from './server'\nexport * from './session'\nexport * from './stats'\nexport * from './utils'\nexport * from './vmaf'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wCAAqB;AACrB,2CAAwB;AACxB,2CAAwB;AACxB,0CAAuB;AACvB,6CAA0B;AAC1B,2CAAwB;AACxB,4CAAyB;AACzB,0CAAuB;AACvB,0CAAuB;AACvB,yCAAsB;AACtB,8CAA2B","sourcesContent":["export * from './app'\nexport * from './config'\nexport * from './docker'\nexport * from './media'\nexport * from './rtcstats'\nexport * from './server'\nexport * from './session'\nexport * from './stats'\nexport * from './utils'\nexport * from './vmaf'\nexport * from './scenarios'\n"]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { FastStats } from './stats';
|
|
2
|
+
import { ThrottleRule } from '@vpalmisano/throttler';
|
|
3
|
+
export declare function parseStatsFile(filePath: string): Promise<Record<string, string | number>[]>;
|
|
4
|
+
export declare function aggregateStatsSummary({ dirPath, senderParticipantName, receiverParticipantName, nameParser, }: {
|
|
5
|
+
dirPath?: string | undefined;
|
|
6
|
+
senderParticipantName?: string | undefined;
|
|
7
|
+
receiverParticipantName?: string | undefined;
|
|
8
|
+
nameParser?: ((name: string) => {
|
|
9
|
+
id: string;
|
|
10
|
+
scenario: string;
|
|
11
|
+
}) | undefined;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
timestamp: number;
|
|
14
|
+
id: string;
|
|
15
|
+
scenario: string;
|
|
16
|
+
videoRecvBitratePerPixel: FastStats;
|
|
17
|
+
videoRecvFps: FastStats;
|
|
18
|
+
videoSentFps: FastStats;
|
|
19
|
+
}[]>;
|
|
20
|
+
export type ThrottleDirection = 'up' | 'down' | 'bidi';
|
|
21
|
+
export declare function formatThrottleRule(throttleRule: ThrottleRule, direction: ThrottleDirection): string;
|
|
22
|
+
export declare function parseThrottleRule(throttleDesc: string): {
|
|
23
|
+
direction: ThrottleDirection;
|
|
24
|
+
rate: number;
|
|
25
|
+
loss: number;
|
|
26
|
+
delay: number;
|
|
27
|
+
};
|
|
28
|
+
export declare function simpleTestWithRateLossDelay(id: string, { rate, loss, delay, direction }: {
|
|
29
|
+
rate: number;
|
|
30
|
+
loss: number;
|
|
31
|
+
delay: number;
|
|
32
|
+
direction: ThrottleDirection;
|
|
33
|
+
}, repeat: 1): Promise<Partial<{
|
|
34
|
+
url: string;
|
|
35
|
+
urlQuery: string;
|
|
36
|
+
customUrlHandler: string;
|
|
37
|
+
videoPath: string;
|
|
38
|
+
videoWidth: number;
|
|
39
|
+
videoHeight: number;
|
|
40
|
+
videoFramerate: number;
|
|
41
|
+
videoSeek: number;
|
|
42
|
+
videoDuration: number;
|
|
43
|
+
videoCacheRaw: boolean;
|
|
44
|
+
videoCachePath: string;
|
|
45
|
+
videoFormat: string;
|
|
46
|
+
useFakeMedia: boolean;
|
|
47
|
+
runDuration: number;
|
|
48
|
+
throttleConfig: string;
|
|
49
|
+
useBrowserThrottling: boolean;
|
|
50
|
+
randomAudioPeriod: number;
|
|
51
|
+
randomAudioProbability: number;
|
|
52
|
+
randomAudioRange: string;
|
|
53
|
+
chromiumPath: string;
|
|
54
|
+
chromiumVersion: any;
|
|
55
|
+
chromiumUrl: string;
|
|
56
|
+
chromiumFieldTrials: string;
|
|
57
|
+
windowWidth: number;
|
|
58
|
+
windowHeight: number;
|
|
59
|
+
deviceScaleFactor: number;
|
|
60
|
+
maxVideoDecoders: number;
|
|
61
|
+
maxVideoDecodersRange: string;
|
|
62
|
+
incognito: boolean;
|
|
63
|
+
display: string;
|
|
64
|
+
sessions: number;
|
|
65
|
+
tabsPerSession: number;
|
|
66
|
+
startSessionId: number;
|
|
67
|
+
startTimestamp: number;
|
|
68
|
+
enableDetailedStats: string;
|
|
69
|
+
spawnRate: number;
|
|
70
|
+
showPageLog: boolean;
|
|
71
|
+
pageLogFilter: string;
|
|
72
|
+
pageLogPath: string;
|
|
73
|
+
enableBrowserLogging: string;
|
|
74
|
+
userAgent: string;
|
|
75
|
+
scriptPath: string;
|
|
76
|
+
scriptParams: string;
|
|
77
|
+
disabledVideoCodecs: string;
|
|
78
|
+
localStorage: string;
|
|
79
|
+
sessionStorage: string;
|
|
80
|
+
clearCookies: boolean;
|
|
81
|
+
enableGpu: string;
|
|
82
|
+
blockedUrls: string;
|
|
83
|
+
extraHeaders: string;
|
|
84
|
+
responseModifiers: string;
|
|
85
|
+
downloadResponses: string;
|
|
86
|
+
extraCSS: string;
|
|
87
|
+
cookies: string;
|
|
88
|
+
overridePermissions: string;
|
|
89
|
+
hardwareConcurrency: number;
|
|
90
|
+
debuggingPort: number;
|
|
91
|
+
debuggingAddress: string;
|
|
92
|
+
emulateCpuThrottling: number;
|
|
93
|
+
showStats: boolean;
|
|
94
|
+
statsPath: string;
|
|
95
|
+
detailedStatsPath: string;
|
|
96
|
+
statsInterval: number;
|
|
97
|
+
rtcStatsTimeout: number;
|
|
98
|
+
customMetrics: string;
|
|
99
|
+
prometheusPushgateway: string;
|
|
100
|
+
prometheusPushgatewayJobName: string;
|
|
101
|
+
prometheusPushgatewayAuth: string;
|
|
102
|
+
prometheusPushgatewayGzip: boolean;
|
|
103
|
+
alertRules: string;
|
|
104
|
+
alertRulesOutput: string;
|
|
105
|
+
alertRulesFailPercentile: number;
|
|
106
|
+
pushStatsUrl: string;
|
|
107
|
+
pushStatsId: string;
|
|
108
|
+
serverPort: number;
|
|
109
|
+
serverSecret: string;
|
|
110
|
+
serverUseHttps: boolean;
|
|
111
|
+
serverData: string;
|
|
112
|
+
vmafPath: string;
|
|
113
|
+
vmafPreview: boolean;
|
|
114
|
+
vmafKeepIntermediateFiles: boolean;
|
|
115
|
+
vmafKeepSourceFiles: boolean;
|
|
116
|
+
vmafSkipDuplicated: boolean;
|
|
117
|
+
vmafCrop: string;
|
|
118
|
+
vmafPrepareVideo: string;
|
|
119
|
+
vmafProcessVideo: string;
|
|
120
|
+
vmafVideoCrop: string;
|
|
121
|
+
visqolPath: string;
|
|
122
|
+
visqolKeepSourceFiles: boolean;
|
|
123
|
+
}>[]>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseStatsFile = parseStatsFile;
|
|
7
|
+
exports.aggregateStatsSummary = aggregateStatsSummary;
|
|
8
|
+
exports.formatThrottleRule = formatThrottleRule;
|
|
9
|
+
exports.parseThrottleRule = parseThrottleRule;
|
|
10
|
+
exports.simpleTestWithRateLossDelay = simpleTestWithRateLossDelay;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const stats_1 = require("./stats");
|
|
14
|
+
async function parseStatsFile(filePath) {
|
|
15
|
+
const fileData = await fs_1.default.promises.readFile(filePath, 'utf-8');
|
|
16
|
+
const lines = fileData.split('\n');
|
|
17
|
+
const headers = lines[0].split(',');
|
|
18
|
+
const data = lines.slice(1).map(line => line.split(',').reduce((acc, value, index) => {
|
|
19
|
+
if (value !== '') {
|
|
20
|
+
acc[headers[index]] = isNaN(Number(value)) ? value : Number(value);
|
|
21
|
+
}
|
|
22
|
+
return acc;
|
|
23
|
+
}, {}));
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
async function aggregateStatsSummary({ dirPath = 'logs', senderParticipantName = 'Participant-000001', receiverParticipantName = 'Participant-000000', nameParser = (name) => {
|
|
27
|
+
const [_, id, scenario] = name.split('_');
|
|
28
|
+
return { id, scenario };
|
|
29
|
+
}, }) {
|
|
30
|
+
const stats = [];
|
|
31
|
+
const results = await fs_1.default.promises.readdir(dirPath);
|
|
32
|
+
for (const test of results) {
|
|
33
|
+
const filePath = path_1.default.join(dirPath, test, 'detailed-stats-summary.csv');
|
|
34
|
+
if (!fs_1.default.existsSync(filePath))
|
|
35
|
+
continue;
|
|
36
|
+
const timestamp = fs_1.default.statSync(path_1.default.join(dirPath, test)).ctime.getTime();
|
|
37
|
+
const data = await parseStatsFile(filePath);
|
|
38
|
+
const { id, scenario } = nameParser(test);
|
|
39
|
+
const aggregated = {
|
|
40
|
+
timestamp,
|
|
41
|
+
id,
|
|
42
|
+
scenario,
|
|
43
|
+
videoRecvBitratePerPixel: new stats_1.FastStats(),
|
|
44
|
+
videoRecvFps: new stats_1.FastStats(),
|
|
45
|
+
videoSentFps: new stats_1.FastStats(),
|
|
46
|
+
};
|
|
47
|
+
data.forEach(v => {
|
|
48
|
+
const { participantName, trackId } = v;
|
|
49
|
+
const metrics = v;
|
|
50
|
+
if (participantName === receiverParticipantName) {
|
|
51
|
+
if (trackId?.endsWith('-v') && metrics.videoRecvFrames > 0) {
|
|
52
|
+
const videoRecvBitratePerPixel = metrics.videoRecvBitrates / (metrics.videoRecvWidth * metrics.videoRecvHeight);
|
|
53
|
+
if (!isNaN(videoRecvBitratePerPixel))
|
|
54
|
+
aggregated.videoRecvBitratePerPixel.push(videoRecvBitratePerPixel);
|
|
55
|
+
if (!isNaN(metrics.videoRecvFps))
|
|
56
|
+
aggregated.videoRecvFps.push(metrics.videoRecvFps);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (participantName === senderParticipantName) {
|
|
60
|
+
if (trackId?.endsWith('-v') && metrics.videoSentFrames > 0) {
|
|
61
|
+
if (!isNaN(metrics.videoSentFps))
|
|
62
|
+
aggregated.videoSentFps.push(metrics.videoSentFps);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
stats.push(aggregated);
|
|
67
|
+
}
|
|
68
|
+
return stats.sort((a, b) => a.timestamp - b.timestamp);
|
|
69
|
+
}
|
|
70
|
+
function formatThrottleRule(throttleRule, direction) {
|
|
71
|
+
const { rate, loss, delay } = throttleRule;
|
|
72
|
+
return `${direction}-r${rate}-l${loss}-d${delay}`;
|
|
73
|
+
}
|
|
74
|
+
function parseThrottleRule(throttleDesc) {
|
|
75
|
+
const match = throttleDesc.match(/(up|down|bidi)-r(\d+)-l([\d.]+)-d(\d+)/);
|
|
76
|
+
if (!match)
|
|
77
|
+
throw new Error(`Invalid throttle description: ${throttleDesc}`);
|
|
78
|
+
const direction = match[1];
|
|
79
|
+
const rate = parseInt(match[2]);
|
|
80
|
+
const loss = parseInt(match[3]);
|
|
81
|
+
const delay = parseInt(match[4]);
|
|
82
|
+
return { direction, rate, loss, delay };
|
|
83
|
+
}
|
|
84
|
+
async function simpleTestWithRateLossDelay(id, { rate, loss, delay, direction }, repeat) {
|
|
85
|
+
const throttle = {};
|
|
86
|
+
const queue = 25;
|
|
87
|
+
if (direction === 'down' || direction === 'bidi') {
|
|
88
|
+
throttle.down = [
|
|
89
|
+
{ rate: 20000, loss: 0, delay: 0, queue },
|
|
90
|
+
{ rate, loss, delay, queue, at: 30 },
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
if (direction === 'up' || direction === 'bidi') {
|
|
94
|
+
throttle.up = [
|
|
95
|
+
{ rate: 20000, loss: 0, delay, queue },
|
|
96
|
+
{ rate, loss, delay, queue, at: 30 },
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
const throttleDesc = formatThrottleRule({ rate, loss, delay }, direction);
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
const ret = [];
|
|
102
|
+
for (let i = 0; i < repeat; i++) {
|
|
103
|
+
const basePath = `logs/${now}-${i + 1}_${id}_${throttleDesc}`;
|
|
104
|
+
const sessions = direction === 'bidi' ? '0-1' : direction === 'down' ? '0' : '1';
|
|
105
|
+
ret.push({
|
|
106
|
+
sessions: 2,
|
|
107
|
+
runDuration: 60 * 3,
|
|
108
|
+
debuggingPort: 9000,
|
|
109
|
+
prometheusPushgateway: 'http://localhost:9091',
|
|
110
|
+
prometheusPushgatewayJobName: id,
|
|
111
|
+
statsPath: `${basePath}/stats.csv`,
|
|
112
|
+
detailedStatsPath: `${basePath}/detailed-stats.csv`,
|
|
113
|
+
showPageLog: false,
|
|
114
|
+
showStats: false,
|
|
115
|
+
statsInterval: 5,
|
|
116
|
+
scriptParams: JSON.stringify({
|
|
117
|
+
enableMic: '0-1',
|
|
118
|
+
enableCam: '1',
|
|
119
|
+
}),
|
|
120
|
+
throttleConfig: JSON.stringify([
|
|
121
|
+
{
|
|
122
|
+
sessions,
|
|
123
|
+
protocol: 'udp',
|
|
124
|
+
skipSourcePorts: '53,80,443',
|
|
125
|
+
skipDestinationPorts: '53,80,443',
|
|
126
|
+
...throttle,
|
|
127
|
+
},
|
|
128
|
+
]),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return ret;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=scenarios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarios.js","sourceRoot":"","sources":["../../src/scenarios.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { FastStats } from './stats'\nimport { ThrottleConfig, ThrottleRule } from '@vpalmisano/throttler'\nimport { Config } from './config'\n\nexport async function parseStatsFile(filePath: string) {\n const fileData = await fs.promises.readFile(filePath, 'utf-8')\n const lines = fileData.split('\\n')\n const headers = lines[0].split(',')\n const data = lines.slice(1).map(line =>\n line.split(',').reduce(\n (acc, value, index) => {\n if (value !== '') {\n acc[headers[index]] = isNaN(Number(value)) ? value : Number(value)\n }\n return acc\n },\n {} as Record<string, string | number>,\n ),\n )\n return data\n}\n\nexport async function aggregateStatsSummary({\n dirPath = 'logs',\n senderParticipantName = 'Participant-000001',\n receiverParticipantName = 'Participant-000000',\n nameParser = (name: string) => {\n const [_, id, scenario] = name.split('_')\n return { id, scenario }\n },\n}) {\n const stats = [] as {\n timestamp: number\n id: string\n scenario: string\n videoRecvBitratePerPixel: FastStats\n videoRecvFps: FastStats\n videoSentFps: FastStats\n }[]\n const results = await fs.promises.readdir(dirPath)\n for (const test of results) {\n const filePath = path.join(dirPath, test, 'detailed-stats-summary.csv')\n if (!fs.existsSync(filePath)) continue\n const timestamp = fs.statSync(path.join(dirPath, test)).ctime.getTime()\n const data = await parseStatsFile(filePath)\n const { id, scenario } = nameParser(test)\n\n const aggregated = {\n timestamp,\n id,\n scenario,\n videoRecvBitratePerPixel: new FastStats(),\n videoRecvFps: new FastStats(),\n videoSentFps: new FastStats(),\n }\n data.forEach(v => {\n const { participantName, trackId } = v as { participantName: string; trackId: string }\n const metrics = v as Record<string, number>\n if (participantName === receiverParticipantName) {\n if (trackId?.endsWith('-v') && metrics.videoRecvFrames > 0) {\n const videoRecvBitratePerPixel =\n metrics.videoRecvBitrates / (metrics.videoRecvWidth * metrics.videoRecvHeight)\n if (!isNaN(videoRecvBitratePerPixel)) aggregated.videoRecvBitratePerPixel.push(videoRecvBitratePerPixel)\n if (!isNaN(metrics.videoRecvFps)) aggregated.videoRecvFps.push(metrics.videoRecvFps)\n }\n } else if (participantName === senderParticipantName) {\n if (trackId?.endsWith('-v') && metrics.videoSentFrames > 0) {\n if (!isNaN(metrics.videoSentFps)) aggregated.videoSentFps.push(metrics.videoSentFps)\n }\n }\n })\n stats.push(aggregated)\n }\n return stats.sort((a, b) => a.timestamp - b.timestamp)\n}\n\nexport type ThrottleDirection = 'up' | 'down' | 'bidi'\n\nexport function formatThrottleRule(throttleRule: ThrottleRule, direction: ThrottleDirection) {\n const { rate, loss, delay } = throttleRule\n return `${direction}-r${rate}-l${loss}-d${delay}`\n}\n\nexport function parseThrottleRule(throttleDesc: string) {\n const match = throttleDesc.match(/(up|down|bidi)-r(\\d+)-l([\\d.]+)-d(\\d+)/)\n if (!match) throw new Error(`Invalid throttle description: ${throttleDesc}`)\n const direction = match[1] as ThrottleDirection\n const rate = parseInt(match[2])\n const loss = parseInt(match[3])\n const delay = parseInt(match[4])\n return { direction, rate, loss, delay }\n}\n\nexport async function simpleTestWithRateLossDelay(\n id: string,\n { rate, loss, delay, direction }: { rate: number; loss: number; delay: number; direction: ThrottleDirection },\n repeat: 1,\n) {\n const throttle: ThrottleConfig = {}\n const queue = 25\n if (direction === 'down' || direction === 'bidi') {\n throttle.down = [\n { rate: 20000, loss: 0, delay: 0, queue },\n { rate, loss, delay, queue, at: 30 },\n ]\n }\n if (direction === 'up' || direction === 'bidi') {\n throttle.up = [\n { rate: 20000, loss: 0, delay, queue },\n { rate, loss, delay, queue, at: 30 },\n ]\n }\n const throttleDesc = formatThrottleRule({ rate, loss, delay }, direction)\n const now = Date.now()\n const ret: Partial<Config>[] = []\n for (let i = 0; i < repeat; i++) {\n const basePath = `logs/${now}-${i + 1}_${id}_${throttleDesc}`\n const sessions = direction === 'bidi' ? '0-1' : direction === 'down' ? '0' : '1'\n ret.push({\n sessions: 2,\n runDuration: 60 * 3,\n debuggingPort: 9000,\n prometheusPushgateway: 'http://localhost:9091',\n prometheusPushgatewayJobName: id,\n statsPath: `${basePath}/stats.csv`,\n detailedStatsPath: `${basePath}/detailed-stats.csv`,\n showPageLog: false,\n showStats: false,\n statsInterval: 5,\n scriptParams: JSON.stringify({\n enableMic: '0-1',\n enableCam: '1',\n }),\n throttleConfig: JSON.stringify([\n {\n sessions,\n protocol: 'udp',\n skipSourcePorts: '53,80,443',\n skipDestinationPorts: '53,80,443',\n ...throttle,\n },\n ]),\n })\n }\n return ret\n}\n"]}
|
package/build/src/utils.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Session } from './session';
|
|
2
|
-
import { FastStats } from './stats';
|
|
3
2
|
export declare function logger(name: string, options?: {}): any;
|
|
4
3
|
/**
|
|
5
4
|
* Resolves the absolute path from the package installation directory.
|
|
@@ -235,21 +234,4 @@ export declare function analyzeColors(fpath: string): Promise<{
|
|
|
235
234
|
*/
|
|
236
235
|
export declare function waitStopProcess(pid: number, timeout?: number): Promise<boolean>;
|
|
237
236
|
export declare function getDockerLogsPath(): Promise<string>;
|
|
238
|
-
export declare function parseStatsFile(filePath: string): Promise<Record<string, string | number>[]>;
|
|
239
|
-
export declare function aggregateStatsSummary({ dirPath, senderParticipantName, receiverParticipantName, nameParser, }: {
|
|
240
|
-
dirPath?: string | undefined;
|
|
241
|
-
senderParticipantName?: string | undefined;
|
|
242
|
-
receiverParticipantName?: string | undefined;
|
|
243
|
-
nameParser?: ((name: string) => {
|
|
244
|
-
destination: string;
|
|
245
|
-
scenario: string;
|
|
246
|
-
}) | undefined;
|
|
247
|
-
}): Promise<{
|
|
248
|
-
timestamp: number;
|
|
249
|
-
destination: string;
|
|
250
|
-
scenario: string;
|
|
251
|
-
videoRecvBitratePerPixel: FastStats;
|
|
252
|
-
videoRecvFps: FastStats;
|
|
253
|
-
videoSentFps: FastStats;
|
|
254
|
-
}[]>;
|
|
255
237
|
export {};
|
package/build/src/utils.js
CHANGED
|
@@ -78,8 +78,6 @@ exports.ffmpeg = ffmpeg;
|
|
|
78
78
|
exports.analyzeColors = analyzeColors;
|
|
79
79
|
exports.waitStopProcess = waitStopProcess;
|
|
80
80
|
exports.getDockerLogsPath = getDockerLogsPath;
|
|
81
|
-
exports.parseStatsFile = parseStatsFile;
|
|
82
|
-
exports.aggregateStatsSummary = aggregateStatsSummary;
|
|
83
81
|
const browsers_1 = require("@puppeteer/browsers");
|
|
84
82
|
const child_process_1 = require("child_process");
|
|
85
83
|
const axios_1 = __importDefault(require("axios"));
|
|
@@ -97,7 +95,6 @@ const path_1 = __importStar(require("path"));
|
|
|
97
95
|
const pidtree_1 = __importDefault(require("pidtree"));
|
|
98
96
|
const pidusage_1 = __importDefault(require("pidusage"));
|
|
99
97
|
const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
|
|
100
|
-
const stats_1 = require("./stats");
|
|
101
98
|
// eslint-disable-next-line
|
|
102
99
|
const ps = require('pidusage/lib/ps');
|
|
103
100
|
function logger(name, options = {}) {
|
|
@@ -1197,60 +1194,4 @@ async function getDockerLogsPath() {
|
|
|
1197
1194
|
}
|
|
1198
1195
|
return logPath;
|
|
1199
1196
|
}
|
|
1200
|
-
async function parseStatsFile(filePath) {
|
|
1201
|
-
const fileData = await fs_1.default.promises.readFile(filePath, 'utf-8');
|
|
1202
|
-
const lines = fileData.split('\n');
|
|
1203
|
-
const headers = lines[0].split(',');
|
|
1204
|
-
const data = lines.slice(1).map(line => line.split(',').reduce((acc, value, index) => {
|
|
1205
|
-
if (value !== '') {
|
|
1206
|
-
acc[headers[index]] = isNaN(Number(value)) ? value : Number(value);
|
|
1207
|
-
}
|
|
1208
|
-
return acc;
|
|
1209
|
-
}, {}));
|
|
1210
|
-
return data;
|
|
1211
|
-
}
|
|
1212
|
-
async function aggregateStatsSummary({ dirPath = 'logs', senderParticipantName = 'Participant-000001', receiverParticipantName = 'Participant-000000', nameParser = (name) => {
|
|
1213
|
-
const [destination, scenario] = name.split('_');
|
|
1214
|
-
return { destination, scenario };
|
|
1215
|
-
}, }) {
|
|
1216
|
-
const stats = [];
|
|
1217
|
-
const results = await fs_1.default.promises.readdir(dirPath);
|
|
1218
|
-
for (const test of results) {
|
|
1219
|
-
const filePath = path_1.default.join(dirPath, test, 'detailed-stats-summary.csv');
|
|
1220
|
-
if (!fs_1.default.existsSync(filePath))
|
|
1221
|
-
continue;
|
|
1222
|
-
const timestamp = fs_1.default.statSync(path_1.default.join(dirPath, test)).ctime.getTime();
|
|
1223
|
-
const data = await parseStatsFile(filePath);
|
|
1224
|
-
const { destination, scenario } = nameParser(test);
|
|
1225
|
-
const aggregated = {
|
|
1226
|
-
timestamp,
|
|
1227
|
-
destination,
|
|
1228
|
-
scenario,
|
|
1229
|
-
videoRecvBitratePerPixel: new stats_1.FastStats(),
|
|
1230
|
-
videoRecvFps: new stats_1.FastStats(),
|
|
1231
|
-
videoSentFps: new stats_1.FastStats(),
|
|
1232
|
-
};
|
|
1233
|
-
data.forEach(v => {
|
|
1234
|
-
const { participantName, trackId } = v;
|
|
1235
|
-
const metrics = v;
|
|
1236
|
-
if (participantName === receiverParticipantName) {
|
|
1237
|
-
if (trackId?.endsWith('-v') && metrics.videoRecvFrames > 0) {
|
|
1238
|
-
const videoRecvBitratePerPixel = metrics.videoRecvBitrates / (metrics.videoRecvWidth * metrics.videoRecvHeight);
|
|
1239
|
-
if (!isNaN(videoRecvBitratePerPixel))
|
|
1240
|
-
aggregated.videoRecvBitratePerPixel.push(videoRecvBitratePerPixel);
|
|
1241
|
-
if (!isNaN(metrics.videoRecvFps))
|
|
1242
|
-
aggregated.videoRecvFps.push(metrics.videoRecvFps);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
else if (participantName === senderParticipantName) {
|
|
1246
|
-
if (trackId?.endsWith('-v') && metrics.videoSentFrames > 0) {
|
|
1247
|
-
if (!isNaN(metrics.videoSentFps))
|
|
1248
|
-
aggregated.videoSentFps.push(metrics.videoSentFps);
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
});
|
|
1252
|
-
stats.push(aggregated);
|
|
1253
|
-
}
|
|
1254
|
-
return stats.sort((a, b) => a.timestamp - b.timestamp);
|
|
1255
|
-
}
|
|
1256
1197
|
//# sourceMappingURL=utils.js.map
|