node-av 0.0.1
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/CHANGELOG.md +8 -0
- package/LICENSE.md +22 -0
- package/README.md +377 -0
- package/binding.gyp +78 -0
- package/dist/api/bitstream-filter.d.ts +246 -0
- package/dist/api/bitstream-filter.js +369 -0
- package/dist/api/bitstream-filter.js.map +1 -0
- package/dist/api/decoder.d.ts +257 -0
- package/dist/api/decoder.js +424 -0
- package/dist/api/decoder.js.map +1 -0
- package/dist/api/encoder.d.ts +298 -0
- package/dist/api/encoder.js +574 -0
- package/dist/api/encoder.js.map +1 -0
- package/dist/api/filter.d.ts +457 -0
- package/dist/api/filter.js +876 -0
- package/dist/api/filter.js.map +1 -0
- package/dist/api/hardware.d.ts +318 -0
- package/dist/api/hardware.js +558 -0
- package/dist/api/hardware.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.js +20 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/io-stream.d.ts +109 -0
- package/dist/api/io-stream.js +124 -0
- package/dist/api/io-stream.js.map +1 -0
- package/dist/api/media-input.d.ts +295 -0
- package/dist/api/media-input.js +456 -0
- package/dist/api/media-input.js.map +1 -0
- package/dist/api/media-output.d.ts +274 -0
- package/dist/api/media-output.js +486 -0
- package/dist/api/media-output.js.map +1 -0
- package/dist/api/pipeline.d.ts +117 -0
- package/dist/api/pipeline.js +836 -0
- package/dist/api/pipeline.js.map +1 -0
- package/dist/api/types.d.ts +440 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/utilities/audio-sample.d.ts +115 -0
- package/dist/api/utilities/audio-sample.js +110 -0
- package/dist/api/utilities/audio-sample.js.map +1 -0
- package/dist/api/utilities/channel-layout.d.ts +83 -0
- package/dist/api/utilities/channel-layout.js +87 -0
- package/dist/api/utilities/channel-layout.js.map +1 -0
- package/dist/api/utilities/image.d.ts +177 -0
- package/dist/api/utilities/image.js +183 -0
- package/dist/api/utilities/image.js.map +1 -0
- package/dist/api/utilities/index.d.ts +8 -0
- package/dist/api/utilities/index.js +17 -0
- package/dist/api/utilities/index.js.map +1 -0
- package/dist/api/utilities/media-type.d.ts +56 -0
- package/dist/api/utilities/media-type.js +60 -0
- package/dist/api/utilities/media-type.js.map +1 -0
- package/dist/api/utilities/pixel-format.d.ts +94 -0
- package/dist/api/utilities/pixel-format.js +102 -0
- package/dist/api/utilities/pixel-format.js.map +1 -0
- package/dist/api/utilities/sample-format.d.ts +132 -0
- package/dist/api/utilities/sample-format.js +144 -0
- package/dist/api/utilities/sample-format.js.map +1 -0
- package/dist/api/utilities/streaming.d.ts +104 -0
- package/dist/api/utilities/streaming.js +137 -0
- package/dist/api/utilities/streaming.js.map +1 -0
- package/dist/api/utilities/timestamp.d.ts +187 -0
- package/dist/api/utilities/timestamp.js +200 -0
- package/dist/api/utilities/timestamp.js.map +1 -0
- package/dist/api/utils.d.ts +61 -0
- package/dist/api/utils.js +330 -0
- package/dist/api/utils.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audio-fifo.d.ts +339 -0
- package/dist/lib/audio-fifo.js +365 -0
- package/dist/lib/audio-fifo.js.map +1 -0
- package/dist/lib/binding.d.ts +192 -0
- package/dist/lib/binding.js +70 -0
- package/dist/lib/binding.js.map +1 -0
- package/dist/lib/bitstream-filter-context.d.ts +345 -0
- package/dist/lib/bitstream-filter-context.js +407 -0
- package/dist/lib/bitstream-filter-context.js.map +1 -0
- package/dist/lib/bitstream-filter.d.ts +124 -0
- package/dist/lib/bitstream-filter.js +138 -0
- package/dist/lib/bitstream-filter.js.map +1 -0
- package/dist/lib/channel-layouts.d.ts +51 -0
- package/dist/lib/channel-layouts.js +55 -0
- package/dist/lib/channel-layouts.js.map +1 -0
- package/dist/lib/codec-context.d.ts +763 -0
- package/dist/lib/codec-context.js +974 -0
- package/dist/lib/codec-context.js.map +1 -0
- package/dist/lib/codec-parameters.d.ts +362 -0
- package/dist/lib/codec-parameters.js +460 -0
- package/dist/lib/codec-parameters.js.map +1 -0
- package/dist/lib/codec-parser.d.ts +185 -0
- package/dist/lib/codec-parser.js +193 -0
- package/dist/lib/codec-parser.js.map +1 -0
- package/dist/lib/codec.d.ts +432 -0
- package/dist/lib/codec.js +492 -0
- package/dist/lib/codec.js.map +1 -0
- package/dist/lib/constants.d.ts +2037 -0
- package/dist/lib/constants.js +1659 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/dictionary.d.ts +371 -0
- package/dist/lib/dictionary.js +406 -0
- package/dist/lib/dictionary.js.map +1 -0
- package/dist/lib/error.d.ts +216 -0
- package/dist/lib/error.js +254 -0
- package/dist/lib/error.js.map +1 -0
- package/dist/lib/filter-context.d.ts +445 -0
- package/dist/lib/filter-context.js +505 -0
- package/dist/lib/filter-context.js.map +1 -0
- package/dist/lib/filter-graph.d.ts +556 -0
- package/dist/lib/filter-graph.js +608 -0
- package/dist/lib/filter-graph.js.map +1 -0
- package/dist/lib/filter-inout.d.ts +205 -0
- package/dist/lib/filter-inout.js +264 -0
- package/dist/lib/filter-inout.js.map +1 -0
- package/dist/lib/filter.d.ts +231 -0
- package/dist/lib/filter.js +260 -0
- package/dist/lib/filter.js.map +1 -0
- package/dist/lib/format-context.d.ts +798 -0
- package/dist/lib/format-context.js +845 -0
- package/dist/lib/format-context.js.map +1 -0
- package/dist/lib/frame.d.ts +784 -0
- package/dist/lib/frame.js +933 -0
- package/dist/lib/frame.js.map +1 -0
- package/dist/lib/hardware-device-context.d.ts +407 -0
- package/dist/lib/hardware-device-context.js +429 -0
- package/dist/lib/hardware-device-context.js.map +1 -0
- package/dist/lib/hardware-frames-context.d.ts +374 -0
- package/dist/lib/hardware-frames-context.js +430 -0
- package/dist/lib/hardware-frames-context.js.map +1 -0
- package/dist/lib/index.d.ts +31 -0
- package/dist/lib/index.js +54 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/input-format.d.ts +216 -0
- package/dist/lib/input-format.js +246 -0
- package/dist/lib/input-format.js.map +1 -0
- package/dist/lib/io-context.d.ts +495 -0
- package/dist/lib/io-context.js +550 -0
- package/dist/lib/io-context.js.map +1 -0
- package/dist/lib/log.d.ts +201 -0
- package/dist/lib/log.js +219 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/native-types.d.ts +719 -0
- package/dist/lib/native-types.js +2 -0
- package/dist/lib/native-types.js.map +1 -0
- package/dist/lib/option.d.ts +589 -0
- package/dist/lib/option.js +853 -0
- package/dist/lib/option.js.map +1 -0
- package/dist/lib/output-format.d.ts +179 -0
- package/dist/lib/output-format.js +205 -0
- package/dist/lib/output-format.js.map +1 -0
- package/dist/lib/packet.d.ts +487 -0
- package/dist/lib/packet.js +558 -0
- package/dist/lib/packet.js.map +1 -0
- package/dist/lib/rational.d.ts +210 -0
- package/dist/lib/rational.js +233 -0
- package/dist/lib/rational.js.map +1 -0
- package/dist/lib/software-resample-context.d.ts +572 -0
- package/dist/lib/software-resample-context.js +610 -0
- package/dist/lib/software-resample-context.js.map +1 -0
- package/dist/lib/software-scale-context.d.ts +290 -0
- package/dist/lib/software-scale-context.js +308 -0
- package/dist/lib/software-scale-context.js.map +1 -0
- package/dist/lib/stream.d.ts +322 -0
- package/dist/lib/stream.js +408 -0
- package/dist/lib/stream.js.map +1 -0
- package/dist/lib/types.d.ts +59 -0
- package/dist/lib/types.js +8 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utilities.d.ts +346 -0
- package/dist/lib/utilities.js +424 -0
- package/dist/lib/utilities.js.map +1 -0
- package/install/check.js +113 -0
- package/install/ffmpeg.js +163 -0
- package/package.json +107 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaOutput - Unified Output Handler for FFmpeg
|
|
3
|
+
*
|
|
4
|
+
* Provides a high-level interface for writing media to various destinations.
|
|
5
|
+
* Supports files, streams, and custom IO with automatic format configuration.
|
|
6
|
+
*
|
|
7
|
+
* Central entry point for all media output operations.
|
|
8
|
+
* Manages FormatContext lifecycle and provides stream management.
|
|
9
|
+
*
|
|
10
|
+
* @module api/media-output
|
|
11
|
+
*/
|
|
12
|
+
import { AVFMT_FLAG_CUSTOM_IO, AVFMT_NOFILE, AVIO_FLAG_WRITE, FFmpegError, FormatContext, IOContext, Rational } from '../lib/index.js';
|
|
13
|
+
import { Encoder } from './encoder.js';
|
|
14
|
+
/**
|
|
15
|
+
* MediaOutput - High-level media output handler.
|
|
16
|
+
*
|
|
17
|
+
* Creates and manages media containers for writing encoded streams.
|
|
18
|
+
* Automatically handles format setup and stream configuration.
|
|
19
|
+
*
|
|
20
|
+
* Manages the FormatContext and provides convenient methods for
|
|
21
|
+
* adding streams, writing packets, and finalizing output.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { MediaOutput, Encoder } from 'node-av/api';
|
|
26
|
+
*
|
|
27
|
+
* // Open output file
|
|
28
|
+
* const output = await MediaOutput.open('output.mp4');
|
|
29
|
+
*
|
|
30
|
+
* // Add encoder stream
|
|
31
|
+
* const encoder = await Encoder.create('libx264', {
|
|
32
|
+
* width: 1920,
|
|
33
|
+
* height: 1080,
|
|
34
|
+
* pixelFormat: AV_PIX_FMT_YUV420P
|
|
35
|
+
* });
|
|
36
|
+
* const streamIdx = output.addStream(encoder);
|
|
37
|
+
*
|
|
38
|
+
* // Write header, packets, trailer
|
|
39
|
+
* await output.writeHeader();
|
|
40
|
+
* await output.writePacket(packet, streamIdx);
|
|
41
|
+
* await output.writeTrailer();
|
|
42
|
+
* await output.close();
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Stream copy without re-encoding
|
|
48
|
+
* const input = await MediaInput.open('input.mp4');
|
|
49
|
+
* const output = await MediaOutput.open('output.mkv');
|
|
50
|
+
*
|
|
51
|
+
* const videoStream = input.video();
|
|
52
|
+
* const streamIdx = output.addStream(videoStream);
|
|
53
|
+
*
|
|
54
|
+
* await output.writeHeader();
|
|
55
|
+
* for await (const packet of input.packets()) {
|
|
56
|
+
* if (packet.streamIndex === videoStream.index) {
|
|
57
|
+
* await output.writePacket(packet, streamIdx);
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* await output.writeTrailer();
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export class MediaOutput {
|
|
64
|
+
formatContext;
|
|
65
|
+
streams = new Map();
|
|
66
|
+
ioContext;
|
|
67
|
+
headerWritten = false;
|
|
68
|
+
trailerWritten = false;
|
|
69
|
+
closed = false;
|
|
70
|
+
/**
|
|
71
|
+
* Private constructor - use MediaOutput.open() instead.
|
|
72
|
+
*
|
|
73
|
+
* Initializes the output with a new FormatContext.
|
|
74
|
+
* The actual format setup happens in the static factory method.
|
|
75
|
+
*/
|
|
76
|
+
constructor() {
|
|
77
|
+
this.formatContext = new FormatContext();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Opens a media output for writing with custom IO callbacks or stream URL or file output
|
|
81
|
+
*
|
|
82
|
+
* Creates a FormatContext and prepares the output for writing.
|
|
83
|
+
* Automatically detects format from filename/URL if not specified.
|
|
84
|
+
*
|
|
85
|
+
* If io callbacks is used, it creates a FormatContext with custom IO handling.
|
|
86
|
+
* Format must be explicitly specified for custom IO.
|
|
87
|
+
*
|
|
88
|
+
* Uses avformat_alloc_output_context2() and avio_alloc_context() internally.
|
|
89
|
+
*
|
|
90
|
+
* @param target - File path or stream URL or custom IO callbacks
|
|
91
|
+
* @param options - Optional configuration
|
|
92
|
+
*
|
|
93
|
+
* @returns Promise resolving to MediaOutput instance
|
|
94
|
+
*
|
|
95
|
+
* @throws {Error} If format not specified or context allocation fails
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const callbacks = {
|
|
100
|
+
* write: (buffer) => myStream.write(buffer),
|
|
101
|
+
* seek: (offset, whence) => offset
|
|
102
|
+
* };
|
|
103
|
+
* const output = await MediaOutput.open(callbacks, { format: 'mp4' });
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const output = await MediaOutput.open('output.mp4');
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const output = await MediaOutput.open('rtmp://server/live/streamkey', { format: 'flv' });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
static async open(target, options) {
|
|
117
|
+
const output = new MediaOutput();
|
|
118
|
+
try {
|
|
119
|
+
if (typeof target === 'string') {
|
|
120
|
+
// File or stream URL
|
|
121
|
+
const ret = output.formatContext.allocOutputContext2(null, options?.format ?? null, target === '' ? null : target);
|
|
122
|
+
FFmpegError.throwIfError(ret, 'Failed to allocate output context');
|
|
123
|
+
// Check if we need to open IO
|
|
124
|
+
const oformat = output.formatContext.oformat;
|
|
125
|
+
if (target && oformat && !(oformat.flags & AVFMT_NOFILE)) {
|
|
126
|
+
// For file-based formats, we need to open the file using avio_open2
|
|
127
|
+
// FFmpeg will manage the AVIOContext internally
|
|
128
|
+
output.ioContext = new IOContext();
|
|
129
|
+
const openRet = await output.ioContext.open2(target, AVIO_FLAG_WRITE);
|
|
130
|
+
FFmpegError.throwIfError(openRet, `Failed to open output file: ${target}`);
|
|
131
|
+
output.formatContext.pb = output.ioContext;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Custom IO with callbacks - format is required
|
|
136
|
+
if (!options?.format) {
|
|
137
|
+
throw new Error('Format must be specified for custom IO');
|
|
138
|
+
}
|
|
139
|
+
const ret = output.formatContext.allocOutputContext2(null, options.format, null);
|
|
140
|
+
FFmpegError.throwIfError(ret, 'Failed to allocate output context');
|
|
141
|
+
// Setup custom IO with callbacks
|
|
142
|
+
output.ioContext = new IOContext();
|
|
143
|
+
output.ioContext.allocContextWithCallbacks(options.bufferSize ?? 4096, 1, target.read, target.write, target.seek);
|
|
144
|
+
output.ioContext.maxPacketSize = options.bufferSize ?? 4096;
|
|
145
|
+
output.formatContext.pb = output.ioContext;
|
|
146
|
+
output.formatContext.flags = AVFMT_FLAG_CUSTOM_IO;
|
|
147
|
+
}
|
|
148
|
+
return output;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// Cleanup on error
|
|
152
|
+
if (output.ioContext) {
|
|
153
|
+
try {
|
|
154
|
+
if (output.formatContext.flags & AVFMT_FLAG_CUSTOM_IO) {
|
|
155
|
+
// Clear the pb reference first
|
|
156
|
+
output.formatContext.pb = null;
|
|
157
|
+
// For custom IO with callbacks, free the context
|
|
158
|
+
output.ioContext.freeContext();
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// For file-based IO, close the file handle
|
|
162
|
+
await output.ioContext.closep();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// Ignore errors
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (output.formatContext) {
|
|
170
|
+
try {
|
|
171
|
+
output.formatContext.freeContext();
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Ignore errors
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Adds a stream to the output container.
|
|
182
|
+
*
|
|
183
|
+
* Creates a new stream in the output format context.
|
|
184
|
+
* Copies codec parameters from encoder or input stream.
|
|
185
|
+
*
|
|
186
|
+
* Uses avformat_new_stream() and avcodec_parameters_copy() internally.
|
|
187
|
+
*
|
|
188
|
+
* @param source - Encoder for transcoding or Stream for copying
|
|
189
|
+
* @param options - Optional stream configuration
|
|
190
|
+
* @param options.timeBase - Custom output timebase for the stream.
|
|
191
|
+
* If not specified, uses the source's timebase.
|
|
192
|
+
* When set, packets will be automatically rescaled
|
|
193
|
+
* from source timebase to this output timebase.
|
|
194
|
+
*
|
|
195
|
+
* @returns Stream index for packet writing
|
|
196
|
+
*
|
|
197
|
+
* @throws {Error} If called after header is written or output is closed
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* // Add encoder stream (transcoding) - uses encoder's timebase
|
|
202
|
+
* const encoder = await Encoder.create('libx264', { width: 1920, height: 1080 });
|
|
203
|
+
* const streamIdx = output.addStream(encoder);
|
|
204
|
+
* ```
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* // Add stream for direct copy - uses input stream's timebase
|
|
209
|
+
* const inputStream = input.video();
|
|
210
|
+
* const streamIdx = output.addStream(inputStream);
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* // Custom output timebase (e.g., for format requirements)
|
|
216
|
+
* const streamIdx = output.addStream(encoder, {
|
|
217
|
+
* timeBase: { num: 1, den: 90000 } // MPEG-TS standard
|
|
218
|
+
* });
|
|
219
|
+
* // Packets from encoder will be automatically rescaled to 1/90000
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
addStream(source, options) {
|
|
223
|
+
if (this.closed) {
|
|
224
|
+
throw new Error('MediaOutput is closed');
|
|
225
|
+
}
|
|
226
|
+
if (this.headerWritten) {
|
|
227
|
+
throw new Error('Cannot add streams after header is written');
|
|
228
|
+
}
|
|
229
|
+
const stream = this.formatContext.newStream(null);
|
|
230
|
+
if (!stream) {
|
|
231
|
+
throw new Error('Failed to create new stream');
|
|
232
|
+
}
|
|
233
|
+
let isStreamCopy = false;
|
|
234
|
+
let sourceTimeBase;
|
|
235
|
+
if (source instanceof Encoder) {
|
|
236
|
+
// Transcoding with encoder
|
|
237
|
+
const codecContext = source.getCodecContext();
|
|
238
|
+
if (!codecContext) {
|
|
239
|
+
throw new Error('Failed to get codec context from encoder');
|
|
240
|
+
}
|
|
241
|
+
const ret = stream.codecpar.fromContext(codecContext);
|
|
242
|
+
FFmpegError.throwIfError(ret, 'Failed to copy codec parameters from encoder');
|
|
243
|
+
// Store the encoder's timebase as source (we'll need it for rescaling)
|
|
244
|
+
sourceTimeBase = codecContext.timeBase;
|
|
245
|
+
// Output stream uses encoder's timebase (or custom if specified)
|
|
246
|
+
stream.timeBase = options?.timeBase ? new Rational(options.timeBase.num, options.timeBase.den) : codecContext.timeBase;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// Stream copy
|
|
250
|
+
const ret = source.codecpar.copy(stream.codecpar);
|
|
251
|
+
FFmpegError.throwIfError(ret, 'Failed to copy codec parameters');
|
|
252
|
+
// Store the input stream's timebase as source (we'll need it for rescaling)
|
|
253
|
+
sourceTimeBase = source.timeBase;
|
|
254
|
+
// Output stream uses input stream's timebase (or custom if specified)
|
|
255
|
+
stream.timeBase = options?.timeBase ? new Rational(options.timeBase.num, options.timeBase.den) : source.timeBase;
|
|
256
|
+
stream.codecpar.codecTag = 0; // Important for format compatibility
|
|
257
|
+
isStreamCopy = true;
|
|
258
|
+
}
|
|
259
|
+
this.streams.set(stream.index, {
|
|
260
|
+
stream,
|
|
261
|
+
timeBase: stream.timeBase,
|
|
262
|
+
isStreamCopy,
|
|
263
|
+
sourceTimeBase,
|
|
264
|
+
});
|
|
265
|
+
return stream.index;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Writes a packet to the output container.
|
|
269
|
+
*
|
|
270
|
+
* Writes an encoded packet to the specified stream.
|
|
271
|
+
* Automatically sets the stream index on the packet.
|
|
272
|
+
*
|
|
273
|
+
* Uses av_interleaved_write_frame() internally.
|
|
274
|
+
*
|
|
275
|
+
* @param packet - Packet to write
|
|
276
|
+
* @param streamIndex - Target stream index
|
|
277
|
+
*
|
|
278
|
+
* @throws {Error} If header not written, trailer already written, or invalid stream
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* const encoded = await encoder.encode(frame);
|
|
283
|
+
* if (encoded) {
|
|
284
|
+
* await output.writePacket(encoded, streamIdx);
|
|
285
|
+
* encoded.free();
|
|
286
|
+
* }
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
async writePacket(packet, streamIndex) {
|
|
290
|
+
if (this.closed) {
|
|
291
|
+
throw new Error('MediaOutput is closed');
|
|
292
|
+
}
|
|
293
|
+
if (!this.headerWritten) {
|
|
294
|
+
throw new Error('Header must be written before packets');
|
|
295
|
+
}
|
|
296
|
+
if (this.trailerWritten) {
|
|
297
|
+
throw new Error('Cannot write packets after trailer');
|
|
298
|
+
}
|
|
299
|
+
const streamInfo = this.streams.get(streamIndex);
|
|
300
|
+
if (!streamInfo) {
|
|
301
|
+
throw new Error(`Invalid stream index: ${streamIndex}`);
|
|
302
|
+
}
|
|
303
|
+
// Set stream index
|
|
304
|
+
packet.streamIndex = streamIndex;
|
|
305
|
+
// Rescale packet timestamps if source and output timebases differ
|
|
306
|
+
// Note: The stream's timebase may have been changed by writeHeader (e.g., MP4 uses 1/time_scale)
|
|
307
|
+
if (streamInfo.sourceTimeBase) {
|
|
308
|
+
const outputStream = this.formatContext.streams?.[streamIndex];
|
|
309
|
+
if (outputStream) {
|
|
310
|
+
// Only rescale if timebases actually differ
|
|
311
|
+
const srcTb = streamInfo.sourceTimeBase;
|
|
312
|
+
const dstTb = outputStream.timeBase;
|
|
313
|
+
if (srcTb.num !== dstTb.num || srcTb.den !== dstTb.den) {
|
|
314
|
+
packet.rescaleTs(streamInfo.sourceTimeBase, outputStream.timeBase);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Write the packet
|
|
319
|
+
const ret = await this.formatContext.interleavedWriteFrame(packet);
|
|
320
|
+
FFmpegError.throwIfError(ret, 'Failed to write packet');
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Writes the output file header.
|
|
324
|
+
*
|
|
325
|
+
* Writes the container header with stream information.
|
|
326
|
+
* Must be called before writing any packets.
|
|
327
|
+
*
|
|
328
|
+
* Uses avformat_write_header() internally.
|
|
329
|
+
*
|
|
330
|
+
* @param options - Optional header options (format-specific)
|
|
331
|
+
*
|
|
332
|
+
* @throws {Error} If header already written or output is closed
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* ```typescript
|
|
336
|
+
* await output.writeHeader();
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
async writeHeader() {
|
|
340
|
+
if (this.closed) {
|
|
341
|
+
throw new Error('MediaOutput is closed');
|
|
342
|
+
}
|
|
343
|
+
if (this.headerWritten) {
|
|
344
|
+
throw new Error('Header already written');
|
|
345
|
+
}
|
|
346
|
+
const ret = await this.formatContext.writeHeader();
|
|
347
|
+
FFmpegError.throwIfError(ret, 'Failed to write header');
|
|
348
|
+
this.headerWritten = true;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Writes the output file trailer.
|
|
352
|
+
*
|
|
353
|
+
* Finalizes the output file with necessary metadata.
|
|
354
|
+
* Should be called after all packets have been written.
|
|
355
|
+
*
|
|
356
|
+
* Uses av_write_trailer() internally.
|
|
357
|
+
*
|
|
358
|
+
* @throws {Error} If header not written, trailer already written, or output is closed
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* await output.writeTrailer();
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
async writeTrailer() {
|
|
366
|
+
if (this.closed) {
|
|
367
|
+
throw new Error('MediaOutput is closed');
|
|
368
|
+
}
|
|
369
|
+
if (!this.headerWritten) {
|
|
370
|
+
throw new Error('Cannot write trailer without header');
|
|
371
|
+
}
|
|
372
|
+
if (this.trailerWritten) {
|
|
373
|
+
throw new Error('Trailer already written');
|
|
374
|
+
}
|
|
375
|
+
const ret = await this.formatContext.writeTrailer();
|
|
376
|
+
FFmpegError.throwIfError(ret, 'Failed to write trailer');
|
|
377
|
+
this.trailerWritten = true;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Closes the output and releases all resources.
|
|
381
|
+
*
|
|
382
|
+
* Writes trailer if needed, closes IO context, and frees format context.
|
|
383
|
+
* Safe to call multiple times.
|
|
384
|
+
*
|
|
385
|
+
* Uses av_write_trailer(), avio_closep(), and avformat_free_context() internally.
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* await output.close();
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
async close() {
|
|
393
|
+
if (this.closed) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
this.closed = true;
|
|
397
|
+
// Try to write trailer if header was written but trailer wasn't
|
|
398
|
+
try {
|
|
399
|
+
if (this.headerWritten && !this.trailerWritten) {
|
|
400
|
+
await this.formatContext.writeTrailer();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// Ignore errors
|
|
405
|
+
}
|
|
406
|
+
// Clear pb reference first to prevent use-after-free
|
|
407
|
+
if (this.ioContext) {
|
|
408
|
+
this.formatContext.pb = null;
|
|
409
|
+
}
|
|
410
|
+
// For file-based IO, close the file handle via closep
|
|
411
|
+
// For custom IO, the context will be freed below
|
|
412
|
+
if (this.ioContext && !(this.formatContext.flags & AVFMT_FLAG_CUSTOM_IO)) {
|
|
413
|
+
try {
|
|
414
|
+
await this.ioContext.closep();
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
// Ignore errors
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// Free format context
|
|
421
|
+
if (this.formatContext) {
|
|
422
|
+
try {
|
|
423
|
+
this.formatContext.freeContext();
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
// Ignore errors
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// Now free custom IO context if present
|
|
430
|
+
if (this.ioContext && this.formatContext.flags & AVFMT_FLAG_CUSTOM_IO) {
|
|
431
|
+
try {
|
|
432
|
+
this.ioContext.freeContext();
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
// Ignore errors
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Gets information about a specific stream.
|
|
441
|
+
*/
|
|
442
|
+
getStreamInfo(streamIndex) {
|
|
443
|
+
return this.streams.get(streamIndex);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Gets all stream indices.
|
|
447
|
+
*/
|
|
448
|
+
getStreamIndices() {
|
|
449
|
+
return Array.from(this.streams.keys());
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Checks if header has been written.
|
|
453
|
+
*/
|
|
454
|
+
isHeaderWritten() {
|
|
455
|
+
return this.headerWritten;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Checks if trailer has been written.
|
|
459
|
+
*/
|
|
460
|
+
isTrailerWritten() {
|
|
461
|
+
return this.trailerWritten;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Gets the underlying format context.
|
|
465
|
+
*
|
|
466
|
+
* For advanced use cases requiring direct format context access.
|
|
467
|
+
*/
|
|
468
|
+
getFormatContext() {
|
|
469
|
+
return this.formatContext;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* AsyncDisposable implementation for using statement.
|
|
473
|
+
*
|
|
474
|
+
* Enables automatic resource cleanup with await using syntax.
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```typescript
|
|
478
|
+
* await using output = await MediaOutput.open('output.mp4');
|
|
479
|
+
* // Output automatically closes when scope ends
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
async [Symbol.asyncDispose]() {
|
|
483
|
+
await this.close();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
//# sourceMappingURL=media-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-output.js","sourceRoot":"","sources":["../../src/api/media-output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACvI,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAYvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,OAAO,WAAW;IACd,aAAa,CAAgB;IAC7B,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IACxC,SAAS,CAAa;IACtB,aAAa,GAAG,KAAK,CAAC;IACtB,cAAc,GAAG,KAAK,CAAC;IACvB,MAAM,GAAG,KAAK,CAAC;IAEvB;;;;;OAKG;IACH;QACE,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAkC,EAAE,OAA4B;QAChF,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAEjC,IAAI,CAAC;YACH,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC/B,qBAAqB;gBACrB,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,IAAI,EAAE,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBACnH,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,mCAAmC,CAAC,CAAC;gBAEnE,8BAA8B;gBAC9B,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC7C,IAAI,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,EAAE,CAAC;oBACzD,oEAAoE;oBACpE,gDAAgD;oBAChD,MAAM,CAAC,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;oBACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtE,WAAW,CAAC,YAAY,CAAC,OAAO,EAAE,+BAA+B,MAAM,EAAE,CAAC,CAAC;oBAC3E,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;gBAC7C,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,gDAAgD;gBAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;oBACrB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBAC5D,CAAC;gBAED,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACjF,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,mCAAmC,CAAC,CAAC;gBAEnE,iCAAiC;gBACjC,MAAM,CAAC,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;gBACnC,MAAM,CAAC,SAAS,CAAC,yBAAyB,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBAClH,MAAM,CAAC,SAAS,CAAC,aAAa,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;gBAC5D,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;gBAC3C,MAAM,CAAC,aAAa,CAAC,KAAK,GAAG,oBAAoB,CAAC;YACpD,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mBAAmB;YACnB,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,IAAI,CAAC;oBACH,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,GAAG,oBAAoB,EAAE,CAAC;wBACtD,+BAA+B;wBAC/B,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,IAAI,CAAC;wBAC/B,iDAAiD;wBACjD,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;oBACjC,CAAC;yBAAM,CAAC;wBACN,2CAA2C;wBAC3C,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;oBAClC,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,gBAAgB;gBAClB,CAAC;YACH,CAAC;YACD,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;gBACzB,IAAI,CAAC;oBACH,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC;gBACrC,CAAC;gBAAC,MAAM,CAAC;oBACP,gBAAgB;gBAClB,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;IACH,SAAS,CACP,MAAwB,EACxB,OAEC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,YAAY,GAAG,KAAK,CAAC;QACzB,IAAI,cAAqC,CAAC;QAE1C,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC9B,2BAA2B;YAC3B,MAAM,YAAY,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;YAC9C,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC9D,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;YACtD,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,8CAA8C,CAAC,CAAC;YAE9E,uEAAuE;YACvE,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAC;YAEvC,iEAAiE;YACjE,MAAM,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC;QACzH,CAAC;aAAM,CAAC;YACN,cAAc;YACd,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClD,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,iCAAiC,CAAC,CAAC;YAEjE,4EAA4E;YAC5E,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC;YAEjC,sEAAsE;YACtE,MAAM,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;YACjH,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,qCAAqC;YACnE,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE;YAC7B,MAAM;YACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,YAAY;YACZ,cAAc;SACf,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,WAAmB;QACnD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,yBAAyB,WAAW,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,mBAAmB;QACnB,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;QAEjC,kEAAkE;QAClE,iGAAiG;QACjG,IAAI,UAAU,CAAC,cAAc,EAAE,CAAC;YAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC;YAC/D,IAAI,YAAY,EAAE,CAAC;gBACjB,4CAA4C;gBAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,CAAC;gBACxC,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC;gBACpC,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC;oBACvD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,cAAc,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;QACH,CAAC;QAED,mBAAmB;QACnB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACnE,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,WAAW;QACf,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC;QACnD,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;QACxD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,YAAY;QAChB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC;QACpD,WAAW,CAAC,YAAY,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;QACzD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAEnB,gEAAgE;QAChE,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAC/C,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC;YAC1C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,gBAAgB;QAClB,CAAC;QAED,qDAAqD;QACrD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC,aAAa,CAAC,EAAE,GAAG,IAAI,CAAC;QAC/B,CAAC;QAED,sDAAsD;QACtD,iDAAiD;QACjD,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,oBAAoB,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC;YACnC,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;QAED,wCAAwC;QACxC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,oBAAoB,EAAE,CAAC;YACtE,IAAI,CAAC;gBACH,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,WAAmB;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QACzB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline - Stream-based media processing pipeline
|
|
3
|
+
*
|
|
4
|
+
* Provides a Node.js-style pipeline function for chaining media processing components.
|
|
5
|
+
* Automatically handles type conversions, buffering, and flushing.
|
|
6
|
+
*
|
|
7
|
+
* Supports two modes:
|
|
8
|
+
* 1. Simple: Single stream with variable parameters
|
|
9
|
+
* 2. Named: Multiple streams with named routing with variable parameters
|
|
10
|
+
*
|
|
11
|
+
* @module api/pipeline
|
|
12
|
+
*/
|
|
13
|
+
import type { Frame, Packet } from '../lib/index.js';
|
|
14
|
+
import type { BitStreamFilterAPI } from './bitstream-filter.js';
|
|
15
|
+
import type { Decoder } from './decoder.js';
|
|
16
|
+
import type { Encoder } from './encoder.js';
|
|
17
|
+
import type { FilterAPI } from './filter.js';
|
|
18
|
+
import type { MediaInput } from './media-input.js';
|
|
19
|
+
import type { MediaOutput } from './media-output.js';
|
|
20
|
+
type StreamName = 'video' | 'audio';
|
|
21
|
+
type NamedInputs<K extends StreamName = StreamName> = Pick<Record<StreamName, MediaInput>, K>;
|
|
22
|
+
type NamedStages<K extends StreamName = StreamName> = Pick<Record<StreamName, (Decoder | FilterAPI | FilterAPI[] | Encoder | BitStreamFilterAPI | BitStreamFilterAPI[])[] | 'passthrough'>, K>;
|
|
23
|
+
type NamedOutputs<K extends StreamName = StreamName> = Pick<Record<StreamName, MediaOutput>, K>;
|
|
24
|
+
/**
|
|
25
|
+
* Pipeline control interface for managing pipeline execution
|
|
26
|
+
*/
|
|
27
|
+
export interface PipelineControl {
|
|
28
|
+
/**
|
|
29
|
+
* Stop the pipeline gracefully
|
|
30
|
+
*/
|
|
31
|
+
stop(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Check if the pipeline has been stopped
|
|
34
|
+
*/
|
|
35
|
+
isStopped(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Promise that resolves when the pipeline completes
|
|
38
|
+
*/
|
|
39
|
+
readonly completion: Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Full transcoding pipeline: input → decoder → encoder → output
|
|
43
|
+
*/
|
|
44
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, encoder: Encoder, output: MediaOutput): PipelineControl;
|
|
45
|
+
/**
|
|
46
|
+
* Full transcoding pipeline with filter: input → decoder → filter → encoder → output
|
|
47
|
+
*/
|
|
48
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, filter: FilterAPI | FilterAPI[], encoder: Encoder, output: MediaOutput): PipelineControl;
|
|
49
|
+
/**
|
|
50
|
+
* Transcoding with bitstream filter: input → decoder → encoder → bsf → output
|
|
51
|
+
*/
|
|
52
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, encoder: Encoder, bsf: BitStreamFilterAPI | BitStreamFilterAPI[], output: MediaOutput): PipelineControl;
|
|
53
|
+
/**
|
|
54
|
+
* Full pipeline with filter and bsf: input → decoder → filter → encoder → bsf → output
|
|
55
|
+
*/
|
|
56
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, filter: FilterAPI | FilterAPI[], encoder: Encoder, bsf: BitStreamFilterAPI | BitStreamFilterAPI[], output: MediaOutput): PipelineControl;
|
|
57
|
+
/**
|
|
58
|
+
* Decode + multiple filters + encode: input → decoder → filter1 → filter2 → encoder → output
|
|
59
|
+
*/
|
|
60
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, filter1: FilterAPI, filter2: FilterAPI, encoder: Encoder, output: MediaOutput): PipelineControl;
|
|
61
|
+
/**
|
|
62
|
+
* Stream copy pipeline: input → output (copies all streams)
|
|
63
|
+
*/
|
|
64
|
+
export declare function pipeline(source: MediaInput, output: MediaOutput): PipelineControl;
|
|
65
|
+
/**
|
|
66
|
+
* Stream copy with bitstream filter: input → bsf → output
|
|
67
|
+
*/
|
|
68
|
+
export declare function pipeline(source: MediaInput, bsf: BitStreamFilterAPI | BitStreamFilterAPI[], output: MediaOutput): PipelineControl;
|
|
69
|
+
/**
|
|
70
|
+
* Filter + encode + output: frames → filter → encoder → output
|
|
71
|
+
*/
|
|
72
|
+
export declare function pipeline(source: AsyncIterable<Frame>, filter: FilterAPI | FilterAPI[], encoder: Encoder, output: MediaOutput): PipelineControl;
|
|
73
|
+
/**
|
|
74
|
+
* Encode + output: frames → encoder → output
|
|
75
|
+
*/
|
|
76
|
+
export declare function pipeline(source: AsyncIterable<Frame>, encoder: Encoder, output: MediaOutput): PipelineControl;
|
|
77
|
+
/**
|
|
78
|
+
* Partial pipeline: input → decoder (returns frames)
|
|
79
|
+
*/
|
|
80
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder): AsyncGenerator<Frame>;
|
|
81
|
+
/**
|
|
82
|
+
* Partial pipeline: input → decoder → filter (returns frames)
|
|
83
|
+
*/
|
|
84
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, filter: FilterAPI | FilterAPI[]): AsyncGenerator<Frame>;
|
|
85
|
+
/**
|
|
86
|
+
* Partial pipeline: input → decoder → filter → encoder (returns packets)
|
|
87
|
+
*/
|
|
88
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, filter: FilterAPI | FilterAPI[], encoder: Encoder): AsyncGenerator<Packet>;
|
|
89
|
+
/**
|
|
90
|
+
* Partial pipeline: input → decoder → encoder (returns packets)
|
|
91
|
+
*/
|
|
92
|
+
export declare function pipeline(source: MediaInput, decoder: Decoder, encoder: Encoder): AsyncGenerator<Packet>;
|
|
93
|
+
/**
|
|
94
|
+
* Partial pipeline: frames → filter (returns frames)
|
|
95
|
+
*/
|
|
96
|
+
export declare function pipeline(source: AsyncIterable<Frame>, filter: FilterAPI | FilterAPI[]): AsyncGenerator<Frame>;
|
|
97
|
+
/**
|
|
98
|
+
* Partial pipeline: frames → encoder (returns packets)
|
|
99
|
+
*/
|
|
100
|
+
export declare function pipeline(source: AsyncIterable<Frame>, encoder: Encoder): AsyncGenerator<Packet>;
|
|
101
|
+
/**
|
|
102
|
+
* Partial pipeline: frames → filter → encoder (returns packets)
|
|
103
|
+
*/
|
|
104
|
+
export declare function pipeline(source: AsyncIterable<Frame>, filter: FilterAPI | FilterAPI[], encoder: Encoder): AsyncGenerator<Packet>;
|
|
105
|
+
/**
|
|
106
|
+
* Named pipeline with single output - all streams go to the same output
|
|
107
|
+
*/
|
|
108
|
+
export declare function pipeline<K extends StreamName>(inputs: NamedInputs<K>, stages: NamedStages<K>, output: MediaOutput): PipelineControl;
|
|
109
|
+
/**
|
|
110
|
+
* Named pipeline with multiple outputs - each stream has its own output
|
|
111
|
+
*/
|
|
112
|
+
export declare function pipeline<K extends StreamName>(inputs: NamedInputs<K>, stages: NamedStages<K>, outputs: NamedOutputs<K>): PipelineControl;
|
|
113
|
+
/**
|
|
114
|
+
* Partial named pipeline (returns generators for further processing)
|
|
115
|
+
*/
|
|
116
|
+
export declare function pipeline<K extends StreamName, T extends Packet | Frame = Packet | Frame>(inputs: NamedInputs<K>, stages: NamedStages<K>): Record<K, AsyncGenerator<T>>;
|
|
117
|
+
export {};
|