node-av 5.1.0 → 5.2.0-beta.2
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/dist/api/rtp-stream.js +0 -6
- package/dist/api/rtp-stream.js.map +1 -1
- package/package.json +26 -11
- package/benchmarks/cases/latency.ts +0 -361
- package/benchmarks/cases/memory.ts +0 -260
- package/benchmarks/cases/transcode.ts +0 -271
- package/benchmarks/index.ts +0 -264
- package/benchmarks/regen-report.ts +0 -22
- package/benchmarks/results/.gitkeep +0 -2
- package/benchmarks/runner.ts +0 -247
- package/benchmarks/utils/ffmpeg-cli.ts +0 -363
- package/benchmarks/utils/measure.ts +0 -275
- package/benchmarks/utils/report.ts +0 -405
- package/binding.gyp +0 -166
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory Usage Benchmark Tests
|
|
3
|
-
*
|
|
4
|
-
* Compares memory consumption between FFmpeg CLI and node-av
|
|
5
|
-
* during media processing operations.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { dirname, join, resolve } from 'node:path';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
|
|
11
|
-
import { Decoder, Demuxer, Encoder, Muxer, pipeline } from '../../src/api/index.js';
|
|
12
|
-
import { FF_ENCODER_LIBX264 } from '../../src/constants/encoders.js';
|
|
13
|
-
import { runner } from '../runner.js';
|
|
14
|
-
import { FFmpegArgs } from '../utils/ffmpeg-cli.js';
|
|
15
|
-
import { MemorySampler } from '../utils/measure.js';
|
|
16
|
-
|
|
17
|
-
import type { BenchmarkConfig } from '../runner.js';
|
|
18
|
-
|
|
19
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
-
const __dirname = dirname(__filename);
|
|
21
|
-
|
|
22
|
-
// Default paths
|
|
23
|
-
const testDataDir = resolve(__dirname, '../../testdata');
|
|
24
|
-
const resultsDir = resolve(__dirname, '../results');
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Configuration for memory-focused benchmarks
|
|
28
|
-
*/
|
|
29
|
-
const memoryConfig = {
|
|
30
|
-
iterations: 3,
|
|
31
|
-
warmupIterations: 1,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Memory usage during H.264 transcode
|
|
36
|
-
*/
|
|
37
|
-
export async function benchmarkMemoryH264Transcode(inputFile?: string, outputFile?: string): Promise<void> {
|
|
38
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
39
|
-
const output = outputFile ?? join(resultsDir, 'memory-h264-output.mp4');
|
|
40
|
-
|
|
41
|
-
const config: BenchmarkConfig = {
|
|
42
|
-
name: 'Memory: H.264 Transcode',
|
|
43
|
-
description: 'Peak memory usage during H.264 software transcoding',
|
|
44
|
-
category: 'memory',
|
|
45
|
-
inputFile: input,
|
|
46
|
-
outputFile: output,
|
|
47
|
-
...memoryConfig,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const ffmpegOptions = {
|
|
51
|
-
input,
|
|
52
|
-
output,
|
|
53
|
-
args: FFmpegArgs.swH264(23),
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const nodeAVFn = async () => {
|
|
57
|
-
await using demuxer = await Demuxer.open(input);
|
|
58
|
-
const videoStream = demuxer.video();
|
|
59
|
-
if (!videoStream) throw new Error('No video stream found');
|
|
60
|
-
|
|
61
|
-
const decoder = await Decoder.create(videoStream);
|
|
62
|
-
const encoder = await Encoder.create(FF_ENCODER_LIBX264, {
|
|
63
|
-
decoder,
|
|
64
|
-
options: { preset: 'medium', crf: 23 },
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
await using muxer = await Muxer.open(output);
|
|
68
|
-
|
|
69
|
-
const control = pipeline(demuxer, decoder, encoder, muxer);
|
|
70
|
-
await control.completion;
|
|
71
|
-
|
|
72
|
-
// Get frame count from codec context
|
|
73
|
-
const ctx = encoder.getCodecContext();
|
|
74
|
-
return { framesProcessed: ctx?.frameNumber };
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
await runner.runBenchmark(config, ffmpegOptions, nodeAVFn);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Memory usage during stream copy
|
|
82
|
-
*/
|
|
83
|
-
export async function benchmarkMemoryStreamCopy(inputFile?: string, outputFile?: string): Promise<void> {
|
|
84
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
85
|
-
const output = outputFile ?? join(resultsDir, 'memory-copy-output.mp4');
|
|
86
|
-
|
|
87
|
-
const config: BenchmarkConfig = {
|
|
88
|
-
name: 'Memory: Stream Copy',
|
|
89
|
-
description: 'Peak memory usage during stream copy (remux)',
|
|
90
|
-
category: 'memory',
|
|
91
|
-
inputFile: input,
|
|
92
|
-
outputFile: output,
|
|
93
|
-
...memoryConfig,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const ffmpegOptions = {
|
|
97
|
-
input,
|
|
98
|
-
output,
|
|
99
|
-
args: FFmpegArgs.streamCopy(),
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const nodeAVFn = async () => {
|
|
103
|
-
await using demuxer = await Demuxer.open(input);
|
|
104
|
-
await using muxer = await Muxer.open(output);
|
|
105
|
-
|
|
106
|
-
const control = pipeline(demuxer, muxer);
|
|
107
|
-
await control.completion;
|
|
108
|
-
|
|
109
|
-
return {};
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
await runner.runBenchmark(config, ffmpegOptions, nodeAVFn);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Detailed memory profiling for node-av
|
|
117
|
-
* This provides more granular memory analysis beyond peak usage
|
|
118
|
-
*/
|
|
119
|
-
export async function profileNodeAVMemory(inputFile?: string): Promise<void> {
|
|
120
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
121
|
-
|
|
122
|
-
console.log('\n📊 Detailed Memory Profile for node-av\n');
|
|
123
|
-
console.log('='.repeat(60));
|
|
124
|
-
|
|
125
|
-
// Force GC before starting
|
|
126
|
-
if (global.gc) {
|
|
127
|
-
global.gc();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const sampler = new MemorySampler();
|
|
131
|
-
sampler.start(50); // Sample every 50ms
|
|
132
|
-
|
|
133
|
-
const baselineMemory = process.memoryUsage();
|
|
134
|
-
console.log('Baseline Memory:');
|
|
135
|
-
console.log(` RSS: ${formatBytes(baselineMemory.rss)}`);
|
|
136
|
-
console.log(` Heap Used: ${formatBytes(baselineMemory.heapUsed)}`);
|
|
137
|
-
console.log(` Heap Total: ${formatBytes(baselineMemory.heapTotal)}`);
|
|
138
|
-
console.log(` External: ${formatBytes(baselineMemory.external)}`);
|
|
139
|
-
console.log('');
|
|
140
|
-
|
|
141
|
-
// Open demuxer
|
|
142
|
-
console.log('Opening demuxer...');
|
|
143
|
-
const demuxer = await Demuxer.open(input);
|
|
144
|
-
const afterDemuxer = process.memoryUsage();
|
|
145
|
-
console.log(` After Demuxer: +${formatBytes(afterDemuxer.rss - baselineMemory.rss)} RSS`);
|
|
146
|
-
|
|
147
|
-
// Create decoder
|
|
148
|
-
const videoStream = demuxer.video();
|
|
149
|
-
if (!videoStream) {
|
|
150
|
-
await demuxer.close();
|
|
151
|
-
throw new Error('No video stream found');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
console.log('Creating decoder...');
|
|
155
|
-
const decoder = await Decoder.create(videoStream);
|
|
156
|
-
const afterDecoder = process.memoryUsage();
|
|
157
|
-
console.log(` After Decoder: +${formatBytes(afterDecoder.rss - afterDemuxer.rss)} RSS`);
|
|
158
|
-
|
|
159
|
-
// Create encoder
|
|
160
|
-
console.log('Creating encoder...');
|
|
161
|
-
const encoder = await Encoder.create(FF_ENCODER_LIBX264, {
|
|
162
|
-
decoder,
|
|
163
|
-
options: { preset: 'ultrafast', crf: 23 },
|
|
164
|
-
});
|
|
165
|
-
const afterEncoder = process.memoryUsage();
|
|
166
|
-
console.log(` After Encoder: +${formatBytes(afterEncoder.rss - afterDecoder.rss)} RSS`);
|
|
167
|
-
|
|
168
|
-
// Process frames
|
|
169
|
-
console.log('Processing frames...');
|
|
170
|
-
let frameCount = 0;
|
|
171
|
-
let maxMemoryDuringProcess = process.memoryUsage().rss;
|
|
172
|
-
|
|
173
|
-
for await (const packet of demuxer.packets(videoStream.index)) {
|
|
174
|
-
if (!packet) continue;
|
|
175
|
-
|
|
176
|
-
for await (const frame of decoder.frames(packet)) {
|
|
177
|
-
if (!frame) continue;
|
|
178
|
-
frameCount++;
|
|
179
|
-
|
|
180
|
-
for await (const _ of encoder.packets(frame)) {
|
|
181
|
-
// Just encode and discard
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Sample memory periodically
|
|
185
|
-
if (frameCount % 30 === 0) {
|
|
186
|
-
const currentMem = process.memoryUsage().rss;
|
|
187
|
-
if (currentMem > maxMemoryDuringProcess) {
|
|
188
|
-
maxMemoryDuringProcess = currentMem;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Flush
|
|
195
|
-
for await (const frame of decoder.frames(null)) {
|
|
196
|
-
if (!frame) continue;
|
|
197
|
-
for await (const _ of encoder.packets(frame)) {
|
|
198
|
-
// Just encode and discard
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
for await (const _ of encoder.packets(null)) {
|
|
202
|
-
// Just encode and discard
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const { samples, peakMemory } = sampler.stop();
|
|
206
|
-
|
|
207
|
-
console.log('\nProcessing Complete:');
|
|
208
|
-
console.log(` Frames: ${frameCount}`);
|
|
209
|
-
console.log(` Peak Memory: ${formatBytes(peakMemory)}`);
|
|
210
|
-
console.log(` Memory Samples: ${samples.length}`);
|
|
211
|
-
console.log(` Memory Growth: ${formatBytes(peakMemory - baselineMemory.rss)}`);
|
|
212
|
-
|
|
213
|
-
// Cleanup
|
|
214
|
-
await demuxer.close();
|
|
215
|
-
|
|
216
|
-
// Force GC and check final memory
|
|
217
|
-
if (global.gc) {
|
|
218
|
-
global.gc();
|
|
219
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
220
|
-
global.gc();
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const finalMemory = process.memoryUsage();
|
|
224
|
-
console.log('\nAfter Cleanup:');
|
|
225
|
-
console.log(` RSS: ${formatBytes(finalMemory.rss)}`);
|
|
226
|
-
console.log(` Retained: ${formatBytes(finalMemory.rss - baselineMemory.rss)}`);
|
|
227
|
-
|
|
228
|
-
console.log('\n' + '='.repeat(60));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Format bytes to human readable string
|
|
233
|
-
*/
|
|
234
|
-
function formatBytes(bytes: number): string {
|
|
235
|
-
const units = ['B', 'KB', 'MB', 'GB'];
|
|
236
|
-
let value = bytes;
|
|
237
|
-
let unitIndex = 0;
|
|
238
|
-
|
|
239
|
-
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
240
|
-
value /= 1024;
|
|
241
|
-
unitIndex++;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Run all memory benchmarks
|
|
249
|
-
*/
|
|
250
|
-
export async function runAllMemoryBenchmarks(inputFile?: string): Promise<void> {
|
|
251
|
-
console.log('\n💾 Running Memory Usage Benchmarks\n');
|
|
252
|
-
console.log('='.repeat(60));
|
|
253
|
-
|
|
254
|
-
await benchmarkMemoryH264Transcode(inputFile);
|
|
255
|
-
await benchmarkMemoryStreamCopy(inputFile);
|
|
256
|
-
await profileNodeAVMemory(inputFile);
|
|
257
|
-
|
|
258
|
-
console.log('\n' + '='.repeat(60));
|
|
259
|
-
console.log('Memory benchmarks completed\n');
|
|
260
|
-
}
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transcode Speed Benchmark Tests
|
|
3
|
-
*
|
|
4
|
-
* Compares transcoding performance between FFmpeg CLI and node-av
|
|
5
|
-
* for various codec configurations.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { dirname, join, resolve } from 'node:path';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
|
|
11
|
-
import { Decoder, Demuxer, Encoder, HardwareContext, Muxer, pipeline } from '../../src/api/index.js';
|
|
12
|
-
import { FF_ENCODER_LIBX264, FF_ENCODER_LIBX265 } from '../../src/constants/encoders.js';
|
|
13
|
-
import { runner } from '../runner.js';
|
|
14
|
-
import { FFmpegArgs } from '../utils/ffmpeg-cli.js';
|
|
15
|
-
|
|
16
|
-
import type { BenchmarkConfig } from '../runner.js';
|
|
17
|
-
|
|
18
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
-
const __dirname = dirname(__filename);
|
|
20
|
-
|
|
21
|
-
// Default paths
|
|
22
|
-
const testDataDir = resolve(__dirname, '../../testdata');
|
|
23
|
-
const resultsDir = resolve(__dirname, '../results');
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Default benchmark configuration
|
|
27
|
-
*/
|
|
28
|
-
const defaultConfig = {
|
|
29
|
-
iterations: 5,
|
|
30
|
-
warmupIterations: 1,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Software H.264 Transcode Benchmark
|
|
35
|
-
*/
|
|
36
|
-
export async function benchmarkSWH264(inputFile?: string, outputFile?: string): Promise<void> {
|
|
37
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
38
|
-
const output = outputFile ?? join(resultsDir, 'sw-h264-output.mp4');
|
|
39
|
-
|
|
40
|
-
const config: BenchmarkConfig = {
|
|
41
|
-
name: 'SW H.264 Transcode',
|
|
42
|
-
description: 'Software H.264 encoding with libx264 (CPU)',
|
|
43
|
-
category: 'transcode',
|
|
44
|
-
inputFile: input,
|
|
45
|
-
outputFile: output,
|
|
46
|
-
...defaultConfig,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const ffmpegOptions = {
|
|
50
|
-
input,
|
|
51
|
-
output,
|
|
52
|
-
args: FFmpegArgs.swH264(23),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const nodeAVFn = async () => {
|
|
56
|
-
await using demuxer = await Demuxer.open(input);
|
|
57
|
-
const videoStream = demuxer.video();
|
|
58
|
-
if (!videoStream) throw new Error('No video stream found');
|
|
59
|
-
|
|
60
|
-
const decoder = await Decoder.create(videoStream);
|
|
61
|
-
const encoder = await Encoder.create(FF_ENCODER_LIBX264, {
|
|
62
|
-
decoder,
|
|
63
|
-
options: { preset: 'medium', crf: 23 },
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
await using muxer = await Muxer.open(output);
|
|
67
|
-
|
|
68
|
-
const control = pipeline(demuxer, decoder, encoder, muxer);
|
|
69
|
-
await control.completion;
|
|
70
|
-
|
|
71
|
-
// Get frame count from codec context
|
|
72
|
-
const ctx = encoder.getCodecContext();
|
|
73
|
-
return { framesProcessed: ctx?.frameNumber };
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
await runner.runBenchmark(config, ffmpegOptions, nodeAVFn);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Software H.265/HEVC Transcode Benchmark
|
|
81
|
-
*/
|
|
82
|
-
export async function benchmarkSWH265(inputFile?: string, outputFile?: string): Promise<void> {
|
|
83
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
84
|
-
const output = outputFile ?? join(resultsDir, 'sw-h265-output.mp4');
|
|
85
|
-
|
|
86
|
-
const config: BenchmarkConfig = {
|
|
87
|
-
name: 'SW H.265 Transcode',
|
|
88
|
-
description: 'Software H.265/HEVC encoding with libx265 (CPU)',
|
|
89
|
-
category: 'transcode',
|
|
90
|
-
inputFile: input,
|
|
91
|
-
outputFile: output,
|
|
92
|
-
...defaultConfig,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const ffmpegOptions = {
|
|
96
|
-
input,
|
|
97
|
-
output,
|
|
98
|
-
args: FFmpegArgs.swH265(28),
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const nodeAVFn = async () => {
|
|
102
|
-
await using demuxer = await Demuxer.open(input);
|
|
103
|
-
const videoStream = demuxer.video();
|
|
104
|
-
if (!videoStream) throw new Error('No video stream found');
|
|
105
|
-
|
|
106
|
-
const decoder = await Decoder.create(videoStream);
|
|
107
|
-
const encoder = await Encoder.create(FF_ENCODER_LIBX265, {
|
|
108
|
-
decoder,
|
|
109
|
-
options: { preset: 'medium', crf: 28 },
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
await using muxer = await Muxer.open(output);
|
|
113
|
-
|
|
114
|
-
const control = pipeline(demuxer, decoder, encoder, muxer);
|
|
115
|
-
await control.completion;
|
|
116
|
-
|
|
117
|
-
// Get frame count from codec context
|
|
118
|
-
const ctx = encoder.getCodecContext();
|
|
119
|
-
return { framesProcessed: ctx?.frameNumber };
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
await runner.runBenchmark(config, ffmpegOptions, nodeAVFn);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Hardware H.264 Transcode Benchmark (platform-dependent)
|
|
127
|
-
*/
|
|
128
|
-
export async function benchmarkHWH264(inputFile?: string, outputFile?: string): Promise<void> {
|
|
129
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
130
|
-
const output = outputFile ?? join(resultsDir, 'hw-h264-output.mp4');
|
|
131
|
-
|
|
132
|
-
// Check hardware availability
|
|
133
|
-
const hw = HardwareContext.auto();
|
|
134
|
-
if (!hw) {
|
|
135
|
-
console.log('⚠️ Skipping HW H.264 benchmark: No hardware acceleration available');
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const hwEncoderCodec = hw.getEncoderCodec('h264');
|
|
140
|
-
if (!hwEncoderCodec) {
|
|
141
|
-
console.log('⚠️ Skipping HW H.264 benchmark: No hardware H.264 encoder available');
|
|
142
|
-
hw.dispose();
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Determine FFmpeg hardware encoder based on platform
|
|
147
|
-
let ffmpegArgs: string[];
|
|
148
|
-
const platform = process.platform;
|
|
149
|
-
|
|
150
|
-
if (platform === 'darwin') {
|
|
151
|
-
ffmpegArgs = FFmpegArgs.hwH264VideoToolbox();
|
|
152
|
-
} else if (platform === 'linux') {
|
|
153
|
-
// Try VAAPI first, then NVENC
|
|
154
|
-
ffmpegArgs = FFmpegArgs.hwH264Vaapi();
|
|
155
|
-
} else if (platform === 'win32') {
|
|
156
|
-
ffmpegArgs = FFmpegArgs.hwH264Nvenc();
|
|
157
|
-
} else {
|
|
158
|
-
console.log('⚠️ Skipping HW H.264 benchmark: Unknown platform');
|
|
159
|
-
hw.dispose();
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const config: BenchmarkConfig = {
|
|
164
|
-
name: 'HW H.264 Transcode',
|
|
165
|
-
description: `Hardware H.264 encoding (${hw.deviceTypeName})`,
|
|
166
|
-
category: 'transcode',
|
|
167
|
-
inputFile: input,
|
|
168
|
-
outputFile: output,
|
|
169
|
-
...defaultConfig,
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const ffmpegOptions = {
|
|
173
|
-
input,
|
|
174
|
-
output,
|
|
175
|
-
args: ffmpegArgs,
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const nodeAVFn = async () => {
|
|
179
|
-
await using demuxer = await Demuxer.open(input);
|
|
180
|
-
const videoStream = demuxer.video();
|
|
181
|
-
if (!videoStream) throw new Error('No video stream found');
|
|
182
|
-
|
|
183
|
-
const decoder = await Decoder.create(videoStream, { hardware: hw });
|
|
184
|
-
const encoder = await Encoder.create(hwEncoderCodec, {
|
|
185
|
-
decoder,
|
|
186
|
-
bitrate: '2M',
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
await using muxer = await Muxer.open(output);
|
|
190
|
-
|
|
191
|
-
const control = pipeline(demuxer, decoder, encoder, muxer);
|
|
192
|
-
await control.completion;
|
|
193
|
-
|
|
194
|
-
// Get frame count from codec context
|
|
195
|
-
const ctx = encoder.getCodecContext();
|
|
196
|
-
return { framesProcessed: ctx?.frameNumber };
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
await runner.runBenchmark(config, ffmpegOptions, nodeAVFn);
|
|
201
|
-
} finally {
|
|
202
|
-
hw.dispose();
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Stream Copy (Remux) Benchmark
|
|
208
|
-
*/
|
|
209
|
-
export async function benchmarkStreamCopy(inputFile?: string, outputFile?: string): Promise<void> {
|
|
210
|
-
const input = inputFile ?? join(testDataDir, 'video.mp4');
|
|
211
|
-
const output = outputFile ?? join(resultsDir, 'stream-copy-output.mp4');
|
|
212
|
-
|
|
213
|
-
const config: BenchmarkConfig = {
|
|
214
|
-
name: 'Stream Copy (Remux)',
|
|
215
|
-
description: 'Copy streams without re-encoding',
|
|
216
|
-
category: 'transcode',
|
|
217
|
-
inputFile: input,
|
|
218
|
-
outputFile: output,
|
|
219
|
-
...defaultConfig,
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const ffmpegOptions = {
|
|
223
|
-
input,
|
|
224
|
-
output,
|
|
225
|
-
args: FFmpegArgs.streamCopy(),
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const nodeAVFn = async () => {
|
|
229
|
-
await using demuxer = await Demuxer.open(input);
|
|
230
|
-
await using muxer = await Muxer.open(output);
|
|
231
|
-
|
|
232
|
-
// Add streams from demuxer to muxer (required for stream copy)
|
|
233
|
-
const streamMap = new Map<number, number>();
|
|
234
|
-
for (const stream of demuxer.streams) {
|
|
235
|
-
const outIndex = muxer.addStream(stream);
|
|
236
|
-
streamMap.set(stream.index, outIndex);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Manual packet copy to count packets
|
|
240
|
-
let packetCount = 0;
|
|
241
|
-
for await (const packet of demuxer.packets()) {
|
|
242
|
-
if (!packet) break;
|
|
243
|
-
const outIndex = streamMap.get(packet.streamIndex);
|
|
244
|
-
if (outIndex !== undefined) {
|
|
245
|
-
await muxer.writePacket(packet, outIndex);
|
|
246
|
-
packetCount++;
|
|
247
|
-
}
|
|
248
|
-
packet.free();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return { framesProcessed: packetCount };
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
await runner.runBenchmark(config, ffmpegOptions, nodeAVFn);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Run all transcode benchmarks
|
|
259
|
-
*/
|
|
260
|
-
export async function runAllTranscodeBenchmarks(inputFile?: string): Promise<void> {
|
|
261
|
-
console.log('\n🎬 Running Transcode Speed Benchmarks\n');
|
|
262
|
-
console.log('='.repeat(60));
|
|
263
|
-
|
|
264
|
-
await benchmarkSWH264(inputFile);
|
|
265
|
-
await benchmarkSWH265(inputFile);
|
|
266
|
-
await benchmarkHWH264(inputFile);
|
|
267
|
-
await benchmarkStreamCopy(inputFile);
|
|
268
|
-
|
|
269
|
-
console.log('\n' + '='.repeat(60));
|
|
270
|
-
console.log('Transcode benchmarks completed\n');
|
|
271
|
-
}
|