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
|
@@ -0,0 +1,239 @@
|
|
|
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 { MuxerError } from './muxer-types.js';
|
|
9
|
+
import { Muxer as NodeAvMuxer } from './Muxer.js';
|
|
10
|
+
import { FFmpegSpawnMuxer } from './FFmpegSpawnMuxer.js';
|
|
11
|
+
/**
|
|
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.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const muxer = new MuxerWithFallback({
|
|
21
|
+
* path: 'output.mp4',
|
|
22
|
+
* onFallback: (err) => console.warn('Using FFmpeg fallback:', err.message),
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
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);
|
|
31
|
+
*
|
|
32
|
+
* const result = await muxer.closeWithResult();
|
|
33
|
+
* console.log(`Muxed with ${result.backend} in ${result.durationMs}ms`);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class MuxerWithFallback {
|
|
37
|
+
config;
|
|
38
|
+
videoConfig = null;
|
|
39
|
+
audioConfig = null;
|
|
40
|
+
videoChunks = [];
|
|
41
|
+
audioChunks = [];
|
|
42
|
+
_videoChunkCount = 0;
|
|
43
|
+
_audioChunkCount = 0;
|
|
44
|
+
isOpen = false;
|
|
45
|
+
usedBackend = null;
|
|
46
|
+
constructor(config) {
|
|
47
|
+
this.config = config;
|
|
48
|
+
}
|
|
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()
|
|
53
|
+
}
|
|
54
|
+
async addVideoTrack(config) {
|
|
55
|
+
if (!this.isOpen) {
|
|
56
|
+
throw new MuxerError('Muxer not opened', 'node-av', 'addTrack');
|
|
57
|
+
}
|
|
58
|
+
this.videoConfig = config;
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
async addAudioTrack(config) {
|
|
62
|
+
if (!this.isOpen) {
|
|
63
|
+
throw new MuxerError('Muxer not opened', 'node-av', 'addTrack');
|
|
64
|
+
}
|
|
65
|
+
this.audioConfig = config;
|
|
66
|
+
return this.videoConfig ? 1 : 0;
|
|
67
|
+
}
|
|
68
|
+
async writeVideoChunk(chunk) {
|
|
69
|
+
if (!this.isOpen || !this.videoConfig) {
|
|
70
|
+
throw new MuxerError('Video track not configured', 'node-av', 'write');
|
|
71
|
+
}
|
|
72
|
+
this.videoChunks.push(chunk);
|
|
73
|
+
this._videoChunkCount++;
|
|
74
|
+
}
|
|
75
|
+
async writeAudioChunk(chunk) {
|
|
76
|
+
if (!this.isOpen || !this.audioConfig) {
|
|
77
|
+
throw new MuxerError('Audio track not configured', 'node-av', 'write');
|
|
78
|
+
}
|
|
79
|
+
this.audioChunks.push(chunk);
|
|
80
|
+
this._audioChunkCount++;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Close the muxer and finalize the output file
|
|
84
|
+
*/
|
|
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
|
+
};
|
|
100
|
+
}
|
|
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';
|
|
106
|
+
}
|
|
107
|
+
else if (this.config.forceBackend === 'node-av') {
|
|
108
|
+
await this.muxWithNodeAv(timeout);
|
|
109
|
+
this.usedBackend = 'node-av';
|
|
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
|
+
};
|
|
136
|
+
}
|
|
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 FFmpegSpawnMuxer({
|
|
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();
|
|
190
|
+
}
|
|
191
|
+
get videoChunkCount() {
|
|
192
|
+
return this._videoChunkCount;
|
|
193
|
+
}
|
|
194
|
+
get audioChunkCount() {
|
|
195
|
+
return this._audioChunkCount;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get which backend was used for muxing (available after close)
|
|
199
|
+
*/
|
|
200
|
+
get backend() {
|
|
201
|
+
return this.usedBackend;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
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
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export async function muxChunks(options) {
|
|
218
|
+
const muxer = new MuxerWithFallback({
|
|
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);
|
|
229
|
+
}
|
|
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);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return muxer.closeWithResult();
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=MuxerWithFallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MuxerWithFallback.js","sourceRoot":"","sources":["../../src/containers/MuxerWithFallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node-av Muxer - Fast muxer using node-av's FormatContext API
|
|
3
|
+
*
|
|
4
|
+
* Uses node-av's low-level FormatContext API to provide a WebCodecs-compatible interface
|
|
5
|
+
* that accepts EncodedVideoChunk and EncodedAudioChunk objects. This is the fast path
|
|
6
|
+
* (~5ms muxing time) used by the main Muxer class.
|
|
7
|
+
*/
|
|
8
|
+
import { FormatContext } from 'node-av';
|
|
9
|
+
import { EncodedVideoChunk } from '../core/EncodedVideoChunk.js';
|
|
10
|
+
import { EncodedAudioChunk } from '../core/EncodedAudioChunk.js';
|
|
11
|
+
import type { IMuxer, MuxerConfig, VideoTrackConfig, AudioTrackConfig } from './muxer-types.js';
|
|
12
|
+
export type { MuxerConfig, VideoTrackConfig, AudioTrackConfig } from './muxer-types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Node-av based muxer that accepts WebCodecs-compatible chunks
|
|
15
|
+
*
|
|
16
|
+
* Uses node-av's low-level FormatContext API for direct packet writing.
|
|
17
|
+
* This is the fast implementation (~5ms) used internally by the main Muxer class.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const muxer = new NodeAvMuxer({ path: 'output.mp4' });
|
|
22
|
+
* await muxer.open();
|
|
23
|
+
* await muxer.addVideoTrack({
|
|
24
|
+
* codec: 'avc1.42001E',
|
|
25
|
+
* codedWidth: 640,
|
|
26
|
+
* codedHeight: 480,
|
|
27
|
+
* framerate: 30,
|
|
28
|
+
* description: spsNaluBuffer, // Optional: H.264 SPS/PPS
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Write encoded chunks from VideoEncoder
|
|
32
|
+
* await muxer.writeVideoChunk(chunk);
|
|
33
|
+
*
|
|
34
|
+
* await muxer.close();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class NodeAvMuxer implements IMuxer {
|
|
38
|
+
private formatContext;
|
|
39
|
+
private config;
|
|
40
|
+
private _videoStreamIndex;
|
|
41
|
+
private _audioStreamIndex;
|
|
42
|
+
private _videoConfig;
|
|
43
|
+
private _audioConfig;
|
|
44
|
+
private _videoChunkCount;
|
|
45
|
+
private _audioChunkCount;
|
|
46
|
+
private _headerWritten;
|
|
47
|
+
constructor(config: MuxerConfig);
|
|
48
|
+
/**
|
|
49
|
+
* Open the muxer for writing
|
|
50
|
+
*
|
|
51
|
+
* @param timeout - Operation timeout in milliseconds (default: 15000)
|
|
52
|
+
*/
|
|
53
|
+
open(timeout?: number): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Add a video track to the output
|
|
56
|
+
*
|
|
57
|
+
* @param config - Video track configuration
|
|
58
|
+
* @returns Stream index for the video track
|
|
59
|
+
*/
|
|
60
|
+
addVideoTrack(config: VideoTrackConfig): Promise<number>;
|
|
61
|
+
/**
|
|
62
|
+
* Add an audio track to the output
|
|
63
|
+
*
|
|
64
|
+
* @param config - Audio track configuration
|
|
65
|
+
* @returns Stream index for the audio track
|
|
66
|
+
*/
|
|
67
|
+
addAudioTrack(config: AudioTrackConfig): Promise<number>;
|
|
68
|
+
/**
|
|
69
|
+
* Write header if not already written
|
|
70
|
+
*/
|
|
71
|
+
private writeHeaderIfNeeded;
|
|
72
|
+
/**
|
|
73
|
+
* Write an encoded video chunk to the output container
|
|
74
|
+
*
|
|
75
|
+
* @param chunk - EncodedVideoChunk from VideoEncoder
|
|
76
|
+
*/
|
|
77
|
+
writeVideoChunk(chunk: EncodedVideoChunk): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Write an encoded audio chunk to the output container
|
|
80
|
+
*
|
|
81
|
+
* @param chunk - EncodedAudioChunk from AudioEncoder
|
|
82
|
+
*/
|
|
83
|
+
writeAudioChunk(chunk: EncodedAudioChunk): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Finalize and close the muxer
|
|
86
|
+
*
|
|
87
|
+
* @param timeout - Operation timeout in milliseconds (default: 10000)
|
|
88
|
+
*/
|
|
89
|
+
close(timeout?: number): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Get the underlying FormatContext (for advanced use)
|
|
92
|
+
*/
|
|
93
|
+
get native(): FormatContext | null;
|
|
94
|
+
/**
|
|
95
|
+
* Get number of video chunks written
|
|
96
|
+
*/
|
|
97
|
+
get videoChunkCount(): number;
|
|
98
|
+
/**
|
|
99
|
+
* Get number of audio chunks written
|
|
100
|
+
*/
|
|
101
|
+
get audioChunkCount(): number;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Helper class for stream copy (remux) operations
|
|
105
|
+
* This copies encoded data from one container to another without re-encoding
|
|
106
|
+
*/
|
|
107
|
+
export declare class StreamCopier {
|
|
108
|
+
private srcDemuxer;
|
|
109
|
+
private dstMuxer;
|
|
110
|
+
/**
|
|
111
|
+
* Remux a file from one container format to another
|
|
112
|
+
* This performs a stream copy without re-encoding
|
|
113
|
+
*/
|
|
114
|
+
static remux(inputPath: string, outputPath: string, options?: {
|
|
115
|
+
format?: string;
|
|
116
|
+
}): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=NodeAvMuxer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeAvMuxer.d.ts","sourceRoot":"","sources":["../../src/containers/NodeAvMuxer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EAiBd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAI1B,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAwCxF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,WAAY,YAAW,MAAM;IACxC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAAS;gBAEnB,MAAM,EAAE,WAAW;IAI/B;;;;OAIG;IACG,IAAI,CAAC,OAAO,GAAE,MAA8B,GAAG,OAAO,CAAC,IAAI,CAAC;IAelE;;;;;OAKG;IACG,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgC9D;;;;;OAKG;IACG,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IA+B9D;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C9D;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C9D;;;;OAIG;IACG,KAAK,CAAC,OAAO,GAAE,MAA+B,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBpE;;OAEG;IACH,IAAI,MAAM,IAAI,aAAa,GAAG,IAAI,CAEjC;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,CAAsC;IACxD,OAAO,CAAC,QAAQ,CAAoC;IAEpD;;;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"}
|