node-av 5.2.4 → 6.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +15 -1
  2. package/dist/api/bitstream-filter.d.ts +110 -87
  3. package/dist/api/bitstream-filter.js +162 -102
  4. package/dist/api/bitstream-filter.js.map +1 -1
  5. package/dist/api/decoder.d.ts +87 -8
  6. package/dist/api/decoder.js +179 -4
  7. package/dist/api/decoder.js.map +1 -1
  8. package/dist/api/demuxer.d.ts +30 -24
  9. package/dist/api/demuxer.js +4 -5
  10. package/dist/api/demuxer.js.map +1 -1
  11. package/dist/api/device.js.map +1 -1
  12. package/dist/api/encoder-pool.d.ts +220 -0
  13. package/dist/api/encoder-pool.js +285 -0
  14. package/dist/api/encoder-pool.js.map +1 -0
  15. package/dist/api/encoder.d.ts +133 -7
  16. package/dist/api/encoder.js +392 -68
  17. package/dist/api/encoder.js.map +1 -1
  18. package/dist/api/filter-complex.d.ts +2 -1
  19. package/dist/api/filter-complex.js +11 -7
  20. package/dist/api/filter-complex.js.map +1 -1
  21. package/dist/api/filter-presets.d.ts +130 -654
  22. package/dist/api/filter-presets.js +180 -858
  23. package/dist/api/filter-presets.js.map +1 -1
  24. package/dist/api/filter.js +13 -8
  25. package/dist/api/filter.js.map +1 -1
  26. package/dist/api/fmp4-stream.js +1 -1
  27. package/dist/api/fmp4-stream.js.map +1 -1
  28. package/dist/api/index.d.ts +6 -6
  29. package/dist/api/index.js +8 -8
  30. package/dist/api/index.js.map +1 -1
  31. package/dist/api/muxer.d.ts +43 -15
  32. package/dist/api/muxer.js +79 -27
  33. package/dist/api/muxer.js.map +1 -1
  34. package/dist/api/pipeline.d.ts +50 -0
  35. package/dist/api/pipeline.js +138 -22
  36. package/dist/api/pipeline.js.map +1 -1
  37. package/dist/api/probe.d.ts +128 -0
  38. package/dist/api/probe.js +227 -0
  39. package/dist/api/probe.js.map +1 -0
  40. package/dist/api/rtp-stream.js +1 -1
  41. package/dist/api/rtp-stream.js.map +1 -1
  42. package/dist/api/scaler.d.ts +431 -0
  43. package/dist/api/scaler.js +620 -0
  44. package/dist/api/scaler.js.map +1 -0
  45. package/dist/api/utilities/async-queue.d.ts +27 -1
  46. package/dist/api/utilities/async-queue.js +38 -1
  47. package/dist/api/utilities/async-queue.js.map +1 -1
  48. package/dist/api/utilities/electron-shared-texture.d.ts +41 -1
  49. package/dist/api/utilities/electron-shared-texture.js +41 -4
  50. package/dist/api/utilities/electron-shared-texture.js.map +1 -1
  51. package/dist/api/utilities/index.d.ts +1 -1
  52. package/dist/api/utilities/index.js.map +1 -1
  53. package/dist/api/webrtc-stream.d.ts +0 -1
  54. package/dist/api/webrtc-stream.js +0 -1
  55. package/dist/api/webrtc-stream.js.map +1 -1
  56. package/dist/constants/bsf-options.d.ts +333 -0
  57. package/dist/constants/bsf-options.js +7 -0
  58. package/dist/constants/bsf-options.js.map +1 -0
  59. package/dist/constants/constants.d.ts +109 -0
  60. package/dist/constants/constants.js +110 -0
  61. package/dist/constants/constants.js.map +1 -1
  62. package/dist/constants/decoders.d.ts +636 -618
  63. package/dist/constants/decoders.js +1 -3
  64. package/dist/constants/decoders.js.map +1 -1
  65. package/dist/constants/encoders.d.ts +300 -282
  66. package/dist/constants/encoders.js +0 -2
  67. package/dist/constants/encoders.js.map +1 -1
  68. package/dist/constants/filter-options.d.ts +10915 -0
  69. package/dist/constants/filter-options.js +7 -0
  70. package/dist/constants/filter-options.js.map +1 -0
  71. package/dist/constants/format-options.d.ts +3056 -0
  72. package/dist/constants/format-options.js +7 -0
  73. package/dist/constants/format-options.js.map +1 -0
  74. package/dist/constants/formats.d.ts +18 -0
  75. package/dist/constants/formats.js +7 -0
  76. package/dist/constants/formats.js.map +1 -0
  77. package/dist/constants/index.d.ts +5 -0
  78. package/dist/constants/options.d.ts +4073 -0
  79. package/dist/constants/options.js +7 -0
  80. package/dist/constants/options.js.map +1 -0
  81. package/dist/lib/binding.d.ts +4 -1
  82. package/dist/lib/binding.js.map +1 -1
  83. package/dist/lib/codec.d.ts +36 -5
  84. package/dist/lib/codec.js +37 -4
  85. package/dist/lib/codec.js.map +1 -1
  86. package/dist/lib/dictionary.d.ts +1 -1
  87. package/dist/lib/dictionary.js.map +1 -1
  88. package/dist/lib/error.d.ts +69 -0
  89. package/dist/lib/error.js +92 -0
  90. package/dist/lib/error.js.map +1 -1
  91. package/dist/lib/frame.d.ts +46 -3
  92. package/dist/lib/frame.js +50 -3
  93. package/dist/lib/frame.js.map +1 -1
  94. package/dist/lib/index.d.ts +1 -1
  95. package/dist/lib/index.js.map +1 -1
  96. package/dist/lib/native-types.d.ts +68 -0
  97. package/dist/lib/packet.d.ts +17 -3
  98. package/dist/lib/packet.js +19 -3
  99. package/dist/lib/packet.js.map +1 -1
  100. package/dist/lib/utilities.d.ts +21 -0
  101. package/dist/lib/utilities.js +23 -0
  102. package/dist/lib/utilities.js.map +1 -1
  103. package/dist/webrtc/index.d.ts +3 -0
  104. package/dist/webrtc/index.js +7 -0
  105. package/dist/webrtc/index.js.map +1 -0
  106. package/package.json +32 -21
@@ -0,0 +1,620 @@
1
+ import { AV_CODEC_FLAG_QSCALE, AV_HWDEVICE_TYPE_OPENCL, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE, AV_PIX_FMT_NV12, AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_YUV420P, AVCOL_RANGE_JPEG, FF_QP2LAMBDA, SWS_BILINEAR, } from '../constants/constants.js';
2
+ import { FF_ENCODER_MJPEG, FF_ENCODER_PNG } from '../constants/encoders.js';
3
+ import { bindings } from '../lib/binding.js';
4
+ import { FFmpegError } from '../lib/error.js';
5
+ import { Frame } from '../lib/frame.js';
6
+ import { EncoderPool } from './encoder-pool.js';
7
+ import { FilterPreset } from './filter-presets.js';
8
+ import { FilterAPI } from './filter.js';
9
+ const FORMAT_TO_PIXFMT = {
10
+ rgb: AV_PIX_FMT_RGB24,
11
+ rgba: AV_PIX_FMT_RGBA,
12
+ gray: AV_PIX_FMT_GRAY8,
13
+ nv12: AV_PIX_FMT_NV12,
14
+ yuv420p: AV_PIX_FMT_YUV420P,
15
+ };
16
+ /**
17
+ * High-level image scaler, cropper, and pixel-format converter.
18
+ *
19
+ * Scales, crops, and converts decoded frames to raw pixel buffers or encoded
20
+ * JPEG/PNG images. Built for the detection/thumbnail/snapshot workload: one
21
+ * instance pools its swscale contexts, GPU filter graphs, and encoders, so
22
+ * reusing it for every frame and every crop avoids per-frame allocation.
23
+ * Software frames are scaled with swscale; hardware frames are cropped, scaled,
24
+ * and converted on the GPU with only the small result downloaded.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { Scaler } from 'node-av/api';
29
+ *
30
+ * using scaler = new Scaler({ hardware });
31
+ *
32
+ * // Scale + convert to RGB (on the GPU for hardware frames)
33
+ * const rgb = await scaler.toBuffer(frame, { resize: { width: 640, height: 360 }, format: 'rgb' });
34
+ *
35
+ * // Crop a detected region and encode a snapshot
36
+ * const jpeg = await scaler.toJpeg(frame, { crop: { x, y, width: w, height: h }, quality: 85 });
37
+ * ```
38
+ *
39
+ * @see {@link EncoderPool} For pooled image encoders
40
+ * @see {@link FilterAPI} For streaming filtergraphs
41
+ */
42
+ export class Scaler {
43
+ native;
44
+ hardware;
45
+ flags;
46
+ maxCacheSize;
47
+ graphs = new Map();
48
+ jpegPool;
49
+ pngPool;
50
+ downloadFrame;
51
+ disposed = false;
52
+ /**
53
+ * Create a new scaler.
54
+ *
55
+ * @param options - Scaler options
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * using scaler = new Scaler({ hardware: HardwareContext.auto() });
60
+ * ```
61
+ */
62
+ constructor(options = {}) {
63
+ this.flags = options.flags ?? SWS_BILINEAR;
64
+ this.hardware = options.hardware;
65
+ this.maxCacheSize = options.maxCacheSize ?? 16;
66
+ this.native = new bindings.Scaler(this.flags);
67
+ }
68
+ /**
69
+ * Scale, crop, and/or convert a frame and return the raw pixel data.
70
+ *
71
+ * The returned buffer is tightly packed (no row padding). For planar formats
72
+ * (`nv12`, `yuv420p`) planes are concatenated.
73
+ *
74
+ * @param frame - Source frame (software or hardware; hardware frames are processed on the GPU and downloaded)
75
+ *
76
+ * @param options - Crop, resize, and format options
77
+ *
78
+ * @returns Tightly packed pixel data
79
+ *
80
+ * @throws {FFmpegError} If scaling fails
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const gray = await scaler.toBuffer(frame, { resize: { width: 320, height: 180 }, format: 'gray' });
85
+ * ```
86
+ *
87
+ * @see {@link toBufferSync} For synchronous version
88
+ */
89
+ async toBuffer(frame, options = {}) {
90
+ if (this.disposed) {
91
+ throw new Error('Scaler has been disposed');
92
+ }
93
+ if (frame.isHwFrame()) {
94
+ // With a hardware context, crop/scale/convert on the GPU and download only
95
+ // the small result. Without one, fall back to downloading the full frame
96
+ // and processing it in software.
97
+ if (this.hardware) {
98
+ const out = await this.toFrame(frame, options.crop, options.resize, options.format ?? 'nv12');
99
+ try {
100
+ return out.toBuffer();
101
+ }
102
+ finally {
103
+ out.free();
104
+ }
105
+ }
106
+ return await this.native.process((await this.downloadToSoftware(frame)).getNative(), options);
107
+ }
108
+ return await this.native.process(frame.getNative(), options);
109
+ }
110
+ /**
111
+ * Scale, crop, and/or convert a frame and return the raw pixel data.
112
+ * Synchronous version of toBuffer.
113
+ *
114
+ * @param frame - Source frame (software or hardware; hardware frames are processed on the GPU and downloaded)
115
+ *
116
+ * @param options - Crop, resize, and format options
117
+ *
118
+ * @returns Tightly packed pixel data
119
+ *
120
+ * @throws {FFmpegError} If scaling fails
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const gray = scaler.toBufferSync(frame, { resize: { width: 320, height: 180 }, format: 'gray' });
125
+ * ```
126
+ *
127
+ * @see {@link toBuffer} For async version
128
+ */
129
+ toBufferSync(frame, options = {}) {
130
+ if (this.disposed) {
131
+ throw new Error('Scaler has been disposed');
132
+ }
133
+ if (frame.isHwFrame()) {
134
+ if (this.hardware) {
135
+ const out = this.toFrameSync(frame, options.crop, options.resize, options.format ?? 'nv12');
136
+ try {
137
+ return out.toBuffer();
138
+ }
139
+ finally {
140
+ out.free();
141
+ }
142
+ }
143
+ return this.native.processSync(this.downloadToSoftwareSync(frame).getNative(), options);
144
+ }
145
+ return this.native.processSync(frame.getNative(), options);
146
+ }
147
+ /**
148
+ * Scale/crop a frame and encode it to a JPEG image.
149
+ *
150
+ * The encoder is pooled per output size and reused across calls - no encoder is
151
+ * rebuilt per snapshot. Hardware frames are scaled on the GPU and downloaded;
152
+ * software frames are scaled with swscale.
153
+ *
154
+ * @param frame - Source frame (software or hardware)
155
+ *
156
+ * @param options - Crop, resize, and quality options
157
+ *
158
+ * @returns Encoded JPEG bytes
159
+ *
160
+ * @throws {FFmpegError} If scaling or encoding fails
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * const jpeg = await scaler.toJpeg(frame, { resize: { width: 1280, height: 720 }, quality: 85 });
165
+ * await writeFile('snapshot.jpg', jpeg);
166
+ * ```
167
+ *
168
+ * @see {@link toJpegSync} For synchronous version
169
+ */
170
+ async toJpeg(frame, options = {}) {
171
+ if (this.disposed) {
172
+ throw new Error('Scaler has been disposed');
173
+ }
174
+ const quality = options.quality ?? 90;
175
+ const src = frame.isHwFrame() && !this.hardware ? await this.downloadToSoftware(frame) : frame;
176
+ // mjpeg wants planar YUV; tag it full-range so colors are correct without the
177
+ // deprecated yuvj* formats.
178
+ const out = await this.toFrame(src, options.crop, options.resize, 'yuv420p');
179
+ try {
180
+ this.prepareJpegFrame(out, quality);
181
+ return await this.getJpegPool().encode(out);
182
+ }
183
+ finally {
184
+ out.free();
185
+ }
186
+ }
187
+ /**
188
+ * Scale/crop a frame and encode it to a JPEG image.
189
+ * Synchronous version of toJpeg.
190
+ *
191
+ * @param frame - Source frame (software or hardware)
192
+ *
193
+ * @param options - Crop, resize, and quality options
194
+ *
195
+ * @returns Encoded JPEG bytes
196
+ *
197
+ * @throws {FFmpegError} If scaling or encoding fails
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const jpeg = scaler.toJpegSync(frame, { resize: { width: 1280, height: 720 }, quality: 85 });
202
+ * ```
203
+ *
204
+ * @see {@link toJpeg} For async version
205
+ */
206
+ toJpegSync(frame, options = {}) {
207
+ if (this.disposed) {
208
+ throw new Error('Scaler has been disposed');
209
+ }
210
+ const quality = options.quality ?? 90;
211
+ const src = frame.isHwFrame() && !this.hardware ? this.downloadToSoftwareSync(frame) : frame;
212
+ const out = this.toFrameSync(src, options.crop, options.resize, 'yuv420p');
213
+ try {
214
+ this.prepareJpegFrame(out, quality);
215
+ return this.getJpegPool().encodeSync(out);
216
+ }
217
+ finally {
218
+ out.free();
219
+ }
220
+ }
221
+ /**
222
+ * Scale/crop a frame and encode it to a PNG image.
223
+ *
224
+ * The encoder is pooled per output size/format and reused across calls. Hardware
225
+ * frames are scaled on the GPU and downloaded; software frames use swscale.
226
+ *
227
+ * @param frame - Source frame (software or hardware)
228
+ *
229
+ * @param options - Crop, resize, and color-format options
230
+ *
231
+ * @returns Encoded PNG bytes
232
+ *
233
+ * @throws {FFmpegError} If scaling or encoding fails
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * const png = await scaler.toPng(frame, { crop: { x, y, width: w, height: h } });
238
+ * await writeFile('region.png', png);
239
+ * ```
240
+ *
241
+ * @see {@link toPngSync} For synchronous version
242
+ */
243
+ async toPng(frame, options = {}) {
244
+ if (this.disposed) {
245
+ throw new Error('Scaler has been disposed');
246
+ }
247
+ const format = options.format ?? 'rgb';
248
+ const src = frame.isHwFrame() && !this.hardware ? await this.downloadToSoftware(frame) : frame;
249
+ const out = await this.toFrame(src, options.crop, options.resize, format);
250
+ try {
251
+ return await this.getPngPool().encode(out);
252
+ }
253
+ finally {
254
+ out.free();
255
+ }
256
+ }
257
+ /**
258
+ * Scale/crop a frame and encode it to a PNG image.
259
+ * Synchronous version of toPng.
260
+ *
261
+ * @param frame - Source frame (software or hardware)
262
+ *
263
+ * @param options - Crop, resize, and color-format options
264
+ *
265
+ * @returns Encoded PNG bytes
266
+ *
267
+ * @throws {FFmpegError} If scaling or encoding fails
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const png = scaler.toPngSync(frame, { crop: { x, y, width: w, height: h } });
272
+ * ```
273
+ *
274
+ * @see {@link toPng} For async version
275
+ */
276
+ toPngSync(frame, options = {}) {
277
+ if (this.disposed) {
278
+ throw new Error('Scaler has been disposed');
279
+ }
280
+ const format = options.format ?? 'rgb';
281
+ const src = frame.isHwFrame() && !this.hardware ? this.downloadToSoftwareSync(frame) : frame;
282
+ const out = this.toFrameSync(src, options.crop, options.resize, format);
283
+ try {
284
+ return this.getPngPool().encodeSync(out);
285
+ }
286
+ finally {
287
+ out.free();
288
+ }
289
+ }
290
+ /**
291
+ * Release all pooled scaling contexts, graphs, and frames.
292
+ *
293
+ * @example
294
+ * ```typescript
295
+ * scaler.close();
296
+ * ```
297
+ */
298
+ close() {
299
+ if (this.disposed) {
300
+ return;
301
+ }
302
+ this.disposed = true;
303
+ this.native.close();
304
+ for (const entry of this.graphs.values()) {
305
+ entry.filter[Symbol.dispose]();
306
+ }
307
+ this.graphs.clear();
308
+ this.jpegPool?.close();
309
+ this.jpegPool = undefined;
310
+ this.pngPool?.close();
311
+ this.pngPool = undefined;
312
+ this.downloadFrame?.free();
313
+ this.downloadFrame = undefined;
314
+ }
315
+ /**
316
+ * Download a hardware frame to system memory (no hardware context available).
317
+ *
318
+ * Reuses a single target frame across calls. The frame's own hwframe context
319
+ * selects a supported software format.
320
+ *
321
+ * @param frame - Hardware source frame
322
+ *
323
+ * @returns Software frame
324
+ *
325
+ * @throws {FFmpegError} If the transfer fails
326
+ *
327
+ * @internal
328
+ */
329
+ async downloadToSoftware(frame) {
330
+ const dst = this.prepareDownloadTarget();
331
+ const ret = await frame.hwframeTransferData(dst, 0);
332
+ FFmpegError.throwIfError(ret, 'Failed to download hardware frame');
333
+ dst.copyProps(frame);
334
+ return dst;
335
+ }
336
+ /**
337
+ * Synchronous version of downloadToSoftware.
338
+ *
339
+ * @param frame - Hardware source frame
340
+ *
341
+ * @returns Software frame
342
+ *
343
+ * @throws {FFmpegError} If the transfer fails
344
+ *
345
+ * @internal
346
+ */
347
+ downloadToSoftwareSync(frame) {
348
+ const dst = this.prepareDownloadTarget();
349
+ const ret = frame.hwframeTransferDataSync(dst, 0);
350
+ FFmpegError.throwIfError(ret, 'Failed to download hardware frame');
351
+ dst.copyProps(frame);
352
+ return dst;
353
+ }
354
+ /**
355
+ * Lazily allocate and reset the reused download target frame.
356
+ *
357
+ * @returns The cleared download frame
358
+ *
359
+ * @internal
360
+ */
361
+ prepareDownloadTarget() {
362
+ if (!this.downloadFrame) {
363
+ this.downloadFrame = new Frame();
364
+ this.downloadFrame.alloc();
365
+ }
366
+ const dst = this.downloadFrame;
367
+ dst.unref();
368
+ dst.format = AV_PIX_FMT_NONE;
369
+ return dst;
370
+ }
371
+ /**
372
+ * Scale/crop/convert a frame through a cached filter graph and return the
373
+ * resulting frame (caller must free it).
374
+ *
375
+ * Software frames are scaled with swscale filters; hardware frames are
376
+ * cropped/scaled/converted on the GPU and downloaded. The crop region is
377
+ * reconfigured per frame via a runtime command on a graph shared per output
378
+ * size (except OpenCL, where the crop is baked in).
379
+ *
380
+ * @param frame - Source frame (software, or hardware with a hardware context)
381
+ *
382
+ * @param crop - Crop region, or undefined for the full frame
383
+ *
384
+ * @param resize - Target dimensions, or undefined to keep the cropped size
385
+ *
386
+ * @param format - Output pixel format
387
+ *
388
+ * @returns The scaled output frame (caller frees)
389
+ *
390
+ * @throws {Error} If the crop is out of bounds or the graph produces no frame
391
+ *
392
+ * @internal
393
+ */
394
+ async toFrame(frame, crop, resize, format) {
395
+ const entry = this.resolveGraph(frame, crop, resize, format);
396
+ await entry.filter.process(frame);
397
+ const out = await entry.filter.receive();
398
+ if (out == null) {
399
+ throw new Error('Scaler: filter produced no frame');
400
+ }
401
+ return out;
402
+ }
403
+ /**
404
+ * Synchronous version of toFrame.
405
+ *
406
+ * @param frame - Source frame (software, or hardware with a hardware context)
407
+ *
408
+ * @param crop - Crop region, or undefined for the full frame
409
+ *
410
+ * @param resize - Target dimensions, or undefined to keep the cropped size
411
+ *
412
+ * @param format - Output pixel format
413
+ *
414
+ * @returns The scaled output frame (caller frees)
415
+ *
416
+ * @internal
417
+ */
418
+ toFrameSync(frame, crop, resize, format) {
419
+ const entry = this.resolveGraph(frame, crop, resize, format);
420
+ entry.filter.processSync(frame);
421
+ const out = entry.filter.receiveSync();
422
+ if (out == null) {
423
+ throw new Error('Scaler: filter produced no frame');
424
+ }
425
+ return out;
426
+ }
427
+ /**
428
+ * Resolve (and, on demand, build) the cached filter graph for a frame, applying
429
+ * the crop reconfiguration. Shared by the sync and async scaling paths; all of
430
+ * this work (parse, config, sendCommand) is synchronous.
431
+ *
432
+ * @param frame - Source frame (software, or hardware with a hardware context)
433
+ *
434
+ * @param crop - Crop region, or undefined for the full frame
435
+ *
436
+ * @param resize - Target dimensions, or undefined to keep the cropped size
437
+ *
438
+ * @param format - Output pixel format
439
+ *
440
+ * @returns The cached graph entry, ready to process the frame
441
+ *
442
+ * @throws {Error} If the crop is out of bounds
443
+ *
444
+ * @internal
445
+ */
446
+ resolveGraph(frame, crop, resize, format) {
447
+ const isHw = frame.isHwFrame();
448
+ if (isHw && !this.hardware) {
449
+ throw new Error('Scaler received a hardware frame but was created without a HardwareContext');
450
+ }
451
+ const cropW = crop?.width ?? frame.width;
452
+ const cropH = crop?.height ?? frame.height;
453
+ const cropX = crop?.x ?? 0;
454
+ const cropY = crop?.y ?? 0;
455
+ if (cropX < 0 || cropY < 0 || cropW <= 0 || cropH <= 0 || cropX + cropW > frame.width || cropY + cropH > frame.height) {
456
+ throw new Error(`Scaler: crop region {x:${cropX}, y:${cropY}, width:${cropW}, height:${cropH}} is out of bounds for ${frame.width}x${frame.height} frame`);
457
+ }
458
+ const outW = resize?.width ?? cropW;
459
+ const outH = resize?.height ?? cropH;
460
+ // The standard `crop` filter is runtime-commandable for software frames and on
461
+ // every hardware backend except OpenCL (where crop is folded into scale_opencl).
462
+ // When commandable we share one graph per output size and re-aim the crop per
463
+ // frame; otherwise the crop is baked in and the key must include it.
464
+ const commandable = !isHw || this.hardware.deviceType !== AV_HWDEVICE_TYPE_OPENCL;
465
+ const prefix = isHw ? 'hw' : 'sw';
466
+ const key = commandable ? `${prefix}_${outW}x${outH}_${format}` : `${prefix}_${cropX},${cropY},${cropW},${cropH}->${outW}x${outH}_${format}`;
467
+ const cropRegion = { x: cropX, y: cropY, width: cropW, height: cropH };
468
+ let entry = this.graphs.get(key);
469
+ if (!entry) {
470
+ // Bake the first crop in as the graph default: the graph initializes lazily
471
+ // on the first processed frame, before any command can be sent, so the
472
+ // initial crop must already be in place. Later crops use sendCommand.
473
+ const filter = FilterAPI.create(this.buildGraph(isHw, outW, outH, format, cropRegion, commandable), isHw ? { hardware: this.hardware } : {});
474
+ entry = { filter, lastCrop: commandable ? { x: cropX, y: cropY, w: cropW, h: cropH } : null };
475
+ this.cacheSet(key, entry);
476
+ }
477
+ else {
478
+ // Bump LRU recency.
479
+ this.graphs.delete(key);
480
+ this.graphs.set(key, entry);
481
+ }
482
+ if (commandable) {
483
+ // Reconfigure the crop only when it actually changed - identical crops reuse
484
+ // the graph untouched (no config_input/config_output re-run).
485
+ const c = entry.lastCrop;
486
+ if (c?.x !== cropX || c?.y !== cropY || c?.w !== cropW || c?.h !== cropH) {
487
+ // w/h are validated against the input dims (above); x/y are clamped per
488
+ // frame by the crop filter, so this order never produces a transient error.
489
+ entry.filter.sendCommand('crop@sc', 'w', String(cropW));
490
+ entry.filter.sendCommand('crop@sc', 'h', String(cropH));
491
+ entry.filter.sendCommand('crop@sc', 'x', String(cropX));
492
+ entry.filter.sendCommand('crop@sc', 'y', String(cropY));
493
+ entry.lastCrop = { x: cropX, y: cropY, w: cropW, h: cropH };
494
+ }
495
+ }
496
+ return entry;
497
+ }
498
+ /**
499
+ * Tag a frame for MJPEG encoding: full color range and a per-frame quality.
500
+ *
501
+ * MJPEG quality is carried on the frame (the encoder pool sets the QSCALE flag),
502
+ * so one pooled encoder serves every quality for a given output size.
503
+ *
504
+ * @param frame - Frame about to be JPEG-encoded
505
+ *
506
+ * @param quality - Quality from 1 (worst) to 100 (best)
507
+ *
508
+ * @internal
509
+ */
510
+ prepareJpegFrame(frame, quality) {
511
+ frame.colorRange = AVCOL_RANGE_JPEG;
512
+ const q = Math.max(1, Math.min(100, quality));
513
+ const qscale = Math.max(2, Math.min(31, Math.round(2 + ((100 - q) / 99) * 29)));
514
+ frame.quality = qscale * FF_QP2LAMBDA;
515
+ }
516
+ /**
517
+ * The lazily created MJPEG encoder pool (QSCALE flag set for per-frame quality).
518
+ *
519
+ * @returns The JPEG encoder pool
520
+ *
521
+ * @internal
522
+ */
523
+ getJpegPool() {
524
+ this.jpegPool ??= new EncoderPool(FF_ENCODER_MJPEG, { maxSize: this.maxCacheSize, flags: AV_CODEC_FLAG_QSCALE });
525
+ return this.jpegPool;
526
+ }
527
+ /**
528
+ * The lazily created PNG encoder pool.
529
+ *
530
+ * @returns The PNG encoder pool
531
+ *
532
+ * @internal
533
+ */
534
+ getPngPool() {
535
+ this.pngPool ??= new EncoderPool(FF_ENCODER_PNG, { maxSize: this.maxCacheSize });
536
+ return this.pngPool;
537
+ }
538
+ /**
539
+ * Build a crop+scale+convert graph.
540
+ *
541
+ * For software frames this is `crop,scale,format`. For hardware frames the crop
542
+ * filter sets crop metadata that the hardware scaler (scale_vt/scale_cuda/
543
+ * scale_vaapi) applies on the GPU; the result is kept in NV12, downloaded, and
544
+ * converted on the CPU. When `commandable` the crop is a labeled instance
545
+ * (`crop@sc`) whose initial region is `crop` but which is re-aimed per frame via
546
+ * `sendCommand`. Otherwise (OpenCL) the crop is fixed in the graph.
547
+ *
548
+ * @param isHw - Whether the input is a hardware frame
549
+ *
550
+ * @param outW - Output width
551
+ *
552
+ * @param outH - Output height
553
+ *
554
+ * @param format - Output pixel format
555
+ *
556
+ * @param crop - Initial crop region
557
+ *
558
+ * @param commandable - Whether the crop can be reconfigured at runtime
559
+ *
560
+ * @returns Filtergraph description
561
+ *
562
+ * @internal
563
+ */
564
+ buildGraph(isHw, outW, outH, format, crop, commandable) {
565
+ let chain = FilterPreset.chain(isHw ? this.hardware : undefined);
566
+ if (commandable) {
567
+ // Labeled crop so sendCommand('crop@sc', ...) can re-aim it per frame.
568
+ chain = chain.custom(`crop@sc=${crop.width}:${crop.height}:${crop.x}:${crop.y}`);
569
+ }
570
+ else {
571
+ chain = chain.crop(crop.width, crop.height, crop.x, crop.y);
572
+ }
573
+ chain = chain.scale(outW, outH);
574
+ if (isHw) {
575
+ // Keep the GPU result in NV12, download the small frame, then convert on CPU.
576
+ chain = chain.scaleFormat(AV_PIX_FMT_NV12).hwdownload().format(AV_PIX_FMT_NV12);
577
+ if (format !== 'nv12') {
578
+ chain = chain.format(FORMAT_TO_PIXFMT[format]);
579
+ }
580
+ }
581
+ else {
582
+ chain = chain.format(FORMAT_TO_PIXFMT[format]);
583
+ }
584
+ return chain.build();
585
+ }
586
+ /**
587
+ * Insert a graph, evicting the least-recently-used entry if over capacity.
588
+ *
589
+ * @param key - Operation-configuration key
590
+ *
591
+ * @param entry - Graph entry to cache
592
+ *
593
+ * @internal
594
+ */
595
+ cacheSet(key, entry) {
596
+ this.graphs.set(key, entry);
597
+ while (this.graphs.size > this.maxCacheSize) {
598
+ const oldest = this.graphs.keys().next().value;
599
+ if (oldest === undefined) {
600
+ break;
601
+ }
602
+ this.graphs.get(oldest)?.filter[Symbol.dispose]();
603
+ this.graphs.delete(oldest);
604
+ }
605
+ }
606
+ /**
607
+ * Dispose of the scaler (for `using` statements).
608
+ *
609
+ * @example
610
+ * ```typescript
611
+ * using scaler = new Scaler();
612
+ * // ... use scaler ...
613
+ * // automatically disposed at end of scope
614
+ * ```
615
+ */
616
+ [Symbol.dispose]() {
617
+ this.close();
618
+ }
619
+ }
620
+ //# sourceMappingURL=scaler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaler.js","sourceRoot":"","sources":["../../src/api/scaler.ts"],"names":[],"mappings":"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"}
@@ -25,12 +25,17 @@ export declare class AsyncQueue<T> {
25
25
  private maxSize;
26
26
  private closed;
27
27
  private error;
28
+ private disposeItem?;
28
29
  /**
29
30
  * Creates a new AsyncQueue.
30
31
  *
31
32
  * @param maxSize Maximum number of items in queue before send() blocks
33
+ *
34
+ * @param disposeItem Optional disposer for items still buffered at teardown
35
+ * (see {@link clear}). Pass `(item) => item.free()` for queues of owned
36
+ * native resources (Frame/Packet) so aborted pipelines don't leak them.
32
37
  */
33
- constructor(maxSize: number);
38
+ constructor(maxSize: number, disposeItem?: (item: T) => void);
34
39
  /**
35
40
  * Current number of items in the queue.
36
41
  */
@@ -117,4 +122,25 @@ export declare class AsyncQueue<T> {
117
122
  * ```
118
123
  */
119
124
  closeWithError(error: Error): void;
125
+ /**
126
+ * Drop and dispose any items still buffered in the queue.
127
+ *
128
+ * Call during final teardown (a component's `close()`, after the worker and
129
+ * consumer tasks have settled) to deterministically free items that were
130
+ * produced but never consumed - e.g. when a pipeline is aborted before
131
+ * draining. Without this they are only reclaimed by GC, which for hardware
132
+ * frames pins GPU/hwframe memory in the meantime.
133
+ *
134
+ * Items are owned by the queue (the consumer frees what it receives), so
135
+ * disposing buffered items here cannot double-free. Do NOT call this while a
136
+ * consumer may still drain the queue after `close()` (the flush path relies on
137
+ * that) - only at final teardown.
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * this.outputQueue.close();
142
+ * this.outputQueue.clear(); // free anything left behind on abort
143
+ * ```
144
+ */
145
+ clear(): void;
120
146
  }