@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
@@ -0,0 +1,47 @@
1
+ import { writeFile } from 'fs/promises'
2
+
3
+ import { getConfigDocs } from './config'
4
+
5
+ function formatJson(data: string): string {
6
+ return `\`${data.replace(/\n/g, '')}\``
7
+ }
8
+
9
+ let data = `
10
+ The configuration properties are applied in the following order (from higher to
11
+ lower precedence):
12
+
13
+ - arguments passed to the executable in kebab case (e.g. \`--url-query\`);
14
+ - environment variables in uppercase snake format (e.g. \`URL_QUERY\`);
15
+ - \`config.json\` configuration file;
16
+ - default values.
17
+
18
+ `
19
+
20
+ const configDocs = getConfigDocs()
21
+ Object.entries(configDocs).forEach(entry => {
22
+ const [name, value] = entry
23
+ data += `\
24
+ ## ${name}
25
+ ${value.doc}
26
+
27
+ *Type*: \`${value.format === '"nat"' ? 'positive int' : value.format.replace(/^"(.+)"$/, '$1')}\`
28
+
29
+ *Default*: ${formatJson(value.default)}
30
+
31
+ `
32
+ })
33
+
34
+ data += `
35
+
36
+ ---
37
+
38
+ `
39
+
40
+ writeFile('docs/config.md', data).then(
41
+ () => {
42
+ console.log('done')
43
+ },
44
+ err => {
45
+ console.error(`Error writing file: ${err.message}`)
46
+ },
47
+ )
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ export * from './app'
2
+ export * from './config'
3
+ export * from './media'
4
+ export * from './rtcstats'
5
+ export * from './server'
6
+ export * from './session'
7
+ export * from './stats'
8
+ export * from './utils'
9
+ export * from './vmaf'
package/src/media.ts ADDED
@@ -0,0 +1,151 @@
1
+ import { existsSync, promises as fs } from 'fs'
2
+
3
+ import { logger, runShellCommand, sha256 } from './utils'
4
+
5
+ const log = logger('webrtcperf:media')
6
+
7
+ const DEFAULT_VIDEO_PATH = 'https://github.com/vpalmisano/webrtcperf/releases/download/v2.0.4/video.mp4'
8
+
9
+ export type MediaPath = {
10
+ video: string
11
+ audio: string
12
+ mp4: string
13
+ m4a: string
14
+ }
15
+
16
+ /**
17
+ * Converts the video file into raw audio and video files.
18
+ * @param {*} config
19
+ * @param {string} config.videoPath the video to convert
20
+ * @param {string} config.videoWidth the output video width
21
+ * @param {string} config.videoHeight the output video height
22
+ * @param {string} config.videoFramerate the output video framerate
23
+ * @param {string} config.videoSeek the seek position in seconds
24
+ * @param {string} config.videoDuration the output video duration in seconds
25
+ * @param {boolean} config.videoCacheRaw if true and the destinations raw files
26
+ * exist on file system, the conversion step is skipped
27
+ * @param {string} config.videoCachePath the destination directory path; if not
28
+ * existing, it will be created
29
+ * @param {string} config.videoFormat the raw video format (y4m or mjpeg)
30
+ */
31
+ export async function prepareFakeMedia({
32
+ videoPath,
33
+ videoWidth,
34
+ videoHeight,
35
+ videoFramerate,
36
+ videoSeek,
37
+ videoDuration,
38
+ videoCacheRaw,
39
+ videoCachePath,
40
+ videoFormat,
41
+ useFakeMedia,
42
+ }: {
43
+ videoPath: string
44
+ videoWidth: number
45
+ videoHeight: number
46
+ videoFramerate: number
47
+ videoSeek: number
48
+ videoDuration: number
49
+ videoCacheRaw: boolean
50
+ videoCachePath: string
51
+ videoFormat: string
52
+ useFakeMedia: boolean
53
+ }): Promise<MediaPath> {
54
+ log.debug('prepareFakeMedia', {
55
+ videoPath,
56
+ videoWidth,
57
+ videoHeight,
58
+ videoFramerate,
59
+ videoSeek,
60
+ videoDuration,
61
+ videoCacheRaw,
62
+ videoCachePath,
63
+ videoFormat,
64
+ useFakeMedia,
65
+ })
66
+ if (!videoPath) {
67
+ throw new Error('empty video path')
68
+ }
69
+ if (!videoPath.startsWith('http') && !videoPath.startsWith('generate:') && !existsSync(videoPath)) {
70
+ log.warn(`video not found: ${videoPath}, using default test video`)
71
+ videoPath = DEFAULT_VIDEO_PATH
72
+ }
73
+
74
+ await fs.mkdir(videoCachePath, { recursive: true })
75
+ const name = sha256(videoPath)
76
+
77
+ const destVideoPath = `${videoCachePath}/${name}_${videoWidth}x${videoHeight}_${videoFramerate}fps.${videoFormat}`
78
+ const destAudioPath = `${videoCachePath}/${name}.wav`
79
+ const destMp4Path = useFakeMedia
80
+ ? ''
81
+ : `${videoCachePath}/${name}_${videoWidth}x${videoHeight}_${videoFramerate}fps.mp4`
82
+ const destM4aPath = useFakeMedia ? '' : `${videoCachePath}/${name}.m4a`
83
+
84
+ if (
85
+ !existsSync(destVideoPath) ||
86
+ !existsSync(destAudioPath) ||
87
+ (destMp4Path && !existsSync(destMp4Path)) ||
88
+ (destM4aPath && !existsSync(destM4aPath)) ||
89
+ !videoCacheRaw
90
+ ) {
91
+ log.info(
92
+ `Converting ${videoPath} to ${destVideoPath}, ${destAudioPath}${destMp4Path ? `, ${destMp4Path}` : ''}${destM4aPath ? `, ${destM4aPath}` : ''}`,
93
+ )
94
+ const destVideoPathTmp = `${videoCachePath}/${name}_${videoWidth}x${videoHeight}_${videoFramerate}fps.tmp.${videoFormat}`
95
+ const destAudioPathTmp = `${videoCachePath}/${name}.tmp.wav`
96
+ const destMp4PathTmp = useFakeMedia
97
+ ? ''
98
+ : `${videoCachePath}/${name}_${videoWidth}x${videoHeight}_${videoFramerate}fps.tmp.mp4`
99
+ const destM4aPathTmp = useFakeMedia ? '' : `${videoCachePath}/${name}.tmp.m4a`
100
+
101
+ try {
102
+ let source = `-i "${videoPath}"`
103
+ const videoMap = `-map 0:v`
104
+ const audioMap = videoPath.startsWith('generate:') ? '-map 1:a' : '-map 0:a'
105
+ if (videoPath === 'generate:null') {
106
+ source =
107
+ `-f lavfi -i color=size=${videoWidth}x${videoHeight}:rate=${videoFramerate}:color=black` +
108
+ ` -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000`
109
+ } else if (videoPath === 'generate:test') {
110
+ source =
111
+ `-f lavfi -i testsrc=size=${videoWidth}x${videoHeight}:rate=${videoFramerate} -pix_fmt yuv420p` +
112
+ ` -f lavfi -i sine=frequency=220:beep_factor=4:sample_rate=48000`
113
+ }
114
+ await runShellCommand(
115
+ `ffmpeg -loglevel warning -y -threads 0 ${source}` +
116
+ ` -s ${videoWidth}:${videoHeight}` +
117
+ ` -r ${videoFramerate}` +
118
+ ` -ss ${videoSeek} -t ${videoDuration} -shortest -af apad` +
119
+ ` ${videoMap} ${destVideoPathTmp}` +
120
+ ` ${audioMap} -ar 48000 ${destAudioPathTmp}` +
121
+ (destMp4PathTmp
122
+ ? ` ${videoMap} -c:v libx264 -crf 10 -f mp4 -movflags faststart ${destMp4PathTmp}` +
123
+ ` ${audioMap} -c:a aac -b:a 192k ${destM4aPathTmp}`
124
+ : ''),
125
+ )
126
+ await fs.rename(destVideoPathTmp, destVideoPath)
127
+ await fs.rename(destAudioPathTmp, destAudioPath)
128
+ if (destMp4PathTmp) {
129
+ await fs.rename(destMp4PathTmp, destMp4Path)
130
+ }
131
+ if (destM4aPathTmp) {
132
+ await fs.rename(destM4aPathTmp, destM4aPath)
133
+ }
134
+ } catch (err) {
135
+ log.error(`Error converting video: ${(err as Error).stack}`)
136
+ fs.unlink(destVideoPathTmp).catch(e => log.debug(e.message))
137
+ fs.unlink(destAudioPathTmp).catch(e => log.debug(e.message))
138
+ if (destMp4PathTmp) {
139
+ fs.unlink(destMp4PathTmp).catch(e => log.debug(e.message))
140
+ }
141
+ throw err
142
+ }
143
+ }
144
+
145
+ return {
146
+ video: destVideoPath,
147
+ audio: destAudioPath,
148
+ mp4: destMp4Path,
149
+ m4a: destM4aPath,
150
+ }
151
+ }