webcodecs-node 0.7.2 → 0.7.5
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 +94 -12
- package/dist/config/ffmpeg-quality.d.ts +10 -1
- package/dist/config/ffmpeg-quality.d.ts.map +1 -1
- package/dist/config/ffmpeg-quality.js +12 -38
- package/dist/config/ffmpeg-quality.js.map +1 -1
- package/dist/config/webcodecs-config.d.ts +58 -0
- package/dist/config/webcodecs-config.d.ts.map +1 -0
- package/dist/config/webcodecs-config.js +187 -0
- package/dist/config/webcodecs-config.js.map +1 -0
- package/dist/containers/Demuxer.d.ts +3 -1
- package/dist/containers/Demuxer.d.ts.map +1 -1
- package/dist/containers/Demuxer.js +26 -19
- package/dist/containers/Demuxer.js.map +1 -1
- package/dist/containers/FFmpegMuxer.d.ts +42 -0
- package/dist/containers/FFmpegMuxer.d.ts.map +1 -0
- package/dist/containers/FFmpegMuxer.js +311 -0
- package/dist/containers/FFmpegMuxer.js.map +1 -0
- package/dist/containers/FFmpegSpawnMuxer.d.ts +42 -0
- package/dist/containers/FFmpegSpawnMuxer.d.ts.map +1 -0
- package/dist/containers/FFmpegSpawnMuxer.js +311 -0
- package/dist/containers/FFmpegSpawnMuxer.js.map +1 -0
- package/dist/containers/FallbackMuxer.d.ts +42 -0
- package/dist/containers/FallbackMuxer.d.ts.map +1 -0
- package/dist/containers/FallbackMuxer.js +311 -0
- package/dist/containers/FallbackMuxer.js.map +1 -0
- package/dist/containers/Muxer.d.ts +75 -107
- package/dist/containers/Muxer.d.ts.map +1 -1
- package/dist/containers/Muxer.js +184 -243
- package/dist/containers/Muxer.js.map +1 -1
- package/dist/containers/MuxerWithFallback.d.ts +110 -0
- package/dist/containers/MuxerWithFallback.d.ts.map +1 -0
- package/dist/containers/MuxerWithFallback.js +239 -0
- package/dist/containers/MuxerWithFallback.js.map +1 -0
- package/dist/containers/NodeAvMuxer.d.ts +118 -0
- package/dist/containers/NodeAvMuxer.d.ts.map +1 -0
- package/dist/containers/NodeAvMuxer.js +338 -0
- package/dist/containers/NodeAvMuxer.js.map +1 -0
- package/dist/containers/extract.d.ts.map +1 -1
- package/dist/containers/extract.js +3 -1
- package/dist/containers/extract.js.map +1 -1
- package/dist/containers/index.d.ts +20 -14
- package/dist/containers/index.d.ts.map +1 -1
- package/dist/containers/index.js +21 -14
- package/dist/containers/index.js.map +1 -1
- package/dist/containers/muxer-types.d.ts +117 -0
- package/dist/containers/muxer-types.d.ts.map +1 -0
- package/dist/containers/muxer-types.js +45 -0
- package/dist/containers/muxer-types.js.map +1 -0
- package/dist/containers/transcode.d.ts.map +1 -1
- package/dist/containers/transcode.js +171 -150
- package/dist/containers/transcode.js.map +1 -1
- package/dist/core/VideoFrame.d.ts +19 -0
- package/dist/core/VideoFrame.d.ts.map +1 -1
- package/dist/core/VideoFrame.js +11 -0
- package/dist/core/VideoFrame.js.map +1 -1
- package/dist/decoders/VideoDecoder.d.ts +1 -0
- package/dist/decoders/VideoDecoder.d.ts.map +1 -1
- package/dist/decoders/VideoDecoder.js +6 -4
- package/dist/decoders/VideoDecoder.js.map +1 -1
- package/dist/demos/demo-audio-visualizer-mediabunny.d.ts +10 -0
- package/dist/demos/demo-audio-visualizer-mediabunny.d.ts.map +1 -0
- package/dist/demos/demo-audio-visualizer-mediabunny.js +357 -0
- package/dist/demos/demo-audio-visualizer-mediabunny.js.map +1 -0
- package/dist/demos/demo-audio-visualizer-nodeav.d.ts +10 -0
- package/dist/demos/demo-audio-visualizer-nodeav.d.ts.map +1 -0
- package/dist/demos/demo-audio-visualizer-nodeav.js +318 -0
- package/dist/demos/demo-audio-visualizer-nodeav.js.map +1 -0
- package/dist/demos/demo-muxer-fallback.d.ts +8 -0
- package/dist/demos/demo-muxer-fallback.d.ts.map +1 -0
- package/dist/demos/demo-muxer-fallback.js +165 -0
- package/dist/demos/demo-muxer-fallback.js.map +1 -0
- package/dist/encoders/AudioEncoder.d.ts +2 -0
- package/dist/encoders/AudioEncoder.d.ts.map +1 -1
- package/dist/encoders/AudioEncoder.js +7 -4
- package/dist/encoders/AudioEncoder.js.map +1 -1
- package/dist/hardware/decoder-args.d.ts.map +1 -1
- package/dist/hardware/decoder-args.js +35 -14
- package/dist/hardware/decoder-args.js.map +1 -1
- package/dist/hardware/detection.d.ts.map +1 -1
- package/dist/hardware/detection.js +39 -0
- package/dist/hardware/detection.js.map +1 -1
- package/dist/hardware/encoder-args.d.ts.map +1 -1
- package/dist/hardware/encoder-args.js +43 -5
- package/dist/hardware/encoder-args.js.map +1 -1
- package/dist/hardware/types.d.ts.map +1 -1
- package/dist/hardware/types.js +30 -28
- package/dist/hardware/types.js.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.d.ts +5 -0
- package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.js +76 -23
- package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
- package/dist/utils/avc.d.ts +2 -0
- package/dist/utils/avc.d.ts.map +1 -1
- package/dist/utils/avc.js +36 -8
- package/dist/utils/avc.js.map +1 -1
- package/dist/utils/codec-validation.d.ts.map +1 -1
- package/dist/utils/codec-validation.js +18 -8
- package/dist/utils/codec-validation.js.map +1 -1
- package/dist/utils/hevc.d.ts +2 -0
- package/dist/utils/hevc.d.ts.map +1 -1
- package/dist/utils/hevc.js +42 -8
- package/dist/utils/hevc.js.map +1 -1
- package/docs/api.md +20 -2
- package/docs/configuration.md +10 -7
- package/package.json +1 -1
package/dist/containers/Muxer.js
CHANGED
|
@@ -1,298 +1,239 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Muxer - Primary muxer with automatic fallback
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* This muxer attempts to use the fast node-av muxer first, and automatically
|
|
5
|
+
* falls back to FFmpeg spawn if it fails. This provides the best of both worlds:
|
|
6
|
+
* fast muxing when possible, with reliable fallback for edge cases.
|
|
6
7
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { MuxerError } from './muxer-types.js';
|
|
9
|
+
import { NodeAvMuxer } from './NodeAvMuxer.js';
|
|
10
|
+
import { FFmpegMuxer } from './FFmpegMuxer.js';
|
|
9
11
|
/**
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return AV_CODEC_ID_H264;
|
|
16
|
-
}
|
|
17
|
-
else if (codecLower.startsWith('hvc1') || codecLower.startsWith('hev1') || codecLower === 'hevc') {
|
|
18
|
-
return AV_CODEC_ID_HEVC;
|
|
19
|
-
}
|
|
20
|
-
else if (codecLower === 'vp8') {
|
|
21
|
-
return AV_CODEC_ID_VP8;
|
|
22
|
-
}
|
|
23
|
-
else if (codecLower.startsWith('vp09') || codecLower === 'vp9') {
|
|
24
|
-
return AV_CODEC_ID_VP9;
|
|
25
|
-
}
|
|
26
|
-
else if (codecLower.startsWith('av01') || codecLower === 'av1') {
|
|
27
|
-
return AV_CODEC_ID_AV1;
|
|
28
|
-
}
|
|
29
|
-
throw new Error(`Unsupported video codec: ${codec}`);
|
|
30
|
-
}
|
|
31
|
-
function mapAudioCodecId(codec) {
|
|
32
|
-
const codecLower = codec.toLowerCase();
|
|
33
|
-
if (codecLower.startsWith('mp4a') || codecLower === 'aac') {
|
|
34
|
-
return AV_CODEC_ID_AAC;
|
|
35
|
-
}
|
|
36
|
-
else if (codecLower === 'mp3') {
|
|
37
|
-
return AV_CODEC_ID_MP3;
|
|
38
|
-
}
|
|
39
|
-
else if (codecLower === 'opus') {
|
|
40
|
-
return AV_CODEC_ID_OPUS;
|
|
41
|
-
}
|
|
42
|
-
else if (codecLower === 'vorbis') {
|
|
43
|
-
return AV_CODEC_ID_VORBIS;
|
|
44
|
-
}
|
|
45
|
-
else if (codecLower === 'flac') {
|
|
46
|
-
return AV_CODEC_ID_FLAC;
|
|
47
|
-
}
|
|
48
|
-
throw new Error(`Unsupported audio codec: ${codec}`);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Infer container format from file extension
|
|
52
|
-
*/
|
|
53
|
-
function inferFormat(path) {
|
|
54
|
-
const ext = path.split('.').pop()?.toLowerCase();
|
|
55
|
-
switch (ext) {
|
|
56
|
-
case 'mp4':
|
|
57
|
-
case 'm4v':
|
|
58
|
-
return 'mp4';
|
|
59
|
-
case 'webm':
|
|
60
|
-
return 'webm';
|
|
61
|
-
case 'mkv':
|
|
62
|
-
return 'matroska';
|
|
63
|
-
case 'mov':
|
|
64
|
-
return 'mov';
|
|
65
|
-
case 'avi':
|
|
66
|
-
return 'avi';
|
|
67
|
-
case 'ts':
|
|
68
|
-
return 'mpegts';
|
|
69
|
-
default:
|
|
70
|
-
return 'mp4';
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Container muxer that accepts WebCodecs-compatible chunks
|
|
12
|
+
* Muxer that tries node-av first, then falls back to FFmpeg spawn
|
|
13
|
+
*
|
|
14
|
+
* This implementation buffers all chunks and only performs the actual
|
|
15
|
+
* muxing when close() is called. This allows seamless fallback if the
|
|
16
|
+
* primary muxer fails at any point.
|
|
75
17
|
*
|
|
76
18
|
* @example
|
|
77
19
|
* ```typescript
|
|
78
|
-
* const muxer = new Muxer({
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* codec: 'avc1.42001E',
|
|
82
|
-
* codedWidth: 640,
|
|
83
|
-
* codedHeight: 480,
|
|
84
|
-
* framerate: 30,
|
|
85
|
-
* description: spsNaluBuffer, // Optional: H.264 SPS/PPS
|
|
20
|
+
* const muxer = new Muxer({
|
|
21
|
+
* path: 'output.mp4',
|
|
22
|
+
* onFallback: (err) => console.warn('Using FFmpeg fallback:', err.message),
|
|
86
23
|
* });
|
|
87
24
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* await muxer.writeVideoChunk(chunk);
|
|
25
|
+
* await muxer.open();
|
|
26
|
+
* await muxer.addVideoTrack({ codec: 'avc1.64001E', ... });
|
|
27
|
+
* await muxer.addAudioTrack({ codec: 'mp4a.40.2', ... });
|
|
28
|
+
*
|
|
29
|
+
* for (const chunk of videoChunks) await muxer.writeVideoChunk(chunk);
|
|
30
|
+
* for (const chunk of audioChunks) await muxer.writeAudioChunk(chunk);
|
|
93
31
|
*
|
|
94
|
-
* await muxer.
|
|
32
|
+
* const result = await muxer.closeWithResult();
|
|
33
|
+
* console.log(`Muxed with ${result.backend} in ${result.durationMs}ms`);
|
|
95
34
|
* ```
|
|
96
35
|
*/
|
|
97
36
|
export class Muxer {
|
|
98
|
-
muxer = null;
|
|
99
37
|
config;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
_videoConfig = null;
|
|
105
|
-
_audioConfig = null;
|
|
38
|
+
videoConfig = null;
|
|
39
|
+
audioConfig = null;
|
|
40
|
+
videoChunks = [];
|
|
41
|
+
audioChunks = [];
|
|
106
42
|
_videoChunkCount = 0;
|
|
107
43
|
_audioChunkCount = 0;
|
|
44
|
+
isOpen = false;
|
|
45
|
+
usedBackend = null;
|
|
108
46
|
constructor(config) {
|
|
109
47
|
this.config = config;
|
|
110
48
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*/
|
|
116
|
-
async open(timeout = DEFAULT_TIMEOUTS.open) {
|
|
117
|
-
const format = this.config.format || inferFormat(this.config.path);
|
|
118
|
-
this.muxer = await withTimeout(NodeAvMuxer.open(this.config.path, { format }), timeout, `Muxer open (${this.config.path})`);
|
|
49
|
+
async open(timeout) {
|
|
50
|
+
this.isOpen = true;
|
|
51
|
+
// We don't actually open anything yet - we buffer chunks
|
|
52
|
+
// and open the muxer during close()
|
|
119
53
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Add a video track to the output
|
|
122
|
-
*
|
|
123
|
-
* @param config - Video track configuration
|
|
124
|
-
* @returns Stream index for the video track
|
|
125
|
-
*/
|
|
126
54
|
async addVideoTrack(config) {
|
|
127
|
-
if (!this.
|
|
128
|
-
throw new
|
|
55
|
+
if (!this.isOpen) {
|
|
56
|
+
throw new MuxerError('Muxer not opened', 'node-av', 'addTrack');
|
|
129
57
|
}
|
|
130
|
-
this.
|
|
131
|
-
|
|
132
|
-
// Create an encoder to properly configure the output stream
|
|
133
|
-
// The encoder sets up codec parameters that the muxer needs
|
|
134
|
-
// Use microsecond timeBase (1/1000000) to match WebCodecs timestamp units
|
|
135
|
-
this._videoEncoder = await NodeAvEncoder.create(codecId, {
|
|
136
|
-
width: config.codedWidth,
|
|
137
|
-
height: config.codedHeight,
|
|
138
|
-
pixelFormat: AV_PIX_FMT_YUV420P,
|
|
139
|
-
timeBase: { num: 1, den: 1_000_000 }, // Microseconds - matches WebCodecs
|
|
140
|
-
frameRate: { num: config.framerate || 30, den: 1 },
|
|
141
|
-
bitrate: config.bitrate || 1_000_000,
|
|
142
|
-
gopSize: 30,
|
|
143
|
-
// Set extradata from description (SPS/PPS for H.264, etc.)
|
|
144
|
-
extradata: config.description ? Buffer.from(config.description) : undefined,
|
|
145
|
-
});
|
|
146
|
-
// Add the encoder's stream to the muxer
|
|
147
|
-
this._videoStreamIndex = this.muxer.addStream(this._videoEncoder);
|
|
148
|
-
return this._videoStreamIndex;
|
|
58
|
+
this.videoConfig = config;
|
|
59
|
+
return 0;
|
|
149
60
|
}
|
|
150
|
-
/**
|
|
151
|
-
* Add an audio track to the output
|
|
152
|
-
*
|
|
153
|
-
* @param config - Audio track configuration
|
|
154
|
-
* @returns Stream index for the audio track
|
|
155
|
-
*/
|
|
156
61
|
async addAudioTrack(config) {
|
|
157
|
-
if (!this.
|
|
158
|
-
throw new
|
|
62
|
+
if (!this.isOpen) {
|
|
63
|
+
throw new MuxerError('Muxer not opened', 'node-av', 'addTrack');
|
|
159
64
|
}
|
|
160
|
-
this.
|
|
161
|
-
|
|
162
|
-
// Create an encoder to properly configure the output stream
|
|
163
|
-
// Use microsecond timeBase (1/1000000) to match WebCodecs timestamp units
|
|
164
|
-
this._audioEncoder = await NodeAvEncoder.create(codecId, {
|
|
165
|
-
sampleRate: config.sampleRate,
|
|
166
|
-
channels: config.numberOfChannels,
|
|
167
|
-
sampleFormat: AV_SAMPLE_FMT_FLTP,
|
|
168
|
-
timeBase: { num: 1, den: 1_000_000 }, // Microseconds - matches WebCodecs
|
|
169
|
-
bitrate: config.bitrate || 128_000,
|
|
170
|
-
// Set extradata from description (AudioSpecificConfig for AAC, etc.)
|
|
171
|
-
extradata: config.description ? Buffer.from(config.description) : undefined,
|
|
172
|
-
});
|
|
173
|
-
// Add the encoder's stream to the muxer
|
|
174
|
-
this._audioStreamIndex = this.muxer.addStream(this._audioEncoder);
|
|
175
|
-
return this._audioStreamIndex;
|
|
65
|
+
this.audioConfig = config;
|
|
66
|
+
return this.videoConfig ? 1 : 0;
|
|
176
67
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Write an encoded video chunk to the output container
|
|
179
|
-
*
|
|
180
|
-
* @param chunk - EncodedVideoChunk from VideoEncoder
|
|
181
|
-
*/
|
|
182
68
|
async writeVideoChunk(chunk) {
|
|
183
|
-
if (!this.
|
|
184
|
-
throw new
|
|
69
|
+
if (!this.isOpen || !this.videoConfig) {
|
|
70
|
+
throw new MuxerError('Video track not configured', 'node-av', 'write');
|
|
185
71
|
}
|
|
186
|
-
|
|
187
|
-
const packetData = {
|
|
188
|
-
data: Buffer.from(chunk._buffer),
|
|
189
|
-
pts: BigInt(Math.round(chunk.timestamp)), // microseconds
|
|
190
|
-
dts: BigInt(Math.round(chunk.timestamp)),
|
|
191
|
-
duration: chunk.duration ? BigInt(Math.round(chunk.duration)) : undefined,
|
|
192
|
-
isKeyframe: chunk.type === 'key',
|
|
193
|
-
};
|
|
194
|
-
await this.muxer.writePacket(packetData, this._videoStreamIndex);
|
|
72
|
+
this.videoChunks.push(chunk);
|
|
195
73
|
this._videoChunkCount++;
|
|
196
74
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Write an encoded audio chunk to the output container
|
|
199
|
-
*
|
|
200
|
-
* @param chunk - EncodedAudioChunk from AudioEncoder
|
|
201
|
-
*/
|
|
202
75
|
async writeAudioChunk(chunk) {
|
|
203
|
-
if (!this.
|
|
204
|
-
throw new
|
|
76
|
+
if (!this.isOpen || !this.audioConfig) {
|
|
77
|
+
throw new MuxerError('Audio track not configured', 'node-av', 'write');
|
|
205
78
|
}
|
|
206
|
-
|
|
207
|
-
const packetData = {
|
|
208
|
-
data: Buffer.from(chunk._rawData),
|
|
209
|
-
pts: BigInt(Math.round(chunk.timestamp)), // microseconds
|
|
210
|
-
dts: BigInt(Math.round(chunk.timestamp)),
|
|
211
|
-
duration: chunk.duration ? BigInt(Math.round(chunk.duration)) : undefined,
|
|
212
|
-
isKeyframe: chunk.type === 'key',
|
|
213
|
-
};
|
|
214
|
-
await this.muxer.writePacket(packetData, this._audioStreamIndex);
|
|
79
|
+
this.audioChunks.push(chunk);
|
|
215
80
|
this._audioChunkCount++;
|
|
216
81
|
}
|
|
217
82
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* @param timeout - Operation timeout in milliseconds (default: 10000)
|
|
83
|
+
* Close the muxer and finalize the output file
|
|
221
84
|
*/
|
|
222
|
-
async close(timeout
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
85
|
+
async close(timeout) {
|
|
86
|
+
await this.closeWithResult(timeout);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Close the muxer and return detailed result including which backend was used
|
|
90
|
+
*/
|
|
91
|
+
async closeWithResult(timeout) {
|
|
92
|
+
if (!this.isOpen) {
|
|
93
|
+
return {
|
|
94
|
+
path: this.config.path,
|
|
95
|
+
videoChunkCount: 0,
|
|
96
|
+
audioChunkCount: 0,
|
|
97
|
+
durationMs: 0,
|
|
98
|
+
backend: 'node-av',
|
|
99
|
+
};
|
|
227
100
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
// If force backend is specified, use only that
|
|
103
|
+
if (this.config.forceBackend === 'ffmpeg-spawn') {
|
|
104
|
+
await this.muxWithFFmpeg();
|
|
105
|
+
this.usedBackend = 'ffmpeg-spawn';
|
|
231
106
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
this.muxer = null;
|
|
107
|
+
else if (this.config.forceBackend === 'node-av') {
|
|
108
|
+
await this.muxWithNodeAv(timeout);
|
|
109
|
+
this.usedBackend = 'node-av';
|
|
236
110
|
}
|
|
111
|
+
else {
|
|
112
|
+
// Try node-av first, fallback to FFmpeg
|
|
113
|
+
try {
|
|
114
|
+
await this.muxWithNodeAv(timeout);
|
|
115
|
+
this.usedBackend = 'node-av';
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
// Notify about fallback
|
|
119
|
+
if (this.config.onFallback) {
|
|
120
|
+
this.config.onFallback(error);
|
|
121
|
+
}
|
|
122
|
+
// Fall back to FFmpeg
|
|
123
|
+
await this.muxWithFFmpeg();
|
|
124
|
+
this.usedBackend = 'ffmpeg-spawn';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.isOpen = false;
|
|
128
|
+
const durationMs = Date.now() - startTime;
|
|
129
|
+
return {
|
|
130
|
+
path: this.config.path,
|
|
131
|
+
videoChunkCount: this._videoChunkCount,
|
|
132
|
+
audioChunkCount: this._audioChunkCount,
|
|
133
|
+
durationMs,
|
|
134
|
+
backend: this.usedBackend,
|
|
135
|
+
};
|
|
237
136
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
137
|
+
async muxWithNodeAv(timeout) {
|
|
138
|
+
const muxer = new NodeAvMuxer({
|
|
139
|
+
path: this.config.path,
|
|
140
|
+
format: this.config.format,
|
|
141
|
+
});
|
|
142
|
+
try {
|
|
143
|
+
await muxer.open(timeout);
|
|
144
|
+
if (this.videoConfig) {
|
|
145
|
+
await muxer.addVideoTrack(this.videoConfig);
|
|
146
|
+
}
|
|
147
|
+
if (this.audioConfig) {
|
|
148
|
+
await muxer.addAudioTrack(this.audioConfig);
|
|
149
|
+
}
|
|
150
|
+
// Write all buffered chunks
|
|
151
|
+
for (const chunk of this.videoChunks) {
|
|
152
|
+
await muxer.writeVideoChunk(chunk);
|
|
153
|
+
}
|
|
154
|
+
for (const chunk of this.audioChunks) {
|
|
155
|
+
await muxer.writeAudioChunk(chunk);
|
|
156
|
+
}
|
|
157
|
+
await muxer.close(timeout);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// Try to clean up partial file
|
|
161
|
+
try {
|
|
162
|
+
await muxer.close(1000);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Ignore cleanup errors
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async muxWithFFmpeg() {
|
|
171
|
+
const muxer = new FFmpegMuxer({
|
|
172
|
+
path: this.config.path,
|
|
173
|
+
format: this.config.format,
|
|
174
|
+
});
|
|
175
|
+
await muxer.open();
|
|
176
|
+
if (this.videoConfig) {
|
|
177
|
+
await muxer.addVideoTrack(this.videoConfig);
|
|
178
|
+
}
|
|
179
|
+
if (this.audioConfig) {
|
|
180
|
+
await muxer.addAudioTrack(this.audioConfig);
|
|
181
|
+
}
|
|
182
|
+
// Write all buffered chunks
|
|
183
|
+
for (const chunk of this.videoChunks) {
|
|
184
|
+
await muxer.writeVideoChunk(chunk);
|
|
185
|
+
}
|
|
186
|
+
for (const chunk of this.audioChunks) {
|
|
187
|
+
await muxer.writeAudioChunk(chunk);
|
|
188
|
+
}
|
|
189
|
+
await muxer.close();
|
|
243
190
|
}
|
|
244
|
-
/**
|
|
245
|
-
* Get number of video chunks written
|
|
246
|
-
*/
|
|
247
191
|
get videoChunkCount() {
|
|
248
192
|
return this._videoChunkCount;
|
|
249
193
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Get number of audio chunks written
|
|
252
|
-
*/
|
|
253
194
|
get audioChunkCount() {
|
|
254
195
|
return this._audioChunkCount;
|
|
255
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Get which backend was used for muxing (available after close)
|
|
199
|
+
*/
|
|
200
|
+
get backend() {
|
|
201
|
+
return this.usedBackend;
|
|
202
|
+
}
|
|
256
203
|
}
|
|
257
204
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
205
|
+
* Convenience function to mux video and audio chunks to a file
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const result = await muxChunks({
|
|
210
|
+
* path: 'output.mp4',
|
|
211
|
+
* video: { config: videoTrackConfig, chunks: videoChunks },
|
|
212
|
+
* audio: { config: audioTrackConfig, chunks: audioChunks },
|
|
213
|
+
* });
|
|
214
|
+
* console.log(`Created ${result.path} using ${result.backend}`);
|
|
215
|
+
* ```
|
|
260
216
|
*/
|
|
261
|
-
export
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
// Copy video stream
|
|
274
|
-
const videoStream = demuxer.video();
|
|
275
|
-
if (videoStream) {
|
|
276
|
-
const outIndex = muxer.addStream(videoStream);
|
|
277
|
-
streamMap.set(videoStream.index, outIndex);
|
|
217
|
+
export async function muxChunks(options) {
|
|
218
|
+
const muxer = new Muxer({
|
|
219
|
+
path: options.path,
|
|
220
|
+
format: options.format,
|
|
221
|
+
onFallback: options.onFallback,
|
|
222
|
+
forceBackend: options.forceBackend,
|
|
223
|
+
});
|
|
224
|
+
await muxer.open();
|
|
225
|
+
if (options.video) {
|
|
226
|
+
await muxer.addVideoTrack(options.video.config);
|
|
227
|
+
for (const chunk of options.video.chunks) {
|
|
228
|
+
await muxer.writeVideoChunk(chunk);
|
|
278
229
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
// Copy packets
|
|
286
|
-
for await (const packet of demuxer.packets()) {
|
|
287
|
-
if (!packet)
|
|
288
|
-
continue;
|
|
289
|
-
const outIndex = streamMap.get(packet.streamIndex);
|
|
290
|
-
if (outIndex !== undefined) {
|
|
291
|
-
await muxer.writePacket(packet, outIndex);
|
|
292
|
-
}
|
|
230
|
+
}
|
|
231
|
+
if (options.audio) {
|
|
232
|
+
await muxer.addAudioTrack(options.audio.config);
|
|
233
|
+
for (const chunk of options.audio.chunks) {
|
|
234
|
+
await muxer.writeAudioChunk(chunk);
|
|
293
235
|
}
|
|
294
|
-
await demuxer.close();
|
|
295
|
-
await muxer.close();
|
|
296
236
|
}
|
|
237
|
+
return muxer.closeWithResult();
|
|
297
238
|
}
|
|
298
239
|
//# sourceMappingURL=Muxer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Muxer.js","sourceRoot":"","sources":["../../src/containers/Muxer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"Muxer.js","sourceRoot":"","sources":["../../src/containers/Muxer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Muxer with Fallback - Primary node-av muxer with FFmpeg spawn fallback
|
|
3
|
+
*
|
|
4
|
+
* This muxer attempts to use the fast node-av muxer first, and automatically
|
|
5
|
+
* falls back to FFmpeg spawn if it fails. This provides the best of both worlds:
|
|
6
|
+
* fast muxing when possible, with reliable fallback for edge cases.
|
|
7
|
+
*/
|
|
8
|
+
import type { EncodedVideoChunk } from '../core/EncodedVideoChunk.js';
|
|
9
|
+
import type { EncodedAudioChunk } from '../core/EncodedAudioChunk.js';
|
|
10
|
+
import type { IMuxer, MuxerConfig, VideoTrackConfig, AudioTrackConfig, MuxResult } from './muxer-types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Options for MuxerWithFallback
|
|
13
|
+
*/
|
|
14
|
+
export interface MuxerWithFallbackOptions extends MuxerConfig {
|
|
15
|
+
/**
|
|
16
|
+
* Callback when fallback is triggered
|
|
17
|
+
* Useful for logging or metrics
|
|
18
|
+
*/
|
|
19
|
+
onFallback?: (error: Error) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Force using a specific backend (skip fallback logic)
|
|
22
|
+
*/
|
|
23
|
+
forceBackend?: 'node-av' | 'ffmpeg-spawn';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Muxer that tries node-av first, then falls back to FFmpeg spawn
|
|
27
|
+
*
|
|
28
|
+
* This implementation buffers all chunks and only performs the actual
|
|
29
|
+
* muxing when close() is called. This allows seamless fallback if the
|
|
30
|
+
* primary muxer fails at any point.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const muxer = new MuxerWithFallback({
|
|
35
|
+
* path: 'output.mp4',
|
|
36
|
+
* onFallback: (err) => console.warn('Using FFmpeg fallback:', err.message),
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* await muxer.open();
|
|
40
|
+
* await muxer.addVideoTrack({ codec: 'avc1.64001E', ... });
|
|
41
|
+
* await muxer.addAudioTrack({ codec: 'mp4a.40.2', ... });
|
|
42
|
+
*
|
|
43
|
+
* for (const chunk of videoChunks) await muxer.writeVideoChunk(chunk);
|
|
44
|
+
* for (const chunk of audioChunks) await muxer.writeAudioChunk(chunk);
|
|
45
|
+
*
|
|
46
|
+
* const result = await muxer.closeWithResult();
|
|
47
|
+
* console.log(`Muxed with ${result.backend} in ${result.durationMs}ms`);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare class MuxerWithFallback implements IMuxer {
|
|
51
|
+
private config;
|
|
52
|
+
private videoConfig;
|
|
53
|
+
private audioConfig;
|
|
54
|
+
private videoChunks;
|
|
55
|
+
private audioChunks;
|
|
56
|
+
private _videoChunkCount;
|
|
57
|
+
private _audioChunkCount;
|
|
58
|
+
private isOpen;
|
|
59
|
+
private usedBackend;
|
|
60
|
+
constructor(config: MuxerWithFallbackOptions);
|
|
61
|
+
open(timeout?: number): Promise<void>;
|
|
62
|
+
addVideoTrack(config: VideoTrackConfig): Promise<number>;
|
|
63
|
+
addAudioTrack(config: AudioTrackConfig): Promise<number>;
|
|
64
|
+
writeVideoChunk(chunk: EncodedVideoChunk): Promise<void>;
|
|
65
|
+
writeAudioChunk(chunk: EncodedAudioChunk): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Close the muxer and finalize the output file
|
|
68
|
+
*/
|
|
69
|
+
close(timeout?: number): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Close the muxer and return detailed result including which backend was used
|
|
72
|
+
*/
|
|
73
|
+
closeWithResult(timeout?: number): Promise<MuxResult>;
|
|
74
|
+
private muxWithNodeAv;
|
|
75
|
+
private muxWithFFmpeg;
|
|
76
|
+
get videoChunkCount(): number;
|
|
77
|
+
get audioChunkCount(): number;
|
|
78
|
+
/**
|
|
79
|
+
* Get which backend was used for muxing (available after close)
|
|
80
|
+
*/
|
|
81
|
+
get backend(): 'node-av' | 'ffmpeg-spawn' | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convenience function to mux video and audio chunks to a file
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const result = await muxChunks({
|
|
89
|
+
* path: 'output.mp4',
|
|
90
|
+
* video: { config: videoTrackConfig, chunks: videoChunks },
|
|
91
|
+
* audio: { config: audioTrackConfig, chunks: audioChunks },
|
|
92
|
+
* });
|
|
93
|
+
* console.log(`Created ${result.path} using ${result.backend}`);
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function muxChunks(options: {
|
|
97
|
+
path: string;
|
|
98
|
+
format?: string;
|
|
99
|
+
video?: {
|
|
100
|
+
config: VideoTrackConfig;
|
|
101
|
+
chunks: EncodedVideoChunk[];
|
|
102
|
+
};
|
|
103
|
+
audio?: {
|
|
104
|
+
config: AudioTrackConfig;
|
|
105
|
+
chunks: EncodedAudioChunk[];
|
|
106
|
+
};
|
|
107
|
+
onFallback?: (error: Error) => void;
|
|
108
|
+
forceBackend?: 'node-av' | 'ffmpeg-spawn';
|
|
109
|
+
}): Promise<MuxResult>;
|
|
110
|
+
//# sourceMappingURL=MuxerWithFallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MuxerWithFallback.d.ts","sourceRoot":"","sources":["../../src/containers/MuxerWithFallback.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACV,MAAM,kBAAkB,CAAC;AAK1B;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,WAAW;IAC3D;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEpC;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,cAAc,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,iBAAkB,YAAW,MAAM;IAC9C,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAA2C;gBAElD,MAAM,EAAE,wBAAwB;IAItC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrC,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAQxD,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAQxD,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxD,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D;;OAEG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C;;OAEG;IACG,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;YAiD7C,aAAa;YAoCb,aAAa;IA0B3B,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,SAAS,GAAG,cAAc,GAAG,IAAI,CAE/C;CACF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QACN,MAAM,EAAE,gBAAgB,CAAC;QACzB,MAAM,EAAE,iBAAiB,EAAE,CAAC;KAC7B,CAAC;IACF,KAAK,CAAC,EAAE;QACN,MAAM,EAAE,gBAAgB,CAAC;QACzB,MAAM,EAAE,iBAAiB,EAAE,CAAC;KAC7B,CAAC;IACF,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,SAAS,GAAG,cAAc,CAAC;CAC3C,GAAG,OAAO,CAAC,SAAS,CAAC,CAyBrB"}
|