node-av 5.2.4 → 6.0.0-beta.11
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 +15 -1
- package/dist/api/bitstream-filter.d.ts +110 -87
- package/dist/api/bitstream-filter.js +161 -103
- package/dist/api/bitstream-filter.js.map +1 -1
- package/dist/api/decoder.d.ts +177 -15
- package/dist/api/decoder.js +335 -28
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/demuxer.d.ts +30 -24
- package/dist/api/demuxer.js +4 -5
- package/dist/api/demuxer.js.map +1 -1
- package/dist/api/device.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 +194 -7
- package/dist/api/encoder.js +431 -71
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-complex.d.ts +2 -1
- package/dist/api/filter-complex.js +11 -7
- package/dist/api/filter-complex.js.map +1 -1
- package/dist/api/filter-presets.d.ts +130 -654
- package/dist/api/filter-presets.js +180 -858
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.js +12 -9
- package/dist/api/filter.js.map +1 -1
- package/dist/api/fmp4-stream.js +1 -1
- package/dist/api/fmp4-stream.js.map +1 -1
- package/dist/api/index.d.ts +6 -6
- package/dist/api/index.js +8 -8
- package/dist/api/index.js.map +1 -1
- package/dist/api/muxer.d.ts +43 -15
- package/dist/api/muxer.js +79 -27
- package/dist/api/muxer.js.map +1 -1
- package/dist/api/pipeline.d.ts +50 -0
- package/dist/api/pipeline.js +138 -22
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/probe.d.ts +128 -0
- package/dist/api/probe.js +227 -0
- package/dist/api/probe.js.map +1 -0
- package/dist/api/rtp-stream.d.ts +14 -11
- package/dist/api/rtp-stream.js +23 -48
- package/dist/api/rtp-stream.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/async-queue.d.ts +27 -1
- package/dist/api/utilities/async-queue.js +38 -3
- package/dist/api/utilities/async-queue.js.map +1 -1
- package/dist/api/utilities/codec-format.d.ts +87 -0
- package/dist/api/utilities/codec-format.js +117 -0
- package/dist/api/utilities/codec-format.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/api/utilities/index.d.ts +2 -1
- package/dist/api/utilities/index.js +2 -0
- package/dist/api/utilities/index.js.map +1 -1
- package/dist/api/webrtc-stream.d.ts +0 -1
- package/dist/api/webrtc-stream.js +0 -1
- package/dist/api/webrtc-stream.js.map +1 -1
- package/dist/constants/bsf-options.d.ts +333 -0
- package/dist/constants/bsf-options.js +7 -0
- package/dist/constants/bsf-options.js.map +1 -0
- package/dist/constants/constants.d.ts +109 -0
- package/dist/constants/constants.js +110 -0
- package/dist/constants/constants.js.map +1 -1
- package/dist/constants/decoders.d.ts +636 -618
- package/dist/constants/decoders.js +1 -3
- package/dist/constants/decoders.js.map +1 -1
- package/dist/constants/encoders.d.ts +300 -282
- package/dist/constants/encoders.js +0 -2
- package/dist/constants/encoders.js.map +1 -1
- package/dist/constants/filter-options.d.ts +10915 -0
- package/dist/constants/filter-options.js +7 -0
- package/dist/constants/filter-options.js.map +1 -0
- package/dist/constants/format-options.d.ts +3056 -0
- package/dist/constants/format-options.js +7 -0
- package/dist/constants/format-options.js.map +1 -0
- package/dist/constants/formats.d.ts +18 -0
- package/dist/constants/formats.js +7 -0
- package/dist/constants/formats.js.map +1 -0
- package/dist/constants/index.d.ts +5 -0
- package/dist/constants/options.d.ts +4073 -0
- package/dist/constants/options.js +7 -0
- package/dist/constants/options.js.map +1 -0
- package/dist/lib/binding.d.ts +5 -1
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/codec.d.ts +36 -5
- package/dist/lib/codec.js +37 -4
- package/dist/lib/codec.js.map +1 -1
- package/dist/lib/dictionary.d.ts +1 -1
- package/dist/lib/dictionary.js.map +1 -1
- package/dist/lib/error.d.ts +69 -0
- package/dist/lib/error.js +92 -0
- package/dist/lib/error.js.map +1 -1
- package/dist/lib/frame.d.ts +55 -3
- package/dist/lib/frame.js +59 -3
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/native-types.d.ts +68 -0
- package/dist/lib/packet.d.ts +22 -3
- package/dist/lib/packet.js +24 -3
- package/dist/lib/packet.js.map +1 -1
- package/dist/lib/utilities.d.ts +45 -0
- package/dist/lib/utilities.js +49 -0
- package/dist/lib/utilities.js.map +1 -1
- package/dist/webrtc/index.d.ts +3 -0
- package/dist/webrtc/index.js +7 -0
- package/dist/webrtc/index.js.map +1 -0
- package/package.json +34 -23
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { AVCodecFlag, AVCodecID, FFEncoderCodec } from '../constants/index.js';
|
|
2
|
+
import type { Codec } from '../lib/codec.js';
|
|
3
|
+
import type { Frame } from '../lib/frame.js';
|
|
4
|
+
import type { EncoderOptions } from './encoder.js';
|
|
5
|
+
/**
|
|
6
|
+
* Options for encoder pool creation.
|
|
7
|
+
*
|
|
8
|
+
* Extends {@link EncoderOptions} with the pool size limit.
|
|
9
|
+
*/
|
|
10
|
+
export type EncoderPoolOptions<C = unknown> = EncoderOptions<C> & {
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of encoders kept alive at once.
|
|
13
|
+
*
|
|
14
|
+
* When exceeded, the least-recently-used encoder is closed. In the worst case
|
|
15
|
+
* (every frame a unique size) the pool degrades to per-frame open/close, never leaking.
|
|
16
|
+
*
|
|
17
|
+
* @default 8
|
|
18
|
+
*/
|
|
19
|
+
maxSize?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Codec flags applied to every pooled encoder before its first frame.
|
|
22
|
+
*
|
|
23
|
+
* Needed for flags that change encode behaviour, e.g. `AV_CODEC_FLAG_QSCALE` so
|
|
24
|
+
* MJPEG honours per-frame `frame.quality`.
|
|
25
|
+
*/
|
|
26
|
+
flags?: AVCodecFlag | AVCodecFlag[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Bounded LRU pool of image encoders keyed by resolution and pixel format.
|
|
30
|
+
*
|
|
31
|
+
* A single encoder cannot change dimensions after opening, so encoding frames of
|
|
32
|
+
* varying sizes needs one encoder per resolution. This pool routes each frame to
|
|
33
|
+
* the encoder matching its `width x height x pixelFormat`, creating one on demand
|
|
34
|
+
* and reusing it afterwards. Each frame is given a monotonic PTS internally, so
|
|
35
|
+
* callers can feed independent snapshots without managing timestamps.
|
|
36
|
+
*
|
|
37
|
+
* Pooled encoders are never flushed between frames, so this only suits intra-only
|
|
38
|
+
* image codecs (MJPEG, PNG, WebP, ...). Hardware frames work as-is: the encoder
|
|
39
|
+
* adopts the frame's hardware context. For a single one-off conversion use
|
|
40
|
+
* {@link Encoder.encodeOne} instead.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { EncoderPool } from 'node-av/api';
|
|
45
|
+
* import { FF_ENCODER_MJPEG } from 'node-av/constants';
|
|
46
|
+
*
|
|
47
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG);
|
|
48
|
+
* const a = await pool.encode(frame1920); // opens + caches
|
|
49
|
+
* const b = await pool.encode(frame640); // opens + caches
|
|
50
|
+
* const c = await pool.encode(frame1920); // reuses, no open
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // MJPEG quality: set the QSCALE flag on the pool, then carry the quality on
|
|
56
|
+
* // each frame (qscale 2 = best, 31 = worst). The `q` private option has no
|
|
57
|
+
* // effect on MJPEG.
|
|
58
|
+
* import { AV_CODEC_FLAG_QSCALE, FF_QP2LAMBDA } from 'node-av/constants';
|
|
59
|
+
*
|
|
60
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG, { maxSize: 4, flags: AV_CODEC_FLAG_QSCALE });
|
|
61
|
+
* frame.quality = 3 * FF_QP2LAMBDA;
|
|
62
|
+
* const jpeg = await pool.encode(frame);
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @see {@link Encoder.encodeOne} For a one-shot, stateless single-frame encode
|
|
66
|
+
*/
|
|
67
|
+
export declare class EncoderPool<const C extends FFEncoderCodec | AVCodecID | Codec> implements Disposable {
|
|
68
|
+
private readonly encoderCodec;
|
|
69
|
+
private readonly options;
|
|
70
|
+
private readonly maxSize;
|
|
71
|
+
private readonly flags;
|
|
72
|
+
private readonly encoders;
|
|
73
|
+
/**
|
|
74
|
+
* Create a new encoder pool.
|
|
75
|
+
*
|
|
76
|
+
* @param encoderCodec - Encoder codec applied to every pooled encoder
|
|
77
|
+
*
|
|
78
|
+
* @param options - Encoder options forwarded to each encoder, plus the optional `maxSize` limit
|
|
79
|
+
*
|
|
80
|
+
* @throws {Error} If maxSize is less than 1
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG, { maxSize: 4, flags: AV_CODEC_FLAG_QSCALE });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
constructor(encoderCodec: C, options?: EncoderPoolOptions<C>);
|
|
88
|
+
/**
|
|
89
|
+
* Number of encoders currently held alive.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* console.log(`Pool holds ${pool.size} encoders`);
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
get size(): number;
|
|
97
|
+
/**
|
|
98
|
+
* Encode a frame into a self-contained image buffer.
|
|
99
|
+
*
|
|
100
|
+
* Routes the frame to the encoder matching its dimensions and pixel format,
|
|
101
|
+
* creating and caching one if necessary.
|
|
102
|
+
*
|
|
103
|
+
* @param frame - Frame to encode
|
|
104
|
+
*
|
|
105
|
+
* @returns Encoded image bytes
|
|
106
|
+
*
|
|
107
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
108
|
+
*
|
|
109
|
+
* @throws {Error} If the encoder produced no output
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const jpeg = await pool.encode(frame);
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @see {@link encodeSync} For synchronous version
|
|
117
|
+
*/
|
|
118
|
+
encode(frame: Frame): Promise<Buffer>;
|
|
119
|
+
/**
|
|
120
|
+
* Encode a frame into a self-contained image buffer synchronously.
|
|
121
|
+
* Synchronous version of encode.
|
|
122
|
+
*
|
|
123
|
+
* @param frame - Frame to encode
|
|
124
|
+
*
|
|
125
|
+
* @returns Encoded image bytes
|
|
126
|
+
*
|
|
127
|
+
* @throws {FFmpegError} If the encoder is not found or encoding fails
|
|
128
|
+
*
|
|
129
|
+
* @throws {Error} If the encoder produced no output
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const jpeg = pool.encodeSync(frame);
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @see {@link encode} For asynchronous version
|
|
137
|
+
*/
|
|
138
|
+
encodeSync(frame: Frame): Buffer;
|
|
139
|
+
/**
|
|
140
|
+
* Close all pooled encoders and clear the pool.
|
|
141
|
+
*
|
|
142
|
+
* Automatically called by Symbol.dispose.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* pool.close();
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* @see {@link Symbol.dispose} For automatic cleanup
|
|
150
|
+
*/
|
|
151
|
+
close(): void;
|
|
152
|
+
/**
|
|
153
|
+
* Build the cache key from frame dimensions and pixel format.
|
|
154
|
+
*
|
|
155
|
+
* Pixel format is part of the key because the encoder is opened with the frame's
|
|
156
|
+
* format; a cached encoder for the same size but a different format would be invalid.
|
|
157
|
+
*
|
|
158
|
+
* @param frame - Frame to derive the key from
|
|
159
|
+
*
|
|
160
|
+
* @returns Cache key `width x height x pixelFormat`
|
|
161
|
+
*
|
|
162
|
+
* @internal
|
|
163
|
+
*/
|
|
164
|
+
private keyFor;
|
|
165
|
+
/**
|
|
166
|
+
* Look up an encoder and mark it as most-recently-used.
|
|
167
|
+
*
|
|
168
|
+
* @param key - Cache key to look up
|
|
169
|
+
*
|
|
170
|
+
* @returns Pooled encoder, or undefined if not cached
|
|
171
|
+
*
|
|
172
|
+
* @internal
|
|
173
|
+
*/
|
|
174
|
+
private touch;
|
|
175
|
+
/**
|
|
176
|
+
* Insert a freshly created encoder, evicting the least-recently-used one if full.
|
|
177
|
+
*
|
|
178
|
+
* @param key - Cache key for the encoder
|
|
179
|
+
*
|
|
180
|
+
* @param encoder - Freshly created encoder to cache
|
|
181
|
+
*
|
|
182
|
+
* @returns The pooled encoder entry
|
|
183
|
+
*
|
|
184
|
+
* @internal
|
|
185
|
+
*/
|
|
186
|
+
private set;
|
|
187
|
+
/**
|
|
188
|
+
* Extract the encoded bytes from the produced packets and free them.
|
|
189
|
+
*
|
|
190
|
+
* packet.data returns a JS-owned copy, so the buffer stays valid after freeing.
|
|
191
|
+
*
|
|
192
|
+
* @param packets - Packets produced by the encoder
|
|
193
|
+
*
|
|
194
|
+
* @param encoder - Encoder that produced the packets (for error messages)
|
|
195
|
+
*
|
|
196
|
+
* @returns Encoded image bytes
|
|
197
|
+
*
|
|
198
|
+
* @throws {Error} If no packet was produced
|
|
199
|
+
*
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
private extract;
|
|
203
|
+
/**
|
|
204
|
+
* Dispose of the pool.
|
|
205
|
+
*
|
|
206
|
+
* Implements Disposable interface for automatic cleanup.
|
|
207
|
+
* Equivalent to calling close().
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* {
|
|
212
|
+
* using pool = new EncoderPool(FF_ENCODER_MJPEG, { maxSize: 4 });
|
|
213
|
+
* // Encode frames...
|
|
214
|
+
* } // All encoders automatically closed
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* @see {@link close} For manual cleanup
|
|
218
|
+
*/
|
|
219
|
+
[Symbol.dispose](): void;
|
|
220
|
+
}
|
|
@@ -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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"}
|