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.
Files changed (105) hide show
  1. package/README.md +94 -12
  2. package/dist/config/ffmpeg-quality.d.ts +10 -1
  3. package/dist/config/ffmpeg-quality.d.ts.map +1 -1
  4. package/dist/config/ffmpeg-quality.js +12 -38
  5. package/dist/config/ffmpeg-quality.js.map +1 -1
  6. package/dist/config/webcodecs-config.d.ts +58 -0
  7. package/dist/config/webcodecs-config.d.ts.map +1 -0
  8. package/dist/config/webcodecs-config.js +187 -0
  9. package/dist/config/webcodecs-config.js.map +1 -0
  10. package/dist/containers/Demuxer.d.ts +3 -1
  11. package/dist/containers/Demuxer.d.ts.map +1 -1
  12. package/dist/containers/Demuxer.js +26 -19
  13. package/dist/containers/Demuxer.js.map +1 -1
  14. package/dist/containers/FFmpegMuxer.d.ts +42 -0
  15. package/dist/containers/FFmpegMuxer.d.ts.map +1 -0
  16. package/dist/containers/FFmpegMuxer.js +311 -0
  17. package/dist/containers/FFmpegMuxer.js.map +1 -0
  18. package/dist/containers/FFmpegSpawnMuxer.d.ts +42 -0
  19. package/dist/containers/FFmpegSpawnMuxer.d.ts.map +1 -0
  20. package/dist/containers/FFmpegSpawnMuxer.js +311 -0
  21. package/dist/containers/FFmpegSpawnMuxer.js.map +1 -0
  22. package/dist/containers/FallbackMuxer.d.ts +42 -0
  23. package/dist/containers/FallbackMuxer.d.ts.map +1 -0
  24. package/dist/containers/FallbackMuxer.js +311 -0
  25. package/dist/containers/FallbackMuxer.js.map +1 -0
  26. package/dist/containers/Muxer.d.ts +75 -107
  27. package/dist/containers/Muxer.d.ts.map +1 -1
  28. package/dist/containers/Muxer.js +184 -243
  29. package/dist/containers/Muxer.js.map +1 -1
  30. package/dist/containers/MuxerWithFallback.d.ts +110 -0
  31. package/dist/containers/MuxerWithFallback.d.ts.map +1 -0
  32. package/dist/containers/MuxerWithFallback.js +239 -0
  33. package/dist/containers/MuxerWithFallback.js.map +1 -0
  34. package/dist/containers/NodeAvMuxer.d.ts +118 -0
  35. package/dist/containers/NodeAvMuxer.d.ts.map +1 -0
  36. package/dist/containers/NodeAvMuxer.js +338 -0
  37. package/dist/containers/NodeAvMuxer.js.map +1 -0
  38. package/dist/containers/extract.d.ts.map +1 -1
  39. package/dist/containers/extract.js +3 -1
  40. package/dist/containers/extract.js.map +1 -1
  41. package/dist/containers/index.d.ts +20 -14
  42. package/dist/containers/index.d.ts.map +1 -1
  43. package/dist/containers/index.js +21 -14
  44. package/dist/containers/index.js.map +1 -1
  45. package/dist/containers/muxer-types.d.ts +117 -0
  46. package/dist/containers/muxer-types.d.ts.map +1 -0
  47. package/dist/containers/muxer-types.js +45 -0
  48. package/dist/containers/muxer-types.js.map +1 -0
  49. package/dist/containers/transcode.d.ts.map +1 -1
  50. package/dist/containers/transcode.js +171 -150
  51. package/dist/containers/transcode.js.map +1 -1
  52. package/dist/core/VideoFrame.d.ts +19 -0
  53. package/dist/core/VideoFrame.d.ts.map +1 -1
  54. package/dist/core/VideoFrame.js +11 -0
  55. package/dist/core/VideoFrame.js.map +1 -1
  56. package/dist/decoders/VideoDecoder.d.ts +1 -0
  57. package/dist/decoders/VideoDecoder.d.ts.map +1 -1
  58. package/dist/decoders/VideoDecoder.js +6 -4
  59. package/dist/decoders/VideoDecoder.js.map +1 -1
  60. package/dist/demos/demo-audio-visualizer-mediabunny.d.ts +10 -0
  61. package/dist/demos/demo-audio-visualizer-mediabunny.d.ts.map +1 -0
  62. package/dist/demos/demo-audio-visualizer-mediabunny.js +357 -0
  63. package/dist/demos/demo-audio-visualizer-mediabunny.js.map +1 -0
  64. package/dist/demos/demo-audio-visualizer-nodeav.d.ts +10 -0
  65. package/dist/demos/demo-audio-visualizer-nodeav.d.ts.map +1 -0
  66. package/dist/demos/demo-audio-visualizer-nodeav.js +318 -0
  67. package/dist/demos/demo-audio-visualizer-nodeav.js.map +1 -0
  68. package/dist/demos/demo-muxer-fallback.d.ts +8 -0
  69. package/dist/demos/demo-muxer-fallback.d.ts.map +1 -0
  70. package/dist/demos/demo-muxer-fallback.js +165 -0
  71. package/dist/demos/demo-muxer-fallback.js.map +1 -0
  72. package/dist/encoders/AudioEncoder.d.ts +2 -0
  73. package/dist/encoders/AudioEncoder.d.ts.map +1 -1
  74. package/dist/encoders/AudioEncoder.js +7 -4
  75. package/dist/encoders/AudioEncoder.js.map +1 -1
  76. package/dist/hardware/decoder-args.d.ts.map +1 -1
  77. package/dist/hardware/decoder-args.js +35 -14
  78. package/dist/hardware/decoder-args.js.map +1 -1
  79. package/dist/hardware/detection.d.ts.map +1 -1
  80. package/dist/hardware/detection.js +39 -0
  81. package/dist/hardware/detection.js.map +1 -1
  82. package/dist/hardware/encoder-args.d.ts.map +1 -1
  83. package/dist/hardware/encoder-args.js +43 -5
  84. package/dist/hardware/encoder-args.js.map +1 -1
  85. package/dist/hardware/types.d.ts.map +1 -1
  86. package/dist/hardware/types.js +30 -28
  87. package/dist/hardware/types.js.map +1 -1
  88. package/dist/node-av/NodeAvVideoEncoder.d.ts +5 -0
  89. package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
  90. package/dist/node-av/NodeAvVideoEncoder.js +76 -23
  91. package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
  92. package/dist/utils/avc.d.ts +2 -0
  93. package/dist/utils/avc.d.ts.map +1 -1
  94. package/dist/utils/avc.js +36 -8
  95. package/dist/utils/avc.js.map +1 -1
  96. package/dist/utils/codec-validation.d.ts.map +1 -1
  97. package/dist/utils/codec-validation.js +18 -8
  98. package/dist/utils/codec-validation.js.map +1 -1
  99. package/dist/utils/hevc.d.ts +2 -0
  100. package/dist/utils/hevc.d.ts.map +1 -1
  101. package/dist/utils/hevc.js +42 -8
  102. package/dist/utils/hevc.js.map +1 -1
  103. package/docs/api.md +20 -2
  104. package/docs/configuration.md +10 -7
  105. package/package.json +1 -1
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Fallback Muxer - FFmpeg subprocess-based muxer
3
+ *
4
+ * This muxer accumulates encoded chunks in memory and uses FFmpeg
5
+ * to mux them when close() is called. It serves as a fallback when
6
+ * the node-av muxer fails.
7
+ */
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import * as os from 'os';
11
+ import { spawn } from 'child_process';
12
+ import { randomBytes } from 'crypto';
13
+ import { MuxerError } from './muxer-types.js';
14
+ /**
15
+ * Convert AVCC format (length-prefixed NALUs) to Annex B (start code prefixed)
16
+ * This is needed because FFmpeg expects Annex B format for raw H.264 input
17
+ */
18
+ function avccToAnnexB(avccData, nalLengthSize = 4) {
19
+ const START_CODE = new Uint8Array([0, 0, 0, 1]);
20
+ const chunks = [];
21
+ let offset = 0;
22
+ while (offset < avccData.length) {
23
+ // Read NAL unit length
24
+ let nalLength = 0;
25
+ for (let i = 0; i < nalLengthSize; i++) {
26
+ nalLength = (nalLength << 8) | avccData[offset + i];
27
+ }
28
+ offset += nalLengthSize;
29
+ if (nalLength <= 0 || offset + nalLength > avccData.length) {
30
+ break;
31
+ }
32
+ // Add start code + NAL unit
33
+ chunks.push(START_CODE);
34
+ chunks.push(avccData.slice(offset, offset + nalLength));
35
+ offset += nalLength;
36
+ }
37
+ // Concatenate all chunks
38
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
39
+ const result = new Uint8Array(totalLength);
40
+ let writeOffset = 0;
41
+ for (const chunk of chunks) {
42
+ result.set(chunk, writeOffset);
43
+ writeOffset += chunk.length;
44
+ }
45
+ return result;
46
+ }
47
+ /**
48
+ * Parse AVCC extradata (SPS/PPS) to Annex B format
49
+ */
50
+ function parseAvccExtradata(extradata) {
51
+ const START_CODE = new Uint8Array([0, 0, 0, 1]);
52
+ const chunks = [];
53
+ // AVCC format:
54
+ // configurationVersion (1 byte) = 1
55
+ // AVCProfileIndication (1 byte)
56
+ // profile_compatibility (1 byte)
57
+ // AVCLevelIndication (1 byte)
58
+ // lengthSizeMinusOne (6 bits reserved + 2 bits) -> NAL length size = (value & 0x03) + 1
59
+ // numOfSequenceParameterSets (3 bits reserved + 5 bits)
60
+ // SPS entries...
61
+ // numOfPictureParameterSets (1 byte)
62
+ // PPS entries...
63
+ if (extradata.length < 7 || extradata[0] !== 1) {
64
+ // Not valid AVCC, return as-is (might already be Annex B)
65
+ return { annexBHeader: extradata, nalLengthSize: 4 };
66
+ }
67
+ const nalLengthSize = (extradata[4] & 0x03) + 1;
68
+ let offset = 5;
69
+ // Parse SPS
70
+ const numSPS = extradata[offset] & 0x1f;
71
+ offset++;
72
+ for (let i = 0; i < numSPS; i++) {
73
+ const spsLength = (extradata[offset] << 8) | extradata[offset + 1];
74
+ offset += 2;
75
+ chunks.push(START_CODE);
76
+ chunks.push(extradata.slice(offset, offset + spsLength));
77
+ offset += spsLength;
78
+ }
79
+ // Parse PPS
80
+ const numPPS = extradata[offset];
81
+ offset++;
82
+ for (let i = 0; i < numPPS; i++) {
83
+ const ppsLength = (extradata[offset] << 8) | extradata[offset + 1];
84
+ offset += 2;
85
+ chunks.push(START_CODE);
86
+ chunks.push(extradata.slice(offset, offset + ppsLength));
87
+ offset += ppsLength;
88
+ }
89
+ // Concatenate
90
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
91
+ const annexBHeader = new Uint8Array(totalLength);
92
+ let writeOffset = 0;
93
+ for (const chunk of chunks) {
94
+ annexBHeader.set(chunk, writeOffset);
95
+ writeOffset += chunk.length;
96
+ }
97
+ return { annexBHeader, nalLengthSize };
98
+ }
99
+ /**
100
+ * Add ADTS header to raw AAC frame
101
+ */
102
+ function addAdtsHeader(aacFrame, sampleRate, channels) {
103
+ const sampleRateIndex = [
104
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025,
105
+ 8000, 7350,
106
+ ].indexOf(sampleRate);
107
+ if (sampleRateIndex === -1) {
108
+ // Unsupported sample rate, return raw frame
109
+ return aacFrame;
110
+ }
111
+ const frameLength = aacFrame.length + 7; // ADTS header is 7 bytes
112
+ const header = new Uint8Array(7);
113
+ // Syncword (12 bits) = 0xFFF
114
+ header[0] = 0xff;
115
+ header[1] = 0xf1; // MPEG-4, Layer 0, no CRC
116
+ // Profile (2 bits) = AAC-LC (1), shifted left 6
117
+ // Sample rate index (4 bits), shifted left 2
118
+ // Private bit (1 bit) = 0
119
+ // Channel config (3 bits, upper 1 bit)
120
+ header[2] = ((1 << 6) | (sampleRateIndex << 2) | (channels >> 2)) & 0xff;
121
+ // Channel config (lower 2 bits), shifted left 6
122
+ // Original/copy (1 bit) = 0
123
+ // Home (1 bit) = 0
124
+ // Copyright ID (1 bit) = 0
125
+ // Copyright start (1 bit) = 0
126
+ // Frame length (13 bits, upper 2 bits)
127
+ header[3] = (((channels & 0x03) << 6) | (frameLength >> 11)) & 0xff;
128
+ // Frame length (middle 8 bits)
129
+ header[4] = (frameLength >> 3) & 0xff;
130
+ // Frame length (lower 3 bits), shifted left 5
131
+ // Buffer fullness (11 bits, upper 5 bits) = 0x7FF (VBR)
132
+ header[5] = (((frameLength & 0x07) << 5) | 0x1f) & 0xff;
133
+ // Buffer fullness (lower 6 bits), shifted left 2
134
+ // Number of AAC frames - 1 (2 bits) = 0
135
+ header[6] = 0xfc;
136
+ // Combine header and frame
137
+ const result = new Uint8Array(frameLength);
138
+ result.set(header);
139
+ result.set(aacFrame, 7);
140
+ return result;
141
+ }
142
+ /**
143
+ * Fallback muxer that uses FFmpeg subprocess for muxing
144
+ *
145
+ * This is a fallback muxer that accumulates chunks and uses FFmpeg
146
+ * to create the final output file. It's more battle-tested but slower
147
+ * (~130ms due to process spawn) than the native node-av muxer (~5ms).
148
+ */
149
+ export class FallbackMuxer {
150
+ config;
151
+ videoConfig = null;
152
+ audioConfig = null;
153
+ videoChunks = [];
154
+ audioChunks = [];
155
+ _videoChunkCount = 0;
156
+ _audioChunkCount = 0;
157
+ isOpen = false;
158
+ tempDir = null;
159
+ constructor(config) {
160
+ this.config = config;
161
+ }
162
+ async open(timeout) {
163
+ // Create temp directory for intermediate files
164
+ this.tempDir = path.join(os.tmpdir(), `ffmpeg-mux-${randomBytes(8).toString('hex')}`);
165
+ fs.mkdirSync(this.tempDir, { recursive: true });
166
+ this.isOpen = true;
167
+ }
168
+ async addVideoTrack(config) {
169
+ if (!this.isOpen) {
170
+ throw new MuxerError('Muxer not opened', 'ffmpeg-spawn', 'addTrack');
171
+ }
172
+ this.videoConfig = config;
173
+ return 0;
174
+ }
175
+ async addAudioTrack(config) {
176
+ if (!this.isOpen) {
177
+ throw new MuxerError('Muxer not opened', 'ffmpeg-spawn', 'addTrack');
178
+ }
179
+ this.audioConfig = config;
180
+ return this.videoConfig ? 1 : 0;
181
+ }
182
+ async writeVideoChunk(chunk) {
183
+ if (!this.isOpen || !this.videoConfig) {
184
+ throw new MuxerError('Video track not configured', 'ffmpeg-spawn', 'write');
185
+ }
186
+ this.videoChunks.push(chunk);
187
+ this._videoChunkCount++;
188
+ }
189
+ async writeAudioChunk(chunk) {
190
+ if (!this.isOpen || !this.audioConfig) {
191
+ throw new MuxerError('Audio track not configured', 'ffmpeg-spawn', 'write');
192
+ }
193
+ this.audioChunks.push(chunk);
194
+ this._audioChunkCount++;
195
+ }
196
+ async close(timeout) {
197
+ if (!this.isOpen || !this.tempDir) {
198
+ return;
199
+ }
200
+ try {
201
+ await this.muxWithFFmpeg();
202
+ }
203
+ finally {
204
+ // Clean up temp directory
205
+ if (this.tempDir && fs.existsSync(this.tempDir)) {
206
+ fs.rmSync(this.tempDir, { recursive: true, force: true });
207
+ }
208
+ this.isOpen = false;
209
+ }
210
+ }
211
+ async muxWithFFmpeg() {
212
+ if (!this.tempDir)
213
+ return;
214
+ const ffmpegArgs = ['-y'];
215
+ const inputFiles = [];
216
+ // Write video to temp file
217
+ if (this.videoConfig && this.videoChunks.length > 0) {
218
+ const videoPath = path.join(this.tempDir, 'video.h264');
219
+ await this.writeVideoToFile(videoPath);
220
+ inputFiles.push(videoPath);
221
+ const framerate = this.videoConfig.framerate || 30;
222
+ ffmpegArgs.push('-f', 'h264', '-r', String(framerate), '-i', videoPath);
223
+ }
224
+ // Write audio to temp file
225
+ if (this.audioConfig && this.audioChunks.length > 0) {
226
+ const audioPath = path.join(this.tempDir, 'audio.aac');
227
+ await this.writeAudioToFile(audioPath);
228
+ inputFiles.push(audioPath);
229
+ ffmpegArgs.push('-f', 'aac', '-i', audioPath);
230
+ }
231
+ if (inputFiles.length === 0) {
232
+ throw new MuxerError('No tracks to mux', 'ffmpeg-spawn', 'close');
233
+ }
234
+ // Output options
235
+ ffmpegArgs.push('-c:v', 'copy', '-c:a', 'copy', '-movflags', '+faststart', this.config.path);
236
+ // Spawn FFmpeg
237
+ await this.spawnFFmpeg(ffmpegArgs);
238
+ }
239
+ async writeVideoToFile(filePath) {
240
+ if (!this.videoConfig)
241
+ return;
242
+ const chunks = [];
243
+ // Parse extradata to get NAL length size and write SPS/PPS header
244
+ let nalLengthSize = 4;
245
+ if (this.videoConfig.description) {
246
+ const { annexBHeader, nalLengthSize: nls } = parseAvccExtradata(this.videoConfig.description);
247
+ nalLengthSize = nls;
248
+ chunks.push(annexBHeader);
249
+ }
250
+ // Convert each chunk from AVCC to Annex B
251
+ for (const chunk of this.videoChunks) {
252
+ const annexBData = avccToAnnexB(chunk._buffer, nalLengthSize);
253
+ chunks.push(annexBData);
254
+ }
255
+ // Write to file
256
+ const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
257
+ const buffer = Buffer.alloc(totalLength);
258
+ let offset = 0;
259
+ for (const chunk of chunks) {
260
+ Buffer.from(chunk).copy(buffer, offset);
261
+ offset += chunk.length;
262
+ }
263
+ fs.writeFileSync(filePath, buffer);
264
+ }
265
+ async writeAudioToFile(filePath) {
266
+ if (!this.audioConfig)
267
+ return;
268
+ const chunks = [];
269
+ // Add ADTS headers to each AAC frame
270
+ for (const chunk of this.audioChunks) {
271
+ const adtsFrame = addAdtsHeader(chunk._rawData, this.audioConfig.sampleRate, this.audioConfig.numberOfChannels);
272
+ chunks.push(adtsFrame);
273
+ }
274
+ // Write to file
275
+ const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
276
+ const buffer = Buffer.alloc(totalLength);
277
+ let offset = 0;
278
+ for (const chunk of chunks) {
279
+ Buffer.from(chunk).copy(buffer, offset);
280
+ offset += chunk.length;
281
+ }
282
+ fs.writeFileSync(filePath, buffer);
283
+ }
284
+ spawnFFmpeg(args) {
285
+ return new Promise((resolve, reject) => {
286
+ const ffmpeg = spawn('ffmpeg', args);
287
+ let stderr = '';
288
+ ffmpeg.stderr.on('data', (data) => {
289
+ stderr += data.toString();
290
+ });
291
+ ffmpeg.on('error', (err) => {
292
+ reject(new MuxerError(`FFmpeg spawn error: ${err.message}`, 'ffmpeg-spawn', 'close', err));
293
+ });
294
+ ffmpeg.on('close', (code) => {
295
+ if (code === 0) {
296
+ resolve();
297
+ }
298
+ else {
299
+ reject(new MuxerError(`FFmpeg exited with code ${code}: ${stderr.slice(-500)}`, 'ffmpeg-spawn', 'close'));
300
+ }
301
+ });
302
+ });
303
+ }
304
+ get videoChunkCount() {
305
+ return this._videoChunkCount;
306
+ }
307
+ get audioChunkCount() {
308
+ return this._audioChunkCount;
309
+ }
310
+ }
311
+ //# sourceMappingURL=FallbackMuxer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FallbackMuxer.js","sourceRoot":"","sources":["../../src/containers/FallbackMuxer.ts"],"names":[],"mappings":"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"}
@@ -1,142 +1,110 @@
1
1
  /**
2
- * Container Muxer - Writes encoded chunks to container files (MP4, WebM, MKV)
2
+ * Muxer - Primary muxer with automatic fallback
3
3
  *
4
- * Wraps node-av's Muxer to provide a WebCodecs-compatible interface that accepts
5
- * EncodedVideoChunk and EncodedAudioChunk objects.
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 { Muxer as NodeAvMuxer } from 'node-av';
8
- import { EncodedVideoChunk } from '../core/EncodedVideoChunk.js';
9
- import { EncodedAudioChunk } from '../core/EncodedAudioChunk.js';
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';
10
11
  /**
11
- * Video track configuration for muxing
12
+ * Options for Muxer
12
13
  */
13
- export interface VideoTrackConfig {
14
- codec: string;
15
- codedWidth: number;
16
- codedHeight: number;
17
- framerate?: number;
18
- bitrate?: number;
19
- description?: Uint8Array;
20
- }
21
- /**
22
- * Audio track configuration for muxing
23
- */
24
- export interface AudioTrackConfig {
25
- codec: string;
26
- sampleRate: number;
27
- numberOfChannels: number;
28
- bitrate?: number;
29
- description?: Uint8Array;
30
- }
31
- /**
32
- * Muxer configuration
33
- */
34
- export interface MuxerConfig {
35
- /** Path to the output file */
36
- path: string;
37
- /** Container format (mp4, webm, mkv) - inferred from extension if not specified */
38
- format?: string;
14
+ export interface MuxerOptions 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';
39
24
  }
40
25
  /**
41
- * Container muxer that accepts WebCodecs-compatible chunks
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.
42
31
  *
43
32
  * @example
44
33
  * ```typescript
45
- * const muxer = new Muxer({ path: 'output.mp4' });
46
- * await muxer.open();
47
- * await muxer.addVideoTrack({
48
- * codec: 'avc1.42001E',
49
- * codedWidth: 640,
50
- * codedHeight: 480,
51
- * framerate: 30,
52
- * description: spsNaluBuffer, // Optional: H.264 SPS/PPS
34
+ * const muxer = new Muxer({
35
+ * path: 'output.mp4',
36
+ * onFallback: (err) => console.warn('Using FFmpeg fallback:', err.message),
53
37
  * });
54
38
  *
55
- * // Write encoded chunks from VideoEncoder
56
- * encoder.configure({ codec: 'avc1.42001E', ... });
57
- * encoder.encode(frame);
58
- * // In output callback:
59
- * await muxer.writeVideoChunk(chunk);
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);
60
45
  *
61
- * await muxer.close();
46
+ * const result = await muxer.closeWithResult();
47
+ * console.log(`Muxed with ${result.backend} in ${result.durationMs}ms`);
62
48
  * ```
63
49
  */
64
- export declare class Muxer {
65
- private muxer;
50
+ export declare class Muxer implements IMuxer {
66
51
  private config;
67
- private _videoStreamIndex;
68
- private _audioStreamIndex;
69
- private _videoEncoder;
70
- private _audioEncoder;
71
- private _videoConfig;
72
- private _audioConfig;
52
+ private videoConfig;
53
+ private audioConfig;
54
+ private videoChunks;
55
+ private audioChunks;
73
56
  private _videoChunkCount;
74
57
  private _audioChunkCount;
75
- constructor(config: MuxerConfig);
76
- /**
77
- * Open the muxer for writing
78
- *
79
- * @param timeout - Operation timeout in milliseconds (default: 15000)
80
- */
58
+ private isOpen;
59
+ private usedBackend;
60
+ constructor(config: MuxerOptions);
81
61
  open(timeout?: number): Promise<void>;
82
- /**
83
- * Add a video track to the output
84
- *
85
- * @param config - Video track configuration
86
- * @returns Stream index for the video track
87
- */
88
62
  addVideoTrack(config: VideoTrackConfig): Promise<number>;
89
- /**
90
- * Add an audio track to the output
91
- *
92
- * @param config - Audio track configuration
93
- * @returns Stream index for the audio track
94
- */
95
63
  addAudioTrack(config: AudioTrackConfig): Promise<number>;
96
- /**
97
- * Write an encoded video chunk to the output container
98
- *
99
- * @param chunk - EncodedVideoChunk from VideoEncoder
100
- */
101
64
  writeVideoChunk(chunk: EncodedVideoChunk): Promise<void>;
102
- /**
103
- * Write an encoded audio chunk to the output container
104
- *
105
- * @param chunk - EncodedAudioChunk from AudioEncoder
106
- */
107
65
  writeAudioChunk(chunk: EncodedAudioChunk): Promise<void>;
108
66
  /**
109
- * Finalize and close the muxer
110
- *
111
- * @param timeout - Operation timeout in milliseconds (default: 10000)
67
+ * Close the muxer and finalize the output file
112
68
  */
113
69
  close(timeout?: number): Promise<void>;
114
70
  /**
115
- * Get the underlying node-av muxer (for advanced use)
116
- */
117
- get native(): NodeAvMuxer | null;
118
- /**
119
- * Get number of video chunks written
71
+ * Close the muxer and return detailed result including which backend was used
120
72
  */
73
+ closeWithResult(timeout?: number): Promise<MuxResult>;
74
+ private muxWithNodeAv;
75
+ private muxWithFFmpeg;
121
76
  get videoChunkCount(): number;
77
+ get audioChunkCount(): number;
122
78
  /**
123
- * Get number of audio chunks written
79
+ * Get which backend was used for muxing (available after close)
124
80
  */
125
- get audioChunkCount(): number;
81
+ get backend(): 'node-av' | 'ffmpeg-spawn' | null;
126
82
  }
127
83
  /**
128
- * Helper class for stream copy (remux) operations
129
- * This copies encoded data from one container to another without re-encoding
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
+ * ```
130
95
  */
131
- export declare class StreamCopier {
132
- private srcDemuxer;
133
- private dstMuxer;
134
- /**
135
- * Remux a file from one container format to another
136
- * This performs a stream copy without re-encoding
137
- */
138
- static remux(inputPath: string, outputPath: string, options?: {
139
- format?: string;
140
- }): Promise<void>;
141
- }
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>;
142
110
  //# sourceMappingURL=Muxer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Muxer.d.ts","sourceRoot":"","sources":["../../src/containers/Muxer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,IAAI,WAAW,EAerB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6DD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,MAAM,EAAE,WAAW;IAI/B;;;;OAIG;IACG,IAAI,CAAC,OAAO,GAAE,MAA8B,GAAG,OAAO,CAAC,IAAI,CAAC;IASlE;;;;;OAKG;IACG,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IA4B9D;;;;;OAKG;IACG,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAyB9D;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9D;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9D;;;;OAIG;IACG,KAAK,CAAC,OAAO,GAAE,MAA+B,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBpE;;OAEG;IACH,IAAI,MAAM,IAAI,WAAW,GAAG,IAAI,CAE/B;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,MAAM,CAE5B;CACF;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAA4B;IAE5C;;;OAGG;WACU,KAAK,CAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5B,OAAO,CAAC,IAAI,CAAC;CAiCjB"}
1
+ {"version":3,"file":"Muxer.d.ts","sourceRoot":"","sources":["../../src/containers/Muxer.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,YAAa,SAAQ,WAAW;IAC/C;;;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,KAAM,YAAW,MAAM;IAClC,OAAO,CAAC,MAAM,CAAe;IAC7B,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,YAAY;IAI1B,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"}