node-av 6.0.0-beta.5 → 6.0.0-beta.7
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 +14 -1
- package/dist/api/demuxer.js.map +1 -1
- package/dist/api/encoder-pool.d.ts +220 -0
- package/dist/api/encoder-pool.js +285 -0
- package/dist/api/encoder-pool.js.map +1 -0
- package/dist/api/encoder.d.ts +58 -0
- package/dist/api/encoder.js +150 -42
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +4 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/muxer.js.map +1 -1
- package/dist/api/scaler.d.ts +431 -0
- package/dist/api/scaler.js +620 -0
- package/dist/api/scaler.js.map +1 -0
- package/dist/api/utilities/electron-shared-texture.d.ts +41 -1
- package/dist/api/utilities/electron-shared-texture.js +41 -4
- package/dist/api/utilities/electron-shared-texture.js.map +1 -1
- package/dist/constants/constants.d.ts +98 -0
- package/dist/constants/constants.js +99 -0
- package/dist/constants/constants.js.map +1 -1
- package/dist/lib/binding.d.ts +3 -1
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/native-types.d.ts +37 -0
- package/package.json +9 -9
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { Encoder } from './encoder.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bounded LRU pool of image encoders keyed by resolution and pixel format.
|
|
4
|
+
*
|
|
5
|
+
* A single encoder cannot change dimensions after opening, so encoding frames of
|
|
6
|
+
* varying sizes needs one encoder per resolution. This pool routes each frame to
|
|
7
|
+
* the encoder matching its `width x height x pixelFormat`, creating one on demand
|
|
8
|
+
* and reusing it afterwards. Each frame is given a monotonic PTS internally, so
|
|
9
|
+
* callers can feed independent snapshots without managing timestamps.
|
|
10
|
+
*
|
|
11
|
+
* Pooled encoders are never flushed between frames, so this only suits intra-only
|
|
12
|
+
* image codecs (MJPEG, PNG, WebP, ...). Hardware frames work as-is: the encoder
|
|
13
|
+
* adopts the frame's hardware context. For a single one-off conversion use
|
|
14
|
+
* {@link Encoder.encodeOne} instead.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { EncoderPool } from 'node-av/api';
|
|
19
|
+
* import { FF_ENCODER_MJPEG } from 'node-av/constants';
|
|
20
|
+
*
|
|
21
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG);
|
|
22
|
+
* const a = await pool.encode(frame1920); // opens + caches
|
|
23
|
+
* const b = await pool.encode(frame640); // opens + caches
|
|
24
|
+
* const c = await pool.encode(frame1920); // reuses, no open
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // MJPEG quality: set the QSCALE flag on the pool, then carry the quality on
|
|
30
|
+
* // each frame (qscale 2 = best, 31 = worst). The `q` private option has no
|
|
31
|
+
* // effect on MJPEG.
|
|
32
|
+
* import { AV_CODEC_FLAG_QSCALE, FF_QP2LAMBDA } from 'node-av/constants';
|
|
33
|
+
*
|
|
34
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG, { maxSize: 4, flags: AV_CODEC_FLAG_QSCALE });
|
|
35
|
+
* frame.quality = 3 * FF_QP2LAMBDA;
|
|
36
|
+
* const jpeg = await pool.encode(frame);
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @see {@link Encoder.encodeOne} For a one-shot, stateless single-frame encode
|
|
40
|
+
*/
|
|
41
|
+
export class EncoderPool {
|
|
42
|
+
encoderCodec;
|
|
43
|
+
options;
|
|
44
|
+
maxSize;
|
|
45
|
+
flags;
|
|
46
|
+
// Insertion order doubles as LRU order: touched entries move to the end,
|
|
47
|
+
// so the first key is always the least-recently-used.
|
|
48
|
+
encoders = new Map();
|
|
49
|
+
/**
|
|
50
|
+
* Create a new encoder pool.
|
|
51
|
+
*
|
|
52
|
+
* @param encoderCodec - Encoder codec applied to every pooled encoder
|
|
53
|
+
*
|
|
54
|
+
* @param options - Encoder options forwarded to each encoder, plus the optional `maxSize` limit
|
|
55
|
+
*
|
|
56
|
+
* @throws {Error} If maxSize is less than 1
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG, { maxSize: 4, flags: AV_CODEC_FLAG_QSCALE });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
constructor(encoderCodec, options = {}) {
|
|
64
|
+
const { maxSize = 8, flags, ...encoderOptions } = options;
|
|
65
|
+
if (maxSize < 1) {
|
|
66
|
+
throw new Error(`EncoderPool maxSize must be at least 1, got ${maxSize}`);
|
|
67
|
+
}
|
|
68
|
+
this.encoderCodec = encoderCodec;
|
|
69
|
+
this.options = { threadCount: 1, ...encoderOptions };
|
|
70
|
+
this.maxSize = maxSize;
|
|
71
|
+
this.flags = flags === undefined ? [] : Array.isArray(flags) ? flags : [flags];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Number of encoders currently held alive.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* console.log(`Pool holds ${pool.size} encoders`);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
get size() {
|
|
82
|
+
return this.encoders.size;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Encode a frame into a self-contained image buffer.
|
|
86
|
+
*
|
|
87
|
+
* Routes the frame to the encoder matching its dimensions and pixel format,
|
|
88
|
+
* creating and caching one if necessary.
|
|
89
|
+
*
|
|
90
|
+
* @param frame - Frame to encode
|
|
91
|
+
*
|
|
92
|
+
* @returns Encoded image bytes
|
|
93
|
+
*
|
|
94
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
95
|
+
*
|
|
96
|
+
* @throws {Error} If the encoder produced no output
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const jpeg = await pool.encode(frame);
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @see {@link encodeSync} For synchronous version
|
|
104
|
+
*/
|
|
105
|
+
async encode(frame) {
|
|
106
|
+
const key = this.keyFor(frame);
|
|
107
|
+
let pooled = this.touch(key);
|
|
108
|
+
if (!pooled) {
|
|
109
|
+
const encoder = await Encoder.create(this.encoderCodec, this.options);
|
|
110
|
+
pooled = this.touch(key);
|
|
111
|
+
if (pooled) {
|
|
112
|
+
encoder.close();
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
pooled = this.set(key, encoder);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const savedPts = frame.pts;
|
|
119
|
+
frame.pts = pooled.nextPts++;
|
|
120
|
+
try {
|
|
121
|
+
return this.extract(await pooled.encoder.encodeAll(frame), pooled.encoder);
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
frame.pts = savedPts;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Encode a frame into a self-contained image buffer synchronously.
|
|
129
|
+
* Synchronous version of encode.
|
|
130
|
+
*
|
|
131
|
+
* @param frame - Frame to encode
|
|
132
|
+
*
|
|
133
|
+
* @returns Encoded image bytes
|
|
134
|
+
*
|
|
135
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
136
|
+
*
|
|
137
|
+
* @throws {Error} If the encoder produced no output
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const jpeg = pool.encodeSync(frame);
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* @see {@link encode} For asynchronous version
|
|
145
|
+
*/
|
|
146
|
+
encodeSync(frame) {
|
|
147
|
+
const key = this.keyFor(frame);
|
|
148
|
+
let pooled = this.touch(key);
|
|
149
|
+
pooled ??= this.set(key, Encoder.createSync(this.encoderCodec, this.options));
|
|
150
|
+
const savedPts = frame.pts;
|
|
151
|
+
frame.pts = pooled.nextPts++;
|
|
152
|
+
try {
|
|
153
|
+
return this.extract(pooled.encoder.encodeAllSync(frame), pooled.encoder);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
frame.pts = savedPts;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Close all pooled encoders and clear the pool.
|
|
161
|
+
*
|
|
162
|
+
* Automatically called by Symbol.dispose.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* pool.close();
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @see {@link Symbol.dispose} For automatic cleanup
|
|
170
|
+
*/
|
|
171
|
+
close() {
|
|
172
|
+
for (const { encoder } of this.encoders.values()) {
|
|
173
|
+
encoder.close();
|
|
174
|
+
}
|
|
175
|
+
this.encoders.clear();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Build the cache key from frame dimensions and pixel format.
|
|
179
|
+
*
|
|
180
|
+
* Pixel format is part of the key because the encoder is opened with the frame's
|
|
181
|
+
* format; a cached encoder for the same size but a different format would be invalid.
|
|
182
|
+
*
|
|
183
|
+
* @param frame - Frame to derive the key from
|
|
184
|
+
*
|
|
185
|
+
* @returns Cache key `width x height x pixelFormat`
|
|
186
|
+
*
|
|
187
|
+
* @internal
|
|
188
|
+
*/
|
|
189
|
+
keyFor(frame) {
|
|
190
|
+
return `${frame.width}x${frame.height}x${frame.format}`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Look up an encoder and mark it as most-recently-used.
|
|
194
|
+
*
|
|
195
|
+
* @param key - Cache key to look up
|
|
196
|
+
*
|
|
197
|
+
* @returns Pooled encoder, or undefined if not cached
|
|
198
|
+
*
|
|
199
|
+
* @internal
|
|
200
|
+
*/
|
|
201
|
+
touch(key) {
|
|
202
|
+
const pooled = this.encoders.get(key);
|
|
203
|
+
if (pooled) {
|
|
204
|
+
this.encoders.delete(key);
|
|
205
|
+
this.encoders.set(key, pooled);
|
|
206
|
+
}
|
|
207
|
+
return pooled;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Insert a freshly created encoder, evicting the least-recently-used one if full.
|
|
211
|
+
*
|
|
212
|
+
* @param key - Cache key for the encoder
|
|
213
|
+
*
|
|
214
|
+
* @param encoder - Freshly created encoder to cache
|
|
215
|
+
*
|
|
216
|
+
* @returns The pooled encoder entry
|
|
217
|
+
*
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
set(key, encoder) {
|
|
221
|
+
if (this.flags.length > 0) {
|
|
222
|
+
encoder.setCodecFlags(...this.flags);
|
|
223
|
+
}
|
|
224
|
+
const pooled = { encoder, nextPts: 0n };
|
|
225
|
+
this.encoders.set(key, pooled);
|
|
226
|
+
if (this.encoders.size > this.maxSize) {
|
|
227
|
+
const oldestKey = this.encoders.keys().next().value;
|
|
228
|
+
if (oldestKey !== undefined) {
|
|
229
|
+
const oldest = this.encoders.get(oldestKey);
|
|
230
|
+
this.encoders.delete(oldestKey);
|
|
231
|
+
oldest?.encoder.close();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return pooled;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract the encoded bytes from the produced packets and free them.
|
|
238
|
+
*
|
|
239
|
+
* packet.data returns a JS-owned copy, so the buffer stays valid after freeing.
|
|
240
|
+
*
|
|
241
|
+
* @param packets - Packets produced by the encoder
|
|
242
|
+
*
|
|
243
|
+
* @param encoder - Encoder that produced the packets (for error messages)
|
|
244
|
+
*
|
|
245
|
+
* @returns Encoded image bytes
|
|
246
|
+
*
|
|
247
|
+
* @throws {Error} If no packet was produced
|
|
248
|
+
*
|
|
249
|
+
* @internal
|
|
250
|
+
*/
|
|
251
|
+
extract(packets, encoder) {
|
|
252
|
+
try {
|
|
253
|
+
const data = packets[0]?.data;
|
|
254
|
+
if (!data) {
|
|
255
|
+
throw new Error(`Encoder '${encoder.getCodec().name}' produced no output for frame`);
|
|
256
|
+
}
|
|
257
|
+
return data;
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
for (const packet of packets) {
|
|
261
|
+
packet.free();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Dispose of the pool.
|
|
267
|
+
*
|
|
268
|
+
* Implements Disposable interface for automatic cleanup.
|
|
269
|
+
* Equivalent to calling close().
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```typescript
|
|
273
|
+
* {
|
|
274
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG, { maxSize: 4 });
|
|
275
|
+
* // Encode frames...
|
|
276
|
+
* } // All encoders automatically closed
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* @see {@link close} For manual cleanup
|
|
280
|
+
*/
|
|
281
|
+
[Symbol.dispose]() {
|
|
282
|
+
this.close();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=encoder-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoder-pool.js","sourceRoot":"","sources":["../../src/api/encoder-pool.ts"],"names":[],"mappings":"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"}
|
package/dist/api/encoder.d.ts
CHANGED
|
@@ -304,6 +304,64 @@ export declare class Encoder implements Disposable {
|
|
|
304
304
|
* @see {@link create} For async version
|
|
305
305
|
*/
|
|
306
306
|
static createSync<const C extends FFEncoderCodec | AVCodecID | Codec>(encoderCodec: C, options?: EncoderOptions<C>): Encoder;
|
|
307
|
+
/**
|
|
308
|
+
* Encode a single frame into a self-contained image buffer.
|
|
309
|
+
*
|
|
310
|
+
* One-shot, stateless helper for intra-only image codecs (MJPEG, PNG, WebP, ...).
|
|
311
|
+
* Creates a fresh encoder, encodes the frame, flushes and frees everything in one call.
|
|
312
|
+
* The encoder adopts dimensions, pixel format and hardware context from the frame,
|
|
313
|
+
* so any frame size works without reconfiguration.
|
|
314
|
+
*
|
|
315
|
+
* @param encoderCodec - Encoder codec (name, ID, branded constant, or Codec)
|
|
316
|
+
*
|
|
317
|
+
* @param frame - Frame to encode
|
|
318
|
+
*
|
|
319
|
+
* @param options - Optional encoder configuration (e.g. `{ options: { q: 3 } }` for MJPEG quality)
|
|
320
|
+
*
|
|
321
|
+
* @returns Encoded image bytes
|
|
322
|
+
*
|
|
323
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
324
|
+
*
|
|
325
|
+
* @throws {Error} If the encoder produced no output
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* const jpeg = await Encoder.encodeOne(FF_ENCODER_MJPEG, frame, { options: { q: 3 } });
|
|
330
|
+
* ```
|
|
331
|
+
*
|
|
332
|
+
* @see {@link EncoderPool} For reusing encoders across recurring resolutions
|
|
333
|
+
*/
|
|
334
|
+
static encodeOne<const C extends FFEncoderCodec | AVCodecID | Codec>(encoderCodec: C, frame: Frame, options?: EncoderOptions<C>): Promise<Buffer>;
|
|
335
|
+
/**
|
|
336
|
+
* Encode a single frame into a self-contained image buffer synchronously.
|
|
337
|
+
* Synchronous version of encodeOne.
|
|
338
|
+
*
|
|
339
|
+
* One-shot, stateless helper for intra-only image codecs (MJPEG, PNG, WebP, ...).
|
|
340
|
+
* Creates a fresh encoder, encodes the frame, flushes and frees everything in one call.
|
|
341
|
+
* The encoder adopts dimensions, pixel format and hardware context from the frame,
|
|
342
|
+
* so any frame size works without reconfiguration.
|
|
343
|
+
*
|
|
344
|
+
* @param encoderCodec - Encoder codec (name, ID, branded constant, or Codec)
|
|
345
|
+
*
|
|
346
|
+
* @param frame - Frame to encode
|
|
347
|
+
*
|
|
348
|
+
* @param options - Optional encoder configuration (e.g. `{ options: { q: 3 } }` for MJPEG quality)
|
|
349
|
+
*
|
|
350
|
+
* @returns Encoded image bytes
|
|
351
|
+
*
|
|
352
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
353
|
+
*
|
|
354
|
+
* @throws {Error} If the encoder produced no output
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* const jpeg = Encoder.encodeOneSync(FF_ENCODER_MJPEG, frame, { options: { q: 3 } });
|
|
359
|
+
* ```
|
|
360
|
+
*
|
|
361
|
+
* @see {@link encodeOne} For async version
|
|
362
|
+
* @see {@link EncoderPool} For reusing encoders across recurring resolutions
|
|
363
|
+
*/
|
|
364
|
+
static encodeOneSync<const C extends FFEncoderCodec | AVCodecID | Codec>(encoderCodec: C, frame: Frame, options?: EncoderOptions<C>): Buffer;
|
|
307
365
|
/**
|
|
308
366
|
* Check if encoder is open.
|
|
309
367
|
*
|
package/dist/api/encoder.js
CHANGED
|
@@ -389,6 +389,114 @@ export class Encoder {
|
|
|
389
389
|
}
|
|
390
390
|
return encoder;
|
|
391
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Encode a single frame into a self-contained image buffer.
|
|
394
|
+
*
|
|
395
|
+
* One-shot, stateless helper for intra-only image codecs (MJPEG, PNG, WebP, ...).
|
|
396
|
+
* Creates a fresh encoder, encodes the frame, flushes and frees everything in one call.
|
|
397
|
+
* The encoder adopts dimensions, pixel format and hardware context from the frame,
|
|
398
|
+
* so any frame size works without reconfiguration.
|
|
399
|
+
*
|
|
400
|
+
* @param encoderCodec - Encoder codec (name, ID, branded constant, or Codec)
|
|
401
|
+
*
|
|
402
|
+
* @param frame - Frame to encode
|
|
403
|
+
*
|
|
404
|
+
* @param options - Optional encoder configuration (e.g. `{ options: { q: 3 } }` for MJPEG quality)
|
|
405
|
+
*
|
|
406
|
+
* @returns Encoded image bytes
|
|
407
|
+
*
|
|
408
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
409
|
+
*
|
|
410
|
+
* @throws {Error} If the encoder produced no output
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```typescript
|
|
414
|
+
* const jpeg = await Encoder.encodeOne(FF_ENCODER_MJPEG, frame, { options: { q: 3 } });
|
|
415
|
+
* ```
|
|
416
|
+
*
|
|
417
|
+
* @see {@link EncoderPool} For reusing encoders across recurring resolutions
|
|
418
|
+
*/
|
|
419
|
+
static async encodeOne(encoderCodec, frame, options = {}) {
|
|
420
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
421
|
+
try {
|
|
422
|
+
const encoder = __addDisposableResource(env_1, await Encoder.create(encoderCodec, options), false);
|
|
423
|
+
const packets = [...(await encoder.encodeAll(frame)), ...(await encoder.encodeAll(null))];
|
|
424
|
+
try {
|
|
425
|
+
const data = packets[0]?.data;
|
|
426
|
+
if (!data) {
|
|
427
|
+
throw new Error(`Encoder '${encoder.getCodec().name}' produced no output for frame`);
|
|
428
|
+
}
|
|
429
|
+
return data;
|
|
430
|
+
}
|
|
431
|
+
finally {
|
|
432
|
+
for (const packet of packets) {
|
|
433
|
+
packet.free();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch (e_1) {
|
|
438
|
+
env_1.error = e_1;
|
|
439
|
+
env_1.hasError = true;
|
|
440
|
+
}
|
|
441
|
+
finally {
|
|
442
|
+
__disposeResources(env_1);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Encode a single frame into a self-contained image buffer synchronously.
|
|
447
|
+
* Synchronous version of encodeOne.
|
|
448
|
+
*
|
|
449
|
+
* One-shot, stateless helper for intra-only image codecs (MJPEG, PNG, WebP, ...).
|
|
450
|
+
* Creates a fresh encoder, encodes the frame, flushes and frees everything in one call.
|
|
451
|
+
* The encoder adopts dimensions, pixel format and hardware context from the frame,
|
|
452
|
+
* so any frame size works without reconfiguration.
|
|
453
|
+
*
|
|
454
|
+
* @param encoderCodec - Encoder codec (name, ID, branded constant, or Codec)
|
|
455
|
+
*
|
|
456
|
+
* @param frame - Frame to encode
|
|
457
|
+
*
|
|
458
|
+
* @param options - Optional encoder configuration (e.g. `{ options: { q: 3 } }` for MJPEG quality)
|
|
459
|
+
*
|
|
460
|
+
* @returns Encoded image bytes
|
|
461
|
+
*
|
|
462
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
463
|
+
*
|
|
464
|
+
* @throws {Error} If the encoder produced no output
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```typescript
|
|
468
|
+
* const jpeg = Encoder.encodeOneSync(FF_ENCODER_MJPEG, frame, { options: { q: 3 } });
|
|
469
|
+
* ```
|
|
470
|
+
*
|
|
471
|
+
* @see {@link encodeOne} For async version
|
|
472
|
+
* @see {@link EncoderPool} For reusing encoders across recurring resolutions
|
|
473
|
+
*/
|
|
474
|
+
static encodeOneSync(encoderCodec, frame, options = {}) {
|
|
475
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
476
|
+
try {
|
|
477
|
+
const encoder = __addDisposableResource(env_2, Encoder.createSync(encoderCodec, options), false);
|
|
478
|
+
const packets = [...encoder.encodeAllSync(frame), ...encoder.encodeAllSync(null)];
|
|
479
|
+
try {
|
|
480
|
+
const data = packets[0]?.data;
|
|
481
|
+
if (!data) {
|
|
482
|
+
throw new Error(`Encoder '${encoder.getCodec().name}' produced no output for frame`);
|
|
483
|
+
}
|
|
484
|
+
return data;
|
|
485
|
+
}
|
|
486
|
+
finally {
|
|
487
|
+
for (const packet of packets) {
|
|
488
|
+
packet.free();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch (e_2) {
|
|
493
|
+
env_2.error = e_2;
|
|
494
|
+
env_2.hasError = true;
|
|
495
|
+
}
|
|
496
|
+
finally {
|
|
497
|
+
__disposeResources(env_2);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
392
500
|
/**
|
|
393
501
|
* Check if encoder is open.
|
|
394
502
|
*
|
|
@@ -912,9 +1020,9 @@ export class Encoder {
|
|
|
912
1020
|
return;
|
|
913
1021
|
}
|
|
914
1022
|
for await (const frame_1 of frames) {
|
|
915
|
-
const
|
|
1023
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
916
1024
|
try {
|
|
917
|
-
const frame = __addDisposableResource(
|
|
1025
|
+
const frame = __addDisposableResource(env_3, frame_1, false);
|
|
918
1026
|
this.signal?.throwIfAborted();
|
|
919
1027
|
if (frame === null) {
|
|
920
1028
|
yield* finalize();
|
|
@@ -922,12 +1030,12 @@ export class Encoder {
|
|
|
922
1030
|
}
|
|
923
1031
|
yield* processFrame(frame);
|
|
924
1032
|
}
|
|
925
|
-
catch (
|
|
926
|
-
|
|
927
|
-
|
|
1033
|
+
catch (e_3) {
|
|
1034
|
+
env_3.error = e_3;
|
|
1035
|
+
env_3.hasError = true;
|
|
928
1036
|
}
|
|
929
1037
|
finally {
|
|
930
|
-
__disposeResources(
|
|
1038
|
+
__disposeResources(env_3);
|
|
931
1039
|
}
|
|
932
1040
|
}
|
|
933
1041
|
}
|
|
@@ -1023,9 +1131,9 @@ export class Encoder {
|
|
|
1023
1131
|
}
|
|
1024
1132
|
// Case 3: Iterable of frames
|
|
1025
1133
|
for (const frame_2 of frames) {
|
|
1026
|
-
const
|
|
1134
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1027
1135
|
try {
|
|
1028
|
-
const frame = __addDisposableResource(
|
|
1136
|
+
const frame = __addDisposableResource(env_4, frame_2, false);
|
|
1029
1137
|
// Check for EOF signal from upstream
|
|
1030
1138
|
if (frame === null) {
|
|
1031
1139
|
yield* finalize();
|
|
@@ -1033,12 +1141,12 @@ export class Encoder {
|
|
|
1033
1141
|
}
|
|
1034
1142
|
yield* processFrame(frame);
|
|
1035
1143
|
}
|
|
1036
|
-
catch (
|
|
1037
|
-
|
|
1038
|
-
|
|
1144
|
+
catch (e_4) {
|
|
1145
|
+
env_4.error = e_4;
|
|
1146
|
+
env_4.hasError = true;
|
|
1039
1147
|
}
|
|
1040
1148
|
finally {
|
|
1041
|
-
__disposeResources(
|
|
1149
|
+
__disposeResources(env_4);
|
|
1042
1150
|
}
|
|
1043
1151
|
}
|
|
1044
1152
|
// No fallback flush - only flush on explicit EOF
|
|
@@ -1081,17 +1189,17 @@ export class Encoder {
|
|
|
1081
1189
|
// For the final frame, we pad or truncate as needed
|
|
1082
1190
|
let _bufferedFrame;
|
|
1083
1191
|
while (!this.isClosed && (_bufferedFrame = await this.audioFrameBuffer.pull()) !== null) {
|
|
1084
|
-
const
|
|
1192
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
1085
1193
|
try {
|
|
1086
|
-
const bufferedFrame = __addDisposableResource(
|
|
1194
|
+
const bufferedFrame = __addDisposableResource(env_5, _bufferedFrame, false);
|
|
1087
1195
|
await this.codecContext.sendFrame(bufferedFrame);
|
|
1088
1196
|
}
|
|
1089
|
-
catch (
|
|
1090
|
-
|
|
1091
|
-
|
|
1197
|
+
catch (e_5) {
|
|
1198
|
+
env_5.error = e_5;
|
|
1199
|
+
env_5.hasError = true;
|
|
1092
1200
|
}
|
|
1093
1201
|
finally {
|
|
1094
|
-
__disposeResources(
|
|
1202
|
+
__disposeResources(env_5);
|
|
1095
1203
|
}
|
|
1096
1204
|
}
|
|
1097
1205
|
}
|
|
@@ -1141,17 +1249,17 @@ export class Encoder {
|
|
|
1141
1249
|
// For the final frame, we pad or truncate as needed
|
|
1142
1250
|
let _bufferedFrame;
|
|
1143
1251
|
while (!this.isClosed && (_bufferedFrame = this.audioFrameBuffer.pullSync()) !== null) {
|
|
1144
|
-
const
|
|
1252
|
+
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
1145
1253
|
try {
|
|
1146
|
-
const bufferedFrame = __addDisposableResource(
|
|
1254
|
+
const bufferedFrame = __addDisposableResource(env_6, _bufferedFrame, false);
|
|
1147
1255
|
this.codecContext.sendFrameSync(bufferedFrame);
|
|
1148
1256
|
}
|
|
1149
|
-
catch (
|
|
1150
|
-
|
|
1151
|
-
|
|
1257
|
+
catch (e_6) {
|
|
1258
|
+
env_6.error = e_6;
|
|
1259
|
+
env_6.hasError = true;
|
|
1152
1260
|
}
|
|
1153
1261
|
finally {
|
|
1154
|
-
__disposeResources(
|
|
1262
|
+
__disposeResources(env_6);
|
|
1155
1263
|
}
|
|
1156
1264
|
}
|
|
1157
1265
|
}
|
|
@@ -1294,19 +1402,19 @@ export class Encoder {
|
|
|
1294
1402
|
// Clear previous packet data
|
|
1295
1403
|
this.packet.unref();
|
|
1296
1404
|
if (this.audioFrameBuffer?.hasFrame()) {
|
|
1297
|
-
const
|
|
1405
|
+
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
1298
1406
|
try {
|
|
1299
|
-
const bufferedFrame = __addDisposableResource(
|
|
1407
|
+
const bufferedFrame = __addDisposableResource(env_7, await this.audioFrameBuffer.pull(), false);
|
|
1300
1408
|
if (bufferedFrame) {
|
|
1301
1409
|
await this.codecContext.sendFrame(bufferedFrame);
|
|
1302
1410
|
}
|
|
1303
1411
|
}
|
|
1304
|
-
catch (
|
|
1305
|
-
|
|
1306
|
-
|
|
1412
|
+
catch (e_7) {
|
|
1413
|
+
env_7.error = e_7;
|
|
1414
|
+
env_7.hasError = true;
|
|
1307
1415
|
}
|
|
1308
1416
|
finally {
|
|
1309
|
-
__disposeResources(
|
|
1417
|
+
__disposeResources(env_7);
|
|
1310
1418
|
}
|
|
1311
1419
|
}
|
|
1312
1420
|
const ret = await this.codecContext.receivePacket(this.packet);
|
|
@@ -1399,19 +1507,19 @@ export class Encoder {
|
|
|
1399
1507
|
// Clear previous packet data
|
|
1400
1508
|
this.packet.unref();
|
|
1401
1509
|
if (this.audioFrameBuffer?.hasFrame()) {
|
|
1402
|
-
const
|
|
1510
|
+
const env_8 = { stack: [], error: void 0, hasError: false };
|
|
1403
1511
|
try {
|
|
1404
|
-
const bufferedFrame = __addDisposableResource(
|
|
1512
|
+
const bufferedFrame = __addDisposableResource(env_8, this.audioFrameBuffer.pullSync(), false);
|
|
1405
1513
|
if (bufferedFrame) {
|
|
1406
1514
|
this.codecContext.sendFrameSync(bufferedFrame);
|
|
1407
1515
|
}
|
|
1408
1516
|
}
|
|
1409
|
-
catch (
|
|
1410
|
-
|
|
1411
|
-
|
|
1517
|
+
catch (e_8) {
|
|
1518
|
+
env_8.error = e_8;
|
|
1519
|
+
env_8.hasError = true;
|
|
1412
1520
|
}
|
|
1413
1521
|
finally {
|
|
1414
|
-
__disposeResources(
|
|
1522
|
+
__disposeResources(env_8);
|
|
1415
1523
|
}
|
|
1416
1524
|
}
|
|
1417
1525
|
const ret = this.codecContext.receivePacketSync(this.packet);
|
|
@@ -1541,9 +1649,9 @@ export class Encoder {
|
|
|
1541
1649
|
try {
|
|
1542
1650
|
// Outer loop - receive frames
|
|
1543
1651
|
while (!this.inputQueue.isClosed) {
|
|
1544
|
-
const
|
|
1652
|
+
const env_9 = { stack: [], error: void 0, hasError: false };
|
|
1545
1653
|
try {
|
|
1546
|
-
const frame = __addDisposableResource(
|
|
1654
|
+
const frame = __addDisposableResource(env_9, await this.inputQueue.receive(), false);
|
|
1547
1655
|
if (!frame)
|
|
1548
1656
|
break;
|
|
1549
1657
|
// Open encoder if not already done
|
|
@@ -1562,12 +1670,12 @@ export class Encoder {
|
|
|
1562
1670
|
await this.outputQueue.send(packet); // Only send actual packets
|
|
1563
1671
|
}
|
|
1564
1672
|
}
|
|
1565
|
-
catch (
|
|
1566
|
-
|
|
1567
|
-
|
|
1673
|
+
catch (e_9) {
|
|
1674
|
+
env_9.error = e_9;
|
|
1675
|
+
env_9.hasError = true;
|
|
1568
1676
|
}
|
|
1569
1677
|
finally {
|
|
1570
|
-
__disposeResources(
|
|
1678
|
+
__disposeResources(env_9);
|
|
1571
1679
|
}
|
|
1572
1680
|
}
|
|
1573
1681
|
// Flush encoder at end
|