node-av-lgpl 0.0.0 → 7.1.0-beta.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/LICENSE.md +22 -0
- package/README.md +711 -0
- package/dist/api/audio-frame-buffer.d.ts +636 -0
- package/dist/api/audio-frame-buffer.js +1027 -0
- package/dist/api/audio-frame-buffer.js.map +1 -0
- package/dist/api/bitstream-filter.d.ts +938 -0
- package/dist/api/bitstream-filter.js +1427 -0
- package/dist/api/bitstream-filter.js.map +1 -0
- package/dist/api/constants.d.ts +44 -0
- package/dist/api/constants.js +45 -0
- package/dist/api/constants.js.map +1 -0
- package/dist/api/data/test_av1.ivf +0 -0
- package/dist/api/data/test_h264.h264 +0 -0
- package/dist/api/data/test_hevc.h265 +0 -0
- package/dist/api/data/test_mjpeg.mjpeg +0 -0
- package/dist/api/data/test_vp8.ivf +0 -0
- package/dist/api/data/test_vp9.ivf +0 -0
- package/dist/api/decoder.d.ts +1413 -0
- package/dist/api/decoder.js +2367 -0
- package/dist/api/decoder.js.map +1 -0
- package/dist/api/demuxer.d.ts +1295 -0
- package/dist/api/demuxer.js +2344 -0
- package/dist/api/demuxer.js.map +1 -0
- package/dist/api/device.d.ts +589 -0
- package/dist/api/device.js +964 -0
- package/dist/api/device.js.map +1 -0
- package/dist/api/encoder-pool.d.ts +262 -0
- package/dist/api/encoder-pool.js +397 -0
- package/dist/api/encoder-pool.js.map +1 -0
- package/dist/api/encoder.d.ts +1509 -0
- package/dist/api/encoder.js +2619 -0
- package/dist/api/encoder.js.map +1 -0
- package/dist/api/filter-complex.d.ts +828 -0
- package/dist/api/filter-complex.js +1661 -0
- package/dist/api/filter-complex.js.map +1 -0
- package/dist/api/filter-presets.d.ts +800 -0
- package/dist/api/filter-presets.js +1554 -0
- package/dist/api/filter-presets.js.map +1 -0
- package/dist/api/filter.d.ts +1339 -0
- package/dist/api/filter.js +2075 -0
- package/dist/api/filter.js.map +1 -0
- package/dist/api/fmp4-stream.d.ts +797 -0
- package/dist/api/fmp4-stream.js +1428 -0
- package/dist/api/fmp4-stream.js.map +1 -0
- package/dist/api/hardware.d.ts +677 -0
- package/dist/api/hardware.js +1300 -0
- package/dist/api/hardware.js.map +1 -0
- package/dist/api/index.d.ts +21 -0
- package/dist/api/index.js +38 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/io-stream.d.ts +308 -0
- package/dist/api/io-stream.js +358 -0
- package/dist/api/io-stream.js.map +1 -0
- package/dist/api/muxer.d.ts +1311 -0
- package/dist/api/muxer.js +2672 -0
- package/dist/api/muxer.js.map +1 -0
- package/dist/api/pipeline.d.ts +801 -0
- package/dist/api/pipeline.js +1344 -0
- package/dist/api/pipeline.js.map +1 -0
- 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 +497 -0
- package/dist/api/rtp-stream.js +1131 -0
- package/dist/api/rtp-stream.js.map +1 -0
- package/dist/api/scaler.d.ts +561 -0
- package/dist/api/scaler.js +918 -0
- package/dist/api/scaler.js.map +1 -0
- package/dist/api/types.d.ts +17 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/utilities/async-queue.d.ts +149 -0
- package/dist/api/utilities/async-queue.js +254 -0
- package/dist/api/utilities/async-queue.js.map +1 -0
- package/dist/api/utilities/audio-sample.d.ts +117 -0
- package/dist/api/utilities/audio-sample.js +112 -0
- package/dist/api/utilities/audio-sample.js.map +1 -0
- package/dist/api/utilities/audio-transcode.d.ts +20 -0
- package/dist/api/utilities/audio-transcode.js +27 -0
- package/dist/api/utilities/audio-transcode.js.map +1 -0
- package/dist/api/utilities/channel-layout.d.ts +76 -0
- package/dist/api/utilities/channel-layout.js +80 -0
- package/dist/api/utilities/channel-layout.js.map +1 -0
- 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/context-options.d.ts +25 -0
- package/dist/api/utilities/context-options.js +23 -0
- package/dist/api/utilities/context-options.js.map +1 -0
- package/dist/api/utilities/electron-shared-texture.d.ts +368 -0
- package/dist/api/utilities/electron-shared-texture.js +540 -0
- package/dist/api/utilities/electron-shared-texture.js.map +1 -0
- package/dist/api/utilities/image.d.ts +207 -0
- package/dist/api/utilities/image.js +213 -0
- package/dist/api/utilities/image.js.map +1 -0
- package/dist/api/utilities/index.d.ts +14 -0
- package/dist/api/utilities/index.js +27 -0
- package/dist/api/utilities/index.js.map +1 -0
- package/dist/api/utilities/live-decoding.d.ts +15 -0
- package/dist/api/utilities/live-decoding.js +32 -0
- package/dist/api/utilities/live-decoding.js.map +1 -0
- package/dist/api/utilities/media-type.d.ts +49 -0
- package/dist/api/utilities/media-type.js +53 -0
- package/dist/api/utilities/media-type.js.map +1 -0
- package/dist/api/utilities/packet-age-watchdog.d.ts +198 -0
- package/dist/api/utilities/packet-age-watchdog.js +594 -0
- package/dist/api/utilities/packet-age-watchdog.js.map +1 -0
- package/dist/api/utilities/pixel-format.d.ts +89 -0
- package/dist/api/utilities/pixel-format.js +97 -0
- package/dist/api/utilities/pixel-format.js.map +1 -0
- package/dist/api/utilities/sample-format.d.ts +129 -0
- package/dist/api/utilities/sample-format.js +141 -0
- package/dist/api/utilities/sample-format.js.map +1 -0
- package/dist/api/utilities/scale-size.d.ts +51 -0
- package/dist/api/utilities/scale-size.js +55 -0
- package/dist/api/utilities/scale-size.js.map +1 -0
- package/dist/api/utilities/scheduler.d.ts +138 -0
- package/dist/api/utilities/scheduler.js +98 -0
- package/dist/api/utilities/scheduler.js.map +1 -0
- package/dist/api/utilities/streaming.d.ts +186 -0
- package/dist/api/utilities/streaming.js +309 -0
- package/dist/api/utilities/streaming.js.map +1 -0
- package/dist/api/utilities/timestamp.d.ts +193 -0
- package/dist/api/utilities/timestamp.js +206 -0
- package/dist/api/utilities/timestamp.js.map +1 -0
- package/dist/api/utilities/video-transcode.d.ts +218 -0
- package/dist/api/utilities/video-transcode.js +624 -0
- package/dist/api/utilities/video-transcode.js.map +1 -0
- package/dist/api/utilities/whisper-model.d.ts +330 -0
- package/dist/api/utilities/whisper-model.js +587 -0
- package/dist/api/utilities/whisper-model.js.map +1 -0
- package/dist/api/utils.d.ts +19 -0
- package/dist/api/utils.js +39 -0
- package/dist/api/utils.js.map +1 -0
- package/dist/api/webrtc-stream.d.ts +294 -0
- package/dist/api/webrtc-stream.js +546 -0
- package/dist/api/webrtc-stream.js.map +1 -0
- package/dist/api/whisper.d.ts +326 -0
- package/dist/api/whisper.js +402 -0
- package/dist/api/whisper.js.map +1 -0
- package/dist/constants/bsf-options.d.ts +364 -0
- package/dist/constants/bsf-options.js +7 -0
- package/dist/constants/bsf-options.js.map +1 -0
- package/dist/constants/channel-layouts.d.ts +53 -0
- package/dist/constants/channel-layouts.js +57 -0
- package/dist/constants/channel-layouts.js.map +1 -0
- package/dist/constants/constants.d.ts +2435 -0
- package/dist/constants/constants.js +1989 -0
- package/dist/constants/constants.js.map +1 -0
- package/dist/constants/decoders.d.ts +651 -0
- package/dist/constants/decoders.js +641 -0
- package/dist/constants/decoders.js.map +1 -0
- package/dist/constants/encoders.d.ts +314 -0
- package/dist/constants/encoders.js +309 -0
- package/dist/constants/encoders.js.map +1 -0
- package/dist/constants/filter-options.d.ts +12099 -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 +4319 -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/hardware.d.ts +26 -0
- package/dist/constants/hardware.js +27 -0
- package/dist/constants/hardware.js.map +1 -0
- package/dist/constants/index.d.ts +10 -0
- package/dist/constants/index.js +6 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/options.d.ts +6217 -0
- package/dist/constants/options.js +7 -0
- package/dist/constants/options.js.map +1 -0
- package/dist/ffmpeg/index.d.ts +101 -0
- package/dist/ffmpeg/index.js +161 -0
- package/dist/ffmpeg/index.js.map +1 -0
- package/dist/ffmpeg/version.d.ts +6 -0
- package/dist/ffmpeg/version.js +7 -0
- package/dist/ffmpeg/version.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audio-fifo.d.ts +399 -0
- package/dist/lib/audio-fifo.js +431 -0
- package/dist/lib/audio-fifo.js.map +1 -0
- package/dist/lib/binding.d.ts +242 -0
- package/dist/lib/binding.js +102 -0
- package/dist/lib/binding.js.map +1 -0
- package/dist/lib/bitstream-filter-context.d.ts +381 -0
- package/dist/lib/bitstream-filter-context.js +443 -0
- package/dist/lib/bitstream-filter-context.js.map +1 -0
- package/dist/lib/bitstream-filter.d.ts +140 -0
- package/dist/lib/bitstream-filter.js +154 -0
- package/dist/lib/bitstream-filter.js.map +1 -0
- package/dist/lib/codec-context.d.ts +1069 -0
- package/dist/lib/codec-context.js +1336 -0
- package/dist/lib/codec-context.js.map +1 -0
- package/dist/lib/codec-parameters.d.ts +664 -0
- package/dist/lib/codec-parameters.js +813 -0
- package/dist/lib/codec-parameters.js.map +1 -0
- package/dist/lib/codec-parser.d.ts +201 -0
- package/dist/lib/codec-parser.js +213 -0
- package/dist/lib/codec-parser.js.map +1 -0
- package/dist/lib/codec.d.ts +617 -0
- package/dist/lib/codec.js +746 -0
- package/dist/lib/codec.js.map +1 -0
- package/dist/lib/device.d.ts +294 -0
- package/dist/lib/device.js +326 -0
- package/dist/lib/device.js.map +1 -0
- package/dist/lib/dictionary.d.ts +333 -0
- package/dist/lib/dictionary.js +372 -0
- package/dist/lib/dictionary.js.map +1 -0
- package/dist/lib/error.d.ts +311 -0
- package/dist/lib/error.js +395 -0
- package/dist/lib/error.js.map +1 -0
- package/dist/lib/fifo.d.ts +416 -0
- package/dist/lib/fifo.js +453 -0
- package/dist/lib/fifo.js.map +1 -0
- package/dist/lib/filter-context.d.ts +714 -0
- package/dist/lib/filter-context.js +783 -0
- package/dist/lib/filter-context.js.map +1 -0
- package/dist/lib/filter-graph-segment.d.ts +161 -0
- package/dist/lib/filter-graph-segment.js +172 -0
- package/dist/lib/filter-graph-segment.js.map +1 -0
- package/dist/lib/filter-graph.d.ts +641 -0
- package/dist/lib/filter-graph.js +704 -0
- package/dist/lib/filter-graph.js.map +1 -0
- package/dist/lib/filter-inout.d.ts +202 -0
- package/dist/lib/filter-inout.js +261 -0
- package/dist/lib/filter-inout.js.map +1 -0
- package/dist/lib/filter.d.ts +243 -0
- package/dist/lib/filter.js +272 -0
- package/dist/lib/filter.js.map +1 -0
- package/dist/lib/format-context.d.ts +1335 -0
- package/dist/lib/format-context.js +1455 -0
- package/dist/lib/format-context.js.map +1 -0
- package/dist/lib/frame-utils.d.ts +116 -0
- package/dist/lib/frame-utils.js +98 -0
- package/dist/lib/frame-utils.js.map +1 -0
- package/dist/lib/frame.d.ts +1315 -0
- package/dist/lib/frame.js +1535 -0
- package/dist/lib/frame.js.map +1 -0
- package/dist/lib/hardware-device-context.d.ts +362 -0
- package/dist/lib/hardware-device-context.js +383 -0
- package/dist/lib/hardware-device-context.js.map +1 -0
- package/dist/lib/hardware-frames-context.d.ts +418 -0
- package/dist/lib/hardware-frames-context.js +471 -0
- package/dist/lib/hardware-frames-context.js.map +1 -0
- package/dist/lib/index.d.ts +35 -0
- package/dist/lib/index.js +60 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/input-format.d.ts +249 -0
- package/dist/lib/input-format.js +306 -0
- package/dist/lib/input-format.js.map +1 -0
- package/dist/lib/io-context.d.ts +698 -0
- package/dist/lib/io-context.js +771 -0
- package/dist/lib/io-context.js.map +1 -0
- package/dist/lib/log.d.ts +174 -0
- package/dist/lib/log.js +184 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/native-types.d.ts +1024 -0
- package/dist/lib/native-types.js +2 -0
- package/dist/lib/native-types.js.map +1 -0
- package/dist/lib/option.d.ts +953 -0
- package/dist/lib/option.js +1607 -0
- package/dist/lib/option.js.map +1 -0
- package/dist/lib/output-format.d.ts +222 -0
- package/dist/lib/output-format.js +259 -0
- package/dist/lib/output-format.js.map +1 -0
- package/dist/lib/packet.d.ts +521 -0
- package/dist/lib/packet.js +613 -0
- package/dist/lib/packet.js.map +1 -0
- package/dist/lib/rational.d.ts +251 -0
- package/dist/lib/rational.js +278 -0
- package/dist/lib/rational.js.map +1 -0
- package/dist/lib/software-resample-context.d.ts +552 -0
- package/dist/lib/software-resample-context.js +592 -0
- package/dist/lib/software-resample-context.js.map +1 -0
- package/dist/lib/software-scale-context.d.ts +344 -0
- package/dist/lib/software-scale-context.js +366 -0
- package/dist/lib/software-scale-context.js.map +1 -0
- package/dist/lib/stream.d.ts +383 -0
- package/dist/lib/stream.js +524 -0
- package/dist/lib/stream.js.map +1 -0
- package/dist/lib/sync-queue.d.ts +179 -0
- package/dist/lib/sync-queue.js +197 -0
- package/dist/lib/sync-queue.js.map +1 -0
- package/dist/lib/types.d.ts +34 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utilities.d.ts +1198 -0
- package/dist/lib/utilities.js +1302 -0
- package/dist/lib/utilities.js.map +1 -0
- package/dist/utils/electron.d.ts +49 -0
- package/dist/utils/electron.js +63 -0
- package/dist/utils/electron.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/libc.d.ts +11 -0
- package/dist/utils/libc.js +35 -0
- package/dist/utils/libc.js.map +1 -0
- package/dist/utils/variant.d.ts +26 -0
- package/dist/utils/variant.js +31 -0
- package/dist/utils/variant.js.map +1 -0
- package/dist/webrtc/index.d.ts +4 -0
- package/dist/webrtc/index.js +7 -0
- package/dist/webrtc/index.js.map +1 -0
- package/install/check.js +124 -0
- package/install/ffmpeg.js +66 -0
- package/package.json +165 -3
|
@@ -0,0 +1,1427 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { AV_PKT_DATA_NEW_EXTRADATA, AVERROR_BSF_NOT_FOUND, AVERROR_EAGAIN, AVERROR_EOF } from '../constants/constants.js';
|
|
54
|
+
import { BitStreamFilterContext } from '../lib/bitstream-filter-context.js';
|
|
55
|
+
import { BitStreamFilter } from '../lib/bitstream-filter.js';
|
|
56
|
+
import { FFmpegError } from '../lib/error.js';
|
|
57
|
+
import { Packet } from '../lib/packet.js';
|
|
58
|
+
import { Stream } from '../lib/stream.js';
|
|
59
|
+
import { PACKET_THREAD_QUEUE_SIZE } from './constants.js';
|
|
60
|
+
import { Encoder } from './encoder.js';
|
|
61
|
+
import { Muxer } from './muxer.js';
|
|
62
|
+
import { AsyncQueue } from './utilities/async-queue.js';
|
|
63
|
+
import { Scheduler, SchedulerControl } from './utilities/scheduler.js';
|
|
64
|
+
/**
|
|
65
|
+
* High-level bitstream filter for packet processing.
|
|
66
|
+
*
|
|
67
|
+
* Provides simplified interface for applying bitstream filters to packets.
|
|
68
|
+
* Handles filter initialization, packet processing, and memory management.
|
|
69
|
+
* Supports both synchronous packet-by-packet filtering and async iteration over packets.
|
|
70
|
+
* Supports filters like h264_mp4toannexb, hevc_mp4toannexb, aac_adtstoasc.
|
|
71
|
+
* Essential for format conversion and stream compatibility in transcoding pipelines.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* import { BitStreamFilterAPI } from 'node-av/api';
|
|
76
|
+
*
|
|
77
|
+
* // Create H.264 Annex B converter
|
|
78
|
+
* const filter = BitStreamFilterAPI.create('h264_mp4toannexb', stream);
|
|
79
|
+
*
|
|
80
|
+
* // Filter packet
|
|
81
|
+
* const outputPackets = await filter.filterAll(inputPacket);
|
|
82
|
+
* for (const packet of outputPackets) {
|
|
83
|
+
* await output.writePacket(packet);
|
|
84
|
+
* packet.free();
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Filter packet stream
|
|
91
|
+
* const filter = BitStreamFilterAPI.create('hevc_mp4toannexb', videoStream);
|
|
92
|
+
*
|
|
93
|
+
* for await (const packet of filter.packets(input.packets())) {
|
|
94
|
+
* await output.writePacket(packet);
|
|
95
|
+
* packet.free();
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @see {@link BitStreamFilter} For available filters
|
|
100
|
+
* @see {@link BitStreamFilterContext} For low-level API
|
|
101
|
+
* @see {@link Muxer} For writing filtered packets
|
|
102
|
+
*/
|
|
103
|
+
export class BitStreamFilterAPI {
|
|
104
|
+
ctx;
|
|
105
|
+
bsf;
|
|
106
|
+
source;
|
|
107
|
+
initialized = false;
|
|
108
|
+
packet;
|
|
109
|
+
pendingOutput = [];
|
|
110
|
+
isClosed = false;
|
|
111
|
+
// Worker pattern for push-based processing
|
|
112
|
+
inputQueue;
|
|
113
|
+
outputQueue;
|
|
114
|
+
workerPromise = null;
|
|
115
|
+
nextComponent = null;
|
|
116
|
+
pipeToPromise = null;
|
|
117
|
+
signal;
|
|
118
|
+
/**
|
|
119
|
+
* @param bsf - Bitstream filter
|
|
120
|
+
*
|
|
121
|
+
* @param ctx - Filter context
|
|
122
|
+
*
|
|
123
|
+
* @param source - Input source for lazy parameter resolution
|
|
124
|
+
*
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
constructor(bsf, ctx, source) {
|
|
128
|
+
this.bsf = bsf;
|
|
129
|
+
this.ctx = ctx;
|
|
130
|
+
this.source = source;
|
|
131
|
+
this.packet = new Packet();
|
|
132
|
+
this.packet.alloc();
|
|
133
|
+
this.inputQueue = new AsyncQueue(PACKET_THREAD_QUEUE_SIZE, (p) => p.free());
|
|
134
|
+
this.outputQueue = new AsyncQueue(PACKET_THREAD_QUEUE_SIZE, (p) => p.free());
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create a bitstream filter.
|
|
138
|
+
*
|
|
139
|
+
* The input source provides the codec parameters the filter is configured with.
|
|
140
|
+
* Pass a {@link Stream} to filter its packets directly (stream copy), or an
|
|
141
|
+
* {@link Encoder} to filter that encoder's output (transcode) - parameters are
|
|
142
|
+
* derived from the encoder once it is open. Another {@link BitStreamFilterAPI}
|
|
143
|
+
* may be passed to chain filters.
|
|
144
|
+
*
|
|
145
|
+
* Initialization is lazy: filter options are validated immediately, but the
|
|
146
|
+
* input parameters are bound and the filter is initialized on the first packet,
|
|
147
|
+
* so an `Encoder` source (opened lazily on its first frame) is ready in time.
|
|
148
|
+
*
|
|
149
|
+
* Direct mapping to av_bsf_get_by_name() and av_bsf_alloc().
|
|
150
|
+
*
|
|
151
|
+
* @param filterName - Name of the bitstream filter
|
|
152
|
+
*
|
|
153
|
+
* @param source - Input source: a `Stream` (copy), `Encoder` (transcode), or upstream filter (chain)
|
|
154
|
+
*
|
|
155
|
+
* @param filterOptions - Optional filter-specific options
|
|
156
|
+
*
|
|
157
|
+
* @returns Configured bitstream filter
|
|
158
|
+
*
|
|
159
|
+
* @throws {FFmpegError} If the filter is not found, allocation fails, or an option is invalid
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* // Stream copy: H.264 MP4 to Annex B conversion
|
|
164
|
+
* const filter = BitStreamFilterAPI.create('h264_mp4toannexb', videoStream);
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // Transcode: derive parameters from the encoder's output
|
|
170
|
+
* const filter = BitStreamFilterAPI.create('h264_metadata', encoder, {
|
|
171
|
+
* options: { aud: 'remove', level: '4.1' },
|
|
172
|
+
* });
|
|
173
|
+
* pipeline(input, decoder, encoder, filter, output);
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @see {@link BitStreamFilter.getByName} For filter discovery
|
|
177
|
+
* @see {@link BitstreamFilterOptions} For available options
|
|
178
|
+
*/
|
|
179
|
+
// eslint-disable-next-line space-before-function-paren
|
|
180
|
+
static create(filterName, source, filterOptions) {
|
|
181
|
+
if (!source) {
|
|
182
|
+
throw new Error('A source (Stream, Encoder, or BitStreamFilterAPI) is required');
|
|
183
|
+
}
|
|
184
|
+
// Find the bitstream filter
|
|
185
|
+
const filter = BitStreamFilter.getByName(filterName);
|
|
186
|
+
if (!filter) {
|
|
187
|
+
throw new FFmpegError(AVERROR_BSF_NOT_FOUND);
|
|
188
|
+
}
|
|
189
|
+
// Create and allocate context
|
|
190
|
+
const ctx = new BitStreamFilterContext();
|
|
191
|
+
const allocRet = ctx.alloc(filter);
|
|
192
|
+
FFmpegError.throwIfError(allocRet, 'Failed to allocate bitstream filter context');
|
|
193
|
+
try {
|
|
194
|
+
// Apply (and validate) filter-specific options now; input parameters and
|
|
195
|
+
// init() are deferred to ensureInitialized() on the first packet.
|
|
196
|
+
if (filterOptions?.options) {
|
|
197
|
+
for (const [key, value] of Object.entries(filterOptions.options)) {
|
|
198
|
+
const ret = ctx.setOption(key, value);
|
|
199
|
+
FFmpegError.throwIfError(ret, `Failed to set bitstream filter option '${key}'`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const bsfApi = new BitStreamFilterAPI(filter, ctx, source);
|
|
203
|
+
if (filterOptions?.signal) {
|
|
204
|
+
filterOptions.signal.throwIfAborted();
|
|
205
|
+
bsfApi.signal = filterOptions.signal;
|
|
206
|
+
}
|
|
207
|
+
return bsfApi;
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
// Clean up on error
|
|
211
|
+
ctx.free();
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get filter name.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* console.log(`Using filter: ${filter.name}`);
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
get name() {
|
|
224
|
+
return this.bsf.name ?? 'unknown';
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get output codec parameters.
|
|
228
|
+
*
|
|
229
|
+
* Parameters after filter processing.
|
|
230
|
+
* May differ from input parameters.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* const outputParams = filter.outputCodecParameters;
|
|
235
|
+
* console.log(`Output codec: ${outputParams?.codecId}`);
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
get outputCodecParameters() {
|
|
239
|
+
return this.ctx.outputCodecParameters;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get output time base.
|
|
243
|
+
*
|
|
244
|
+
* Time base after filter processing.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const tb = filter.outputTimeBase;
|
|
249
|
+
* console.log(`Output timebase: ${tb?.num}/${tb?.den}`);
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
get outputTimeBase() {
|
|
253
|
+
return this.ctx.outputTimeBase;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Check if filter is open.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* if (filter.isBitstreamFilterOpen) {
|
|
261
|
+
* const packet = await filter.process(frame);
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
get isBitstreamFilterOpen() {
|
|
266
|
+
return !this.isClosed;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Check if the filter has been initialized.
|
|
270
|
+
*
|
|
271
|
+
* Initialization is lazy and happens on the first processed packet, after
|
|
272
|
+
* which {@link outputCodecParameters} reflect the filter's output.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* if (filter.isInitialized) {
|
|
277
|
+
* const params = filter.outputCodecParameters;
|
|
278
|
+
* }
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
get isInitialized() {
|
|
282
|
+
return this.initialized;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get associated stream.
|
|
286
|
+
*
|
|
287
|
+
* Returns the stream this filter was created for. Only available when the
|
|
288
|
+
* filter was created from a {@link Stream}; filters created from an `Encoder`
|
|
289
|
+
* derive their parameters from the encoder's output instead.
|
|
290
|
+
*
|
|
291
|
+
* @returns Associated stream
|
|
292
|
+
*
|
|
293
|
+
* @throws {Error} If the filter was not created from a stream
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* const stream = filter.getStream();
|
|
298
|
+
* console.log(`Filtering stream ${stream.index}`);
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
getStream() {
|
|
302
|
+
if (!(this.source instanceof Stream)) {
|
|
303
|
+
throw new Error('No associated stream: this bitstream filter derives its parameters from an upstream component');
|
|
304
|
+
}
|
|
305
|
+
return this.source;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Send a packet to the filter.
|
|
309
|
+
*
|
|
310
|
+
* Sends a packet to the filter for processing.
|
|
311
|
+
* Does not return filtered packets - use {@link receive} to retrieve packets.
|
|
312
|
+
* A single packet can produce zero, one, or multiple packets depending on filter.
|
|
313
|
+
*
|
|
314
|
+
* **Important**: This method only SENDS the packet to the filter.
|
|
315
|
+
* You must call {@link receive} separately (potentially multiple times) to get filtered packets.
|
|
316
|
+
*
|
|
317
|
+
* Direct mapping to av_bsf_send_packet().
|
|
318
|
+
*
|
|
319
|
+
* @param packet - Packet to send to filter, or null to flush
|
|
320
|
+
*
|
|
321
|
+
* @throws {FFmpegError} If sending fails
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```typescript
|
|
325
|
+
* // Send packet and receive filtered packets
|
|
326
|
+
* await filter.filter(inputPacket);
|
|
327
|
+
*
|
|
328
|
+
* // Receive all available filtered packets
|
|
329
|
+
* while (true) {
|
|
330
|
+
* const outPacket = await filter.receive();
|
|
331
|
+
* if (!outPacket) break;
|
|
332
|
+
* console.log(`Filtered packet with PTS: ${outPacket.pts}`);
|
|
333
|
+
* await output.writePacket(outPacket);
|
|
334
|
+
* outPacket.free();
|
|
335
|
+
* }
|
|
336
|
+
* ```
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```typescript
|
|
340
|
+
* for await (const packet of input.packets()) {
|
|
341
|
+
* // packet is null at end of stream - automatically flushes filter
|
|
342
|
+
* await filter.filter(packet);
|
|
343
|
+
*
|
|
344
|
+
* // Receive available filtered packets
|
|
345
|
+
* let outPacket;
|
|
346
|
+
* while ((outPacket = await filter.receive())) {
|
|
347
|
+
* await output.writePacket(outPacket);
|
|
348
|
+
* outPacket.free();
|
|
349
|
+
* }
|
|
350
|
+
* }
|
|
351
|
+
* ```
|
|
352
|
+
*
|
|
353
|
+
* @see {@link receive} For receiving filtered packets
|
|
354
|
+
* @see {@link filterAll} For combined send+receive operation
|
|
355
|
+
* @see {@link packets} For automatic packet iteration
|
|
356
|
+
* @see {@link flush} For end-of-stream handling
|
|
357
|
+
* @see {@link filterSync} For synchronous version
|
|
358
|
+
*/
|
|
359
|
+
async filter(packet) {
|
|
360
|
+
this.signal?.throwIfAborted();
|
|
361
|
+
if (this.isClosed) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
this.ensureInitialized();
|
|
365
|
+
// Send packet to filter (null signals EOF/flush)
|
|
366
|
+
await this.sendPacketWithRetry(packet);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Send a packet to the filter synchronously.
|
|
370
|
+
* Synchronous version of filter.
|
|
371
|
+
*
|
|
372
|
+
* Sends a packet to the filter for processing.
|
|
373
|
+
* Does not return filtered packets - use {@link receiveSync} to retrieve packets.
|
|
374
|
+
* A single packet can produce zero, one, or multiple packets depending on filter.
|
|
375
|
+
*
|
|
376
|
+
* **Important**: This method only SENDS the packet to the filter.
|
|
377
|
+
* You must call {@link receiveSync} separately (potentially multiple times) to get filtered packets.
|
|
378
|
+
*
|
|
379
|
+
* Direct mapping to av_bsf_send_packet().
|
|
380
|
+
*
|
|
381
|
+
* @param packet - Packet to send to filter, or null to flush
|
|
382
|
+
*
|
|
383
|
+
* @throws {FFmpegError} If sending fails
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```typescript
|
|
387
|
+
* // Send packet and receive filtered packets
|
|
388
|
+
* filter.filterSync(inputPacket);
|
|
389
|
+
*
|
|
390
|
+
* // Receive all available filtered packets
|
|
391
|
+
* while (true) {
|
|
392
|
+
* const outPacket = filter.receiveSync();
|
|
393
|
+
* if (!outPacket) break;
|
|
394
|
+
* console.log(`Filtered packet with PTS: ${outPacket.pts}`);
|
|
395
|
+
* output.writePacketSync(outPacket);
|
|
396
|
+
* outPacket.free();
|
|
397
|
+
* }
|
|
398
|
+
* ```
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* for (const packet of packets) {
|
|
403
|
+
* // packet is null at end of stream - automatically flushes filter
|
|
404
|
+
* filter.filterSync(packet);
|
|
405
|
+
*
|
|
406
|
+
* // Receive available filtered packets
|
|
407
|
+
* let outPacket;
|
|
408
|
+
* while ((outPacket = filter.receiveSync())) {
|
|
409
|
+
* output.writePacketSync(outPacket);
|
|
410
|
+
* outPacket.free();
|
|
411
|
+
* }
|
|
412
|
+
* }
|
|
413
|
+
* ```
|
|
414
|
+
*
|
|
415
|
+
* @see {@link receiveSync} For receiving filtered packets
|
|
416
|
+
* @see {@link filterAllSync} For combined send+receive operation
|
|
417
|
+
* @see {@link packetsSync} For automatic packet iteration
|
|
418
|
+
* @see {@link flushSync} For end-of-stream handling
|
|
419
|
+
* @see {@link filter} For async version
|
|
420
|
+
*/
|
|
421
|
+
filterSync(packet) {
|
|
422
|
+
if (this.isClosed) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
this.ensureInitialized();
|
|
426
|
+
// Send packet to filter (null signals EOF/flush)
|
|
427
|
+
this.sendPacketWithRetrySync(packet);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Filter a packet to packets.
|
|
431
|
+
*
|
|
432
|
+
* Sends a packet to the filter and receives all available filtered packets.
|
|
433
|
+
* Returns array of packets - may be empty if filter needs more data.
|
|
434
|
+
* One packet can produce zero, one, or multiple packets depending on filter.
|
|
435
|
+
*
|
|
436
|
+
* Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
|
|
437
|
+
*
|
|
438
|
+
* @param packet - Packet to filter, or null to flush
|
|
439
|
+
*
|
|
440
|
+
* @returns Array of filtered packets (empty if more data needed or filter is closed)
|
|
441
|
+
*
|
|
442
|
+
* @throws {FFmpegError} If filtering fails
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* ```typescript
|
|
446
|
+
* const outputPackets = await filter.filterAll(inputPacket);
|
|
447
|
+
* for (const packet of outputPackets) {
|
|
448
|
+
* console.log(`Filtered packet: pts=${packet.pts}`);
|
|
449
|
+
* await output.writePacket(packet);
|
|
450
|
+
* packet.free();
|
|
451
|
+
* }
|
|
452
|
+
* ```
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```typescript
|
|
456
|
+
* // Flush remaining packets at end of stream
|
|
457
|
+
* const remaining = await filter.filterAll(null);
|
|
458
|
+
* for (const packet of remaining) {
|
|
459
|
+
* await output.writePacket(packet);
|
|
460
|
+
* packet.free();
|
|
461
|
+
* }
|
|
462
|
+
* ```
|
|
463
|
+
*
|
|
464
|
+
* @see {@link filter} For single packet filtering
|
|
465
|
+
* @see {@link packets} For stream processing
|
|
466
|
+
* @see {@link flush} For end-of-stream handling
|
|
467
|
+
* @see {@link filterAllSync} For synchronous version
|
|
468
|
+
*/
|
|
469
|
+
async filterAll(packet) {
|
|
470
|
+
this.signal?.throwIfAborted();
|
|
471
|
+
await this.filter(packet);
|
|
472
|
+
// Receive all output packets
|
|
473
|
+
const outputPackets = [];
|
|
474
|
+
while (true) {
|
|
475
|
+
const outPacket = await this.receive();
|
|
476
|
+
if (!outPacket)
|
|
477
|
+
break;
|
|
478
|
+
outputPackets.push(outPacket);
|
|
479
|
+
}
|
|
480
|
+
return outputPackets;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Filter a packet to packets synchronously.
|
|
484
|
+
* Synchronous version of filterAll.
|
|
485
|
+
*
|
|
486
|
+
* Sends a packet to the filter and receives all available filtered packets.
|
|
487
|
+
* Returns array of packets - may be empty if filter needs more data.
|
|
488
|
+
* One packet can produce zero, one, or multiple packets depending on filter.
|
|
489
|
+
*
|
|
490
|
+
* Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
|
|
491
|
+
*
|
|
492
|
+
* @param packet - Packet to filter, or null to flush
|
|
493
|
+
*
|
|
494
|
+
* @returns Array of filtered packets (empty if more data needed or filter is closed)
|
|
495
|
+
*
|
|
496
|
+
* @throws {FFmpegError} If filtering fails
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* const outputPackets = filter.filterAllSync(inputPacket);
|
|
501
|
+
* for (const packet of outputPackets) {
|
|
502
|
+
* console.log(`Filtered packet: pts=${packet.pts}`);
|
|
503
|
+
* output.writePacketSync(packet);
|
|
504
|
+
* packet.free();
|
|
505
|
+
* }
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* ```typescript
|
|
510
|
+
* // Flush remaining packets at end of stream
|
|
511
|
+
* const remaining = filter.filterAllSync(null);
|
|
512
|
+
* for (const packet of remaining) {
|
|
513
|
+
* output.writePacketSync(packet);
|
|
514
|
+
* packet.free();
|
|
515
|
+
* }
|
|
516
|
+
* ```
|
|
517
|
+
*
|
|
518
|
+
* @see {@link filterSync} For single packet filtering
|
|
519
|
+
* @see {@link packetsSync} For stream processing
|
|
520
|
+
* @see {@link flushSync} For end-of-stream handling
|
|
521
|
+
* @see {@link filterAll} For async version
|
|
522
|
+
*/
|
|
523
|
+
filterAllSync(packet) {
|
|
524
|
+
this.filterSync(packet);
|
|
525
|
+
// Receive all output packets
|
|
526
|
+
const outputPackets = [];
|
|
527
|
+
while (true) {
|
|
528
|
+
const outPacket = this.receiveSync();
|
|
529
|
+
if (!outPacket)
|
|
530
|
+
break;
|
|
531
|
+
outputPackets.push(outPacket);
|
|
532
|
+
}
|
|
533
|
+
return outputPackets;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Filter packet stream to filtered packet stream.
|
|
537
|
+
*
|
|
538
|
+
* High-level async generator for complete filtering pipeline.
|
|
539
|
+
* Filter is only flushed when EOF (null) signal is explicitly received.
|
|
540
|
+
* Primary interface for stream-based filtering.
|
|
541
|
+
*
|
|
542
|
+
* **EOF Handling:**
|
|
543
|
+
* - Send null to flush filter and get remaining buffered packets
|
|
544
|
+
* - Generator yields null after flushing when null is received
|
|
545
|
+
* - No automatic flushing - filter stays open until EOF or close()
|
|
546
|
+
*
|
|
547
|
+
* @param packets - Async iterable of packets, single packet, or null to flush
|
|
548
|
+
*
|
|
549
|
+
* @yields {Packet | null} Filtered packets, followed by null when explicitly flushed
|
|
550
|
+
*
|
|
551
|
+
* @throws {FFmpegError} If filtering fails
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
* ```typescript
|
|
555
|
+
* // Stream of packets with automatic EOF propagation
|
|
556
|
+
* for await (const packet of filter.packets(input.packets())) {
|
|
557
|
+
* if (packet === null) {
|
|
558
|
+
* console.log('Filter flushed');
|
|
559
|
+
* break;
|
|
560
|
+
* }
|
|
561
|
+
* await output.writePacket(packet);
|
|
562
|
+
* packet.free(); // Must free output packets
|
|
563
|
+
* }
|
|
564
|
+
* ```
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```typescript
|
|
568
|
+
* // Single packet - no automatic flush
|
|
569
|
+
* for await (const packet of filter.packets(singlePacket)) {
|
|
570
|
+
* await output.writePacket(packet);
|
|
571
|
+
* packet.free();
|
|
572
|
+
* }
|
|
573
|
+
* // Filter remains open, buffered packets not flushed
|
|
574
|
+
* ```
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```typescript
|
|
578
|
+
* // Explicit flush with EOF
|
|
579
|
+
* for await (const packet of filter.packets(null)) {
|
|
580
|
+
* if (packet === null) {
|
|
581
|
+
* console.log('All buffered packets flushed');
|
|
582
|
+
* break;
|
|
583
|
+
* }
|
|
584
|
+
* console.log('Buffered packet:', packet.pts);
|
|
585
|
+
* await output.writePacket(packet);
|
|
586
|
+
* packet.free();
|
|
587
|
+
* }
|
|
588
|
+
* ```
|
|
589
|
+
*
|
|
590
|
+
* @see {@link filter} For single packet filtering
|
|
591
|
+
* @see {@link Demuxer.packets} For packet source
|
|
592
|
+
* @see {@link packetsSync} For sync version
|
|
593
|
+
*/
|
|
594
|
+
async *packets(packets) {
|
|
595
|
+
const self = this;
|
|
596
|
+
const processPacket = async function* (packet) {
|
|
597
|
+
await self.filter(packet);
|
|
598
|
+
while (true) {
|
|
599
|
+
const outPacket = await self.receive();
|
|
600
|
+
if (!outPacket)
|
|
601
|
+
break;
|
|
602
|
+
yield outPacket;
|
|
603
|
+
}
|
|
604
|
+
}.bind(this);
|
|
605
|
+
const finalize = async function* () {
|
|
606
|
+
for await (const remaining of self.flushPackets()) {
|
|
607
|
+
yield remaining;
|
|
608
|
+
}
|
|
609
|
+
yield null;
|
|
610
|
+
}.bind(this);
|
|
611
|
+
if (packets === null) {
|
|
612
|
+
yield* finalize();
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (packets instanceof Packet) {
|
|
616
|
+
yield* processPacket(packets);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
for await (const packet_1 of packets) {
|
|
620
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
621
|
+
try {
|
|
622
|
+
const packet = __addDisposableResource(env_1, packet_1, false);
|
|
623
|
+
this.signal?.throwIfAborted();
|
|
624
|
+
if (packet === null) {
|
|
625
|
+
yield* finalize();
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
yield* processPacket(packet);
|
|
629
|
+
}
|
|
630
|
+
catch (e_1) {
|
|
631
|
+
env_1.error = e_1;
|
|
632
|
+
env_1.hasError = true;
|
|
633
|
+
}
|
|
634
|
+
finally {
|
|
635
|
+
__disposeResources(env_1);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Filter packet stream to filtered packet stream synchronously.
|
|
641
|
+
* Synchronous version of packets.
|
|
642
|
+
*
|
|
643
|
+
* High-level sync generator for complete filtering pipeline.
|
|
644
|
+
* Filter is only flushed when EOF (null) signal is explicitly received.
|
|
645
|
+
* Primary interface for stream-based filtering.
|
|
646
|
+
*
|
|
647
|
+
* **EOF Handling:**
|
|
648
|
+
* - Send null to flush filter and get remaining buffered packets
|
|
649
|
+
* - Generator yields null after flushing when null is received
|
|
650
|
+
* - No automatic flushing - filter stays open until EOF or close()
|
|
651
|
+
*
|
|
652
|
+
* @param packets - Iterable of packets, single packet, or null to flush
|
|
653
|
+
*
|
|
654
|
+
* @yields {Packet | null} Filtered packets, followed by null when explicitly flushed
|
|
655
|
+
*
|
|
656
|
+
* @throws {FFmpegError} If filtering fails
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* ```typescript
|
|
660
|
+
* // Stream of packets with automatic EOF propagation
|
|
661
|
+
* for (const packet of filter.packetsSync(inputPackets)) {
|
|
662
|
+
* if (packet === null) {
|
|
663
|
+
* console.log('Filter flushed');
|
|
664
|
+
* break;
|
|
665
|
+
* }
|
|
666
|
+
* output.writePacketSync(packet);
|
|
667
|
+
* packet.free(); // Must free output packets
|
|
668
|
+
* }
|
|
669
|
+
* ```
|
|
670
|
+
*
|
|
671
|
+
* @example
|
|
672
|
+
* ```typescript
|
|
673
|
+
* // Single packet - no automatic flush
|
|
674
|
+
* for (const packet of filter.packetsSync(singlePacket)) {
|
|
675
|
+
* output.writePacketSync(packet);
|
|
676
|
+
* packet.free();
|
|
677
|
+
* }
|
|
678
|
+
* // Filter remains open, buffered packets not flushed
|
|
679
|
+
* ```
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```typescript
|
|
683
|
+
* // Explicit flush with EOF
|
|
684
|
+
* for (const packet of filter.packetsSync(null)) {
|
|
685
|
+
* if (packet === null) {
|
|
686
|
+
* console.log('All buffered packets flushed');
|
|
687
|
+
* break;
|
|
688
|
+
* }
|
|
689
|
+
* console.log('Buffered packet:', packet.pts);
|
|
690
|
+
* output.writePacketSync(packet);
|
|
691
|
+
* packet.free();
|
|
692
|
+
* }
|
|
693
|
+
* ```
|
|
694
|
+
*
|
|
695
|
+
* @see {@link filterSync} For single packet filtering
|
|
696
|
+
* @see {@link packets} For async version
|
|
697
|
+
*/
|
|
698
|
+
*packetsSync(packets) {
|
|
699
|
+
const self = this;
|
|
700
|
+
const processPacket = function* (packet) {
|
|
701
|
+
self.filterSync(packet);
|
|
702
|
+
while (true) {
|
|
703
|
+
const outPacket = self.receiveSync();
|
|
704
|
+
if (!outPacket)
|
|
705
|
+
break;
|
|
706
|
+
yield outPacket;
|
|
707
|
+
}
|
|
708
|
+
}.bind(this);
|
|
709
|
+
const finalize = function* () {
|
|
710
|
+
for (const remaining of self.flushPacketsSync()) {
|
|
711
|
+
yield remaining;
|
|
712
|
+
}
|
|
713
|
+
yield null;
|
|
714
|
+
}.bind(this);
|
|
715
|
+
if (packets === null) {
|
|
716
|
+
yield* finalize();
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (packets instanceof Packet) {
|
|
720
|
+
yield* processPacket(packets);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
for (const packet_2 of packets) {
|
|
724
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
725
|
+
try {
|
|
726
|
+
const packet = __addDisposableResource(env_2, packet_2, false);
|
|
727
|
+
if (packet === null) {
|
|
728
|
+
yield* finalize();
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
yield* processPacket(packet);
|
|
732
|
+
}
|
|
733
|
+
catch (e_2) {
|
|
734
|
+
env_2.error = e_2;
|
|
735
|
+
env_2.hasError = true;
|
|
736
|
+
}
|
|
737
|
+
finally {
|
|
738
|
+
__disposeResources(env_2);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Flush filter and signal end-of-stream.
|
|
744
|
+
*
|
|
745
|
+
* Sends null packet to filter to signal end-of-stream.
|
|
746
|
+
* Does nothing if filter is closed.
|
|
747
|
+
* Must call receive() or flushPackets() to get remaining buffered packets.
|
|
748
|
+
* Does not reset filter state - use {@link reset} for that.
|
|
749
|
+
*
|
|
750
|
+
* Direct mapping to av_bsf_send_packet(NULL).
|
|
751
|
+
*
|
|
752
|
+
* @throws {FFmpegError} If flush fails
|
|
753
|
+
*
|
|
754
|
+
* @example
|
|
755
|
+
* ```typescript
|
|
756
|
+
* // Signal end of stream
|
|
757
|
+
* await filter.flush();
|
|
758
|
+
*
|
|
759
|
+
* // Then get remaining packets
|
|
760
|
+
* let packet;
|
|
761
|
+
* while ((packet = await filter.receive()) !== null) {
|
|
762
|
+
* console.log('Got buffered packet');
|
|
763
|
+
* await output.writePacket(packet);
|
|
764
|
+
* packet.free();
|
|
765
|
+
* }
|
|
766
|
+
* ```
|
|
767
|
+
*
|
|
768
|
+
* @see {@link flushPackets} For async iteration
|
|
769
|
+
* @see {@link receive} For getting buffered packets
|
|
770
|
+
* @see {@link reset} For state reset only
|
|
771
|
+
* @see {@link flushSync} For synchronous version
|
|
772
|
+
*/
|
|
773
|
+
async flush() {
|
|
774
|
+
this.signal?.throwIfAborted();
|
|
775
|
+
if (this.isClosed || !this.initialized) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
// Send EOF only - resetting via av_bsf_flush() here would discard the
|
|
779
|
+
// packets that buffering filters (vp9_superframe, setts, ...) still hold.
|
|
780
|
+
await this.sendPacketWithRetry(null);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Flush filter and signal end-of-stream synchronously.
|
|
784
|
+
* Synchronous version of flush.
|
|
785
|
+
*
|
|
786
|
+
* Sends null packet to filter to signal end-of-stream.
|
|
787
|
+
* Does nothing if filter is closed.
|
|
788
|
+
* Must call receiveSync() or flushPacketsSync() to get remaining buffered packets.
|
|
789
|
+
* Does not reset filter state - use {@link reset} for that.
|
|
790
|
+
*
|
|
791
|
+
* Direct mapping to av_bsf_send_packet(NULL).
|
|
792
|
+
*
|
|
793
|
+
* @throws {FFmpegError} If flush fails
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
* ```typescript
|
|
797
|
+
* // Signal end of stream
|
|
798
|
+
* filter.flushSync();
|
|
799
|
+
*
|
|
800
|
+
* // Then get remaining packets
|
|
801
|
+
* let packet;
|
|
802
|
+
* while ((packet = filter.receiveSync()) !== null) {
|
|
803
|
+
* console.log('Got buffered packet');
|
|
804
|
+
* output.writePacketSync(packet);
|
|
805
|
+
* packet.free();
|
|
806
|
+
* }
|
|
807
|
+
* ```
|
|
808
|
+
*
|
|
809
|
+
* @see {@link flushPacketsSync} For sync iteration
|
|
810
|
+
* @see {@link receiveSync} For getting buffered packets
|
|
811
|
+
* @see {@link reset} For state reset only
|
|
812
|
+
* @see {@link flush} For async version
|
|
813
|
+
*/
|
|
814
|
+
flushSync() {
|
|
815
|
+
if (this.isClosed || !this.initialized) {
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
// See flush(): send EOF only, never av_bsf_flush()
|
|
819
|
+
this.sendPacketWithRetrySync(null);
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Receive packet from filter.
|
|
823
|
+
*
|
|
824
|
+
* Gets filtered packets from the filter's internal buffer.
|
|
825
|
+
* Handles packet allocation and error checking.
|
|
826
|
+
* Returns null if filter is closed or no packets available.
|
|
827
|
+
* Call repeatedly until null to drain all buffered packets.
|
|
828
|
+
*
|
|
829
|
+
* Direct mapping to av_bsf_receive_packet().
|
|
830
|
+
*
|
|
831
|
+
* @returns Cloned packet or null if no packets available
|
|
832
|
+
*
|
|
833
|
+
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
834
|
+
*
|
|
835
|
+
* @throws {Error} If packet cloning fails (out of memory)
|
|
836
|
+
*
|
|
837
|
+
* @example
|
|
838
|
+
* ```typescript
|
|
839
|
+
* const packet = await filter.receive();
|
|
840
|
+
* if (packet) {
|
|
841
|
+
* console.log('Got filtered packet');
|
|
842
|
+
* await output.writePacket(packet);
|
|
843
|
+
* packet.free();
|
|
844
|
+
* }
|
|
845
|
+
* ```
|
|
846
|
+
*
|
|
847
|
+
* @example
|
|
848
|
+
* ```typescript
|
|
849
|
+
* // Drain all buffered packets
|
|
850
|
+
* let packet;
|
|
851
|
+
* while ((packet = await filter.receive()) !== null) {
|
|
852
|
+
* console.log(`Packet PTS: ${packet.pts}`);
|
|
853
|
+
* await output.writePacket(packet);
|
|
854
|
+
* packet.free();
|
|
855
|
+
* }
|
|
856
|
+
* ```
|
|
857
|
+
*
|
|
858
|
+
* @see {@link filter} For filtering packets
|
|
859
|
+
* @see {@link flush} For signaling end-of-stream
|
|
860
|
+
* @see {@link receiveSync} For synchronous version
|
|
861
|
+
*/
|
|
862
|
+
async receive() {
|
|
863
|
+
if (this.isClosed || !this.initialized) {
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
// Deliver output buffered by an EAGAIN-triggered drain first (FIFO order)
|
|
867
|
+
const pending = this.pendingOutput.shift();
|
|
868
|
+
if (pending) {
|
|
869
|
+
return pending;
|
|
870
|
+
}
|
|
871
|
+
return await this.receiveFromContext();
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Receive packet from filter synchronously.
|
|
875
|
+
* Synchronous version of receive.
|
|
876
|
+
*
|
|
877
|
+
* Gets filtered packets from the filter's internal buffer.
|
|
878
|
+
* Handles packet allocation and error checking.
|
|
879
|
+
* Returns null if filter is closed or no packets available.
|
|
880
|
+
* Call repeatedly until null to drain all buffered packets.
|
|
881
|
+
*
|
|
882
|
+
* Direct mapping to av_bsf_receive_packet().
|
|
883
|
+
*
|
|
884
|
+
* @returns Cloned packet or null if no packets available
|
|
885
|
+
*
|
|
886
|
+
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
887
|
+
*
|
|
888
|
+
* @throws {Error} If packet cloning fails (out of memory)
|
|
889
|
+
*
|
|
890
|
+
* @example
|
|
891
|
+
* ```typescript
|
|
892
|
+
* const packet = filter.receiveSync();
|
|
893
|
+
* if (packet) {
|
|
894
|
+
* console.log('Got filtered packet');
|
|
895
|
+
* output.writePacketSync(packet);
|
|
896
|
+
* packet.free();
|
|
897
|
+
* }
|
|
898
|
+
* ```
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* ```typescript
|
|
902
|
+
* // Drain all buffered packets
|
|
903
|
+
* let packet;
|
|
904
|
+
* while ((packet = filter.receiveSync()) !== null) {
|
|
905
|
+
* console.log(`Packet PTS: ${packet.pts}`);
|
|
906
|
+
* output.writePacketSync(packet);
|
|
907
|
+
* packet.free();
|
|
908
|
+
* }
|
|
909
|
+
* ```
|
|
910
|
+
*
|
|
911
|
+
* @see {@link filterSync} For filtering packets
|
|
912
|
+
* @see {@link flushSync} For signaling end-of-stream
|
|
913
|
+
* @see {@link receive} For async version
|
|
914
|
+
*/
|
|
915
|
+
receiveSync() {
|
|
916
|
+
if (this.isClosed || !this.initialized) {
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
// See receive(): pending output first
|
|
920
|
+
const pending = this.pendingOutput.shift();
|
|
921
|
+
if (pending) {
|
|
922
|
+
return pending;
|
|
923
|
+
}
|
|
924
|
+
return this.receiveFromContextSync();
|
|
925
|
+
}
|
|
926
|
+
pipeTo(target, streamIndex) {
|
|
927
|
+
if (target instanceof Muxer) {
|
|
928
|
+
// Start worker if not already running
|
|
929
|
+
this.workerPromise ??= this.runWorker();
|
|
930
|
+
// Start pipe task: filter.outputQueue -> output
|
|
931
|
+
this.pipeToPromise = (async () => {
|
|
932
|
+
while (true) {
|
|
933
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
934
|
+
try {
|
|
935
|
+
// writePacket clones internally, so the queue-owned packet must be
|
|
936
|
+
// freed here - `using` also covers the write throwing
|
|
937
|
+
const packet = __addDisposableResource(env_3, await this.receiveFromQueue(), false);
|
|
938
|
+
if (!packet)
|
|
939
|
+
break;
|
|
940
|
+
await target.writePacket(packet, streamIndex);
|
|
941
|
+
}
|
|
942
|
+
catch (e_3) {
|
|
943
|
+
env_3.error = e_3;
|
|
944
|
+
env_3.hasError = true;
|
|
945
|
+
}
|
|
946
|
+
finally {
|
|
947
|
+
__disposeResources(env_3);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
})();
|
|
951
|
+
// Return control without pipeTo (terminal stage)
|
|
952
|
+
return new SchedulerControl(this);
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
// BitStreamFilterAPI
|
|
956
|
+
const t = target;
|
|
957
|
+
// Store reference to next component for flush propagation
|
|
958
|
+
this.nextComponent = t;
|
|
959
|
+
// Start worker if not already running
|
|
960
|
+
this.workerPromise ??= this.runWorker();
|
|
961
|
+
// Start pipe task: filter.outputQueue -> target.inputQueue (via target.send)
|
|
962
|
+
this.pipeToPromise = (async () => {
|
|
963
|
+
while (true) {
|
|
964
|
+
const packet = await this.receiveFromQueue();
|
|
965
|
+
if (!packet)
|
|
966
|
+
break;
|
|
967
|
+
await t.sendToQueue(packet);
|
|
968
|
+
}
|
|
969
|
+
})();
|
|
970
|
+
// Return scheduler for chaining (target is now the last component)
|
|
971
|
+
return new Scheduler(this, t);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Flush all buffered packets as async generator.
|
|
976
|
+
*
|
|
977
|
+
* Convenient async iteration over remaining packets.
|
|
978
|
+
* Automatically sends flush signal and retrieves buffered packets.
|
|
979
|
+
* Useful for end-of-stream processing.
|
|
980
|
+
*
|
|
981
|
+
* @yields {Packet} Buffered packets
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* // Flush at end of filtering
|
|
986
|
+
* for await (const packet of filter.flushPackets()) {
|
|
987
|
+
* console.log('Processing buffered packet');
|
|
988
|
+
* await output.writePacket(packet);
|
|
989
|
+
* packet.free();
|
|
990
|
+
* }
|
|
991
|
+
* ```
|
|
992
|
+
*
|
|
993
|
+
* @see {@link filter} For filtering packets
|
|
994
|
+
* @see {@link flush} For signaling end-of-stream
|
|
995
|
+
* @see {@link flushPacketsSync} For synchronous version
|
|
996
|
+
*/
|
|
997
|
+
async *flushPackets() {
|
|
998
|
+
// Signal EOF first - buffering filters only release their queued packets
|
|
999
|
+
// after av_bsf_send_packet(NULL)
|
|
1000
|
+
await this.flush();
|
|
1001
|
+
while (true) {
|
|
1002
|
+
const packet = await this.receive();
|
|
1003
|
+
if (!packet)
|
|
1004
|
+
break;
|
|
1005
|
+
yield packet;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Flush all buffered packets as generator synchronously.
|
|
1010
|
+
* Synchronous version of flushPackets.
|
|
1011
|
+
*
|
|
1012
|
+
* Convenient sync iteration over remaining packets.
|
|
1013
|
+
* Automatically sends flush signal and retrieves buffered packets.
|
|
1014
|
+
* Useful for end-of-stream processing.
|
|
1015
|
+
*
|
|
1016
|
+
* @yields {Packet} Buffered packets
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```typescript
|
|
1020
|
+
* // Flush at end of filtering
|
|
1021
|
+
* for (const packet of filter.flushPacketsSync()) {
|
|
1022
|
+
* console.log('Processing buffered packet');
|
|
1023
|
+
* output.writePacketSync(packet);
|
|
1024
|
+
* packet.free();
|
|
1025
|
+
* }
|
|
1026
|
+
* ```
|
|
1027
|
+
*
|
|
1028
|
+
* @see {@link filterSync} For filtering packets
|
|
1029
|
+
* @see {@link flushSync} For signaling end-of-stream
|
|
1030
|
+
* @see {@link flushPackets} For async version
|
|
1031
|
+
*/
|
|
1032
|
+
*flushPacketsSync() {
|
|
1033
|
+
// See flushPackets(): EOF first, then drain
|
|
1034
|
+
this.flushSync();
|
|
1035
|
+
while (true) {
|
|
1036
|
+
const packet = this.receiveSync();
|
|
1037
|
+
if (!packet)
|
|
1038
|
+
break;
|
|
1039
|
+
yield packet;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Reset filter state.
|
|
1044
|
+
*
|
|
1045
|
+
* Clears internal buffers and resets filter.
|
|
1046
|
+
* Does not dispose resources.
|
|
1047
|
+
*
|
|
1048
|
+
* Direct mapping to av_bsf_flush().
|
|
1049
|
+
*
|
|
1050
|
+
* @example
|
|
1051
|
+
* ```typescript
|
|
1052
|
+
* // Reset for new segment
|
|
1053
|
+
* filter.reset();
|
|
1054
|
+
* ```
|
|
1055
|
+
*
|
|
1056
|
+
* @see {@link flush} For end-of-stream signaling with packet retrieval
|
|
1057
|
+
*/
|
|
1058
|
+
reset() {
|
|
1059
|
+
if (this.isClosed) {
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
// Drop output buffered from an EAGAIN drain - it belongs to the old segment
|
|
1063
|
+
this.clearPendingOutput();
|
|
1064
|
+
this.ctx.flush();
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Close filter and free resources.
|
|
1068
|
+
*
|
|
1069
|
+
* Releases filter context and marks as closed.
|
|
1070
|
+
* Safe to call multiple times.
|
|
1071
|
+
*
|
|
1072
|
+
* @example
|
|
1073
|
+
* ```typescript
|
|
1074
|
+
* filter.close();
|
|
1075
|
+
* ```
|
|
1076
|
+
*
|
|
1077
|
+
* @see {@link Symbol.dispose} For automatic cleanup
|
|
1078
|
+
*/
|
|
1079
|
+
close() {
|
|
1080
|
+
if (this.isClosed) {
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
this.isClosed = true;
|
|
1084
|
+
this.inputQueue.close();
|
|
1085
|
+
this.outputQueue.close();
|
|
1086
|
+
this.inputQueue.clear();
|
|
1087
|
+
this.outputQueue.clear();
|
|
1088
|
+
this.clearPendingOutput();
|
|
1089
|
+
this.packet.free();
|
|
1090
|
+
this.ctx.free();
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Dispose of filter.
|
|
1094
|
+
*
|
|
1095
|
+
* Implements Disposable interface for automatic cleanup.
|
|
1096
|
+
* Equivalent to calling dispose().
|
|
1097
|
+
*
|
|
1098
|
+
* @example
|
|
1099
|
+
* ```typescript
|
|
1100
|
+
* {
|
|
1101
|
+
* using filter = BitStreamFilterAPI.create('h264_mp4toannexb', stream);
|
|
1102
|
+
* // Use filter...
|
|
1103
|
+
* } // Automatically disposed
|
|
1104
|
+
* ```
|
|
1105
|
+
*
|
|
1106
|
+
* @see {@link close} For manual cleanup
|
|
1107
|
+
*/
|
|
1108
|
+
[Symbol.dispose]() {
|
|
1109
|
+
this.close();
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Bind input parameters from the source and initialize the filter.
|
|
1113
|
+
*
|
|
1114
|
+
* @internal
|
|
1115
|
+
*/
|
|
1116
|
+
ensureInitialized() {
|
|
1117
|
+
if (this.initialized) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const params = this.ctx.inputCodecParameters;
|
|
1121
|
+
if (!params) {
|
|
1122
|
+
throw new Error('Failed to get input codec parameters from filter context');
|
|
1123
|
+
}
|
|
1124
|
+
const source = this.source;
|
|
1125
|
+
if (source instanceof Stream) {
|
|
1126
|
+
source.codecpar.copy(params);
|
|
1127
|
+
this.ctx.inputTimeBase = source.timeBase;
|
|
1128
|
+
}
|
|
1129
|
+
else if (source instanceof Encoder) {
|
|
1130
|
+
const codecContext = source.getCodecContext();
|
|
1131
|
+
if (!codecContext) {
|
|
1132
|
+
throw new Error('Encoder is not initialized yet; cannot derive bitstream filter parameters.');
|
|
1133
|
+
}
|
|
1134
|
+
const ret = params.fromContext(codecContext);
|
|
1135
|
+
FFmpegError.throwIfError(ret, 'Failed to derive bitstream filter parameters from encoder');
|
|
1136
|
+
this.ctx.inputTimeBase = codecContext.timeBase;
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
// Upstream is another bitstream filter: chain from its output.
|
|
1140
|
+
source.ensureInitialized();
|
|
1141
|
+
const upstream = source.outputCodecParameters;
|
|
1142
|
+
if (!upstream) {
|
|
1143
|
+
throw new Error('Upstream bitstream filter has no output parameters.');
|
|
1144
|
+
}
|
|
1145
|
+
upstream.copy(params);
|
|
1146
|
+
const tb = source.outputTimeBase;
|
|
1147
|
+
if (tb) {
|
|
1148
|
+
this.ctx.inputTimeBase = tb;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
const initRet = this.ctx.init();
|
|
1152
|
+
FFmpegError.throwIfError(initRet, 'Failed to initialize bitstream filter');
|
|
1153
|
+
this.initialized = true;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Send a packet to the filter context, retrying once on EAGAIN.
|
|
1157
|
+
*
|
|
1158
|
+
* av_bsf_send_packet() returning EAGAIN means the packet was NOT consumed:
|
|
1159
|
+
* the filter holds output that must be received before it accepts more
|
|
1160
|
+
* input. That output is drained into pendingOutput (delivered by subsequent
|
|
1161
|
+
* receive() calls in FIFO order) and the send is retried, mirroring how the
|
|
1162
|
+
* FFmpeg CLI interleaves send/receive.
|
|
1163
|
+
*
|
|
1164
|
+
* @param packet - Packet to send, or null to signal EOF
|
|
1165
|
+
*
|
|
1166
|
+
* @throws {FFmpegError} If sending fails
|
|
1167
|
+
*
|
|
1168
|
+
* @internal
|
|
1169
|
+
*/
|
|
1170
|
+
async sendPacketWithRetry(packet) {
|
|
1171
|
+
let sendRet = await this.ctx.sendPacket(packet);
|
|
1172
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
1173
|
+
while (true) {
|
|
1174
|
+
const outPacket = await this.receiveFromContext();
|
|
1175
|
+
if (!outPacket)
|
|
1176
|
+
break;
|
|
1177
|
+
this.pendingOutput.push(outPacket);
|
|
1178
|
+
}
|
|
1179
|
+
sendRet = await this.ctx.sendPacket(packet);
|
|
1180
|
+
}
|
|
1181
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1182
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Send a packet to the filter context, retrying once on EAGAIN.
|
|
1187
|
+
* Synchronous version of sendPacketWithRetry.
|
|
1188
|
+
*
|
|
1189
|
+
* @param packet - Packet to send, or null to signal EOF
|
|
1190
|
+
*
|
|
1191
|
+
* @throws {FFmpegError} If sending fails
|
|
1192
|
+
*
|
|
1193
|
+
* @internal
|
|
1194
|
+
*/
|
|
1195
|
+
sendPacketWithRetrySync(packet) {
|
|
1196
|
+
let sendRet = this.ctx.sendPacketSync(packet);
|
|
1197
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
1198
|
+
while (true) {
|
|
1199
|
+
const outPacket = this.receiveFromContextSync();
|
|
1200
|
+
if (!outPacket)
|
|
1201
|
+
break;
|
|
1202
|
+
this.pendingOutput.push(outPacket);
|
|
1203
|
+
}
|
|
1204
|
+
sendRet = this.ctx.sendPacketSync(packet);
|
|
1205
|
+
}
|
|
1206
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1207
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Receive one packet directly from the filter context.
|
|
1212
|
+
*
|
|
1213
|
+
* Bypasses pendingOutput so an EAGAIN drain can buffer packets without
|
|
1214
|
+
* re-reading them.
|
|
1215
|
+
*
|
|
1216
|
+
* @returns Cloned packet or null if no packets available
|
|
1217
|
+
*
|
|
1218
|
+
* @throws {FFmpegError} If receive fails
|
|
1219
|
+
*
|
|
1220
|
+
* @throws {Error} If packet cloning fails (out of memory)
|
|
1221
|
+
*
|
|
1222
|
+
* @internal
|
|
1223
|
+
*/
|
|
1224
|
+
async receiveFromContext() {
|
|
1225
|
+
// Clear previous packet data
|
|
1226
|
+
this.packet.unref();
|
|
1227
|
+
const recvRet = await this.ctx.receivePacket(this.packet);
|
|
1228
|
+
return this.consumeReceived(recvRet);
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Receive one packet directly from the filter context.
|
|
1232
|
+
* Synchronous version of receiveFromContext.
|
|
1233
|
+
*
|
|
1234
|
+
* @returns Cloned packet or null if no packets available
|
|
1235
|
+
*
|
|
1236
|
+
* @throws {FFmpegError} If receive fails
|
|
1237
|
+
*
|
|
1238
|
+
* @throws {Error} If packet cloning fails (out of memory)
|
|
1239
|
+
*
|
|
1240
|
+
* @internal
|
|
1241
|
+
*/
|
|
1242
|
+
receiveFromContextSync() {
|
|
1243
|
+
// Clear previous packet data
|
|
1244
|
+
this.packet.unref();
|
|
1245
|
+
const recvRet = this.ctx.receivePacketSync(this.packet);
|
|
1246
|
+
return this.consumeReceived(recvRet);
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Convert a receive return code into a cloned output packet.
|
|
1250
|
+
*
|
|
1251
|
+
* @param recvRet - Return code of av_bsf_receive_packet()
|
|
1252
|
+
*
|
|
1253
|
+
* @returns Cloned packet, or null on EAGAIN/EOF
|
|
1254
|
+
*
|
|
1255
|
+
* @throws {FFmpegError} If the return code is any other error
|
|
1256
|
+
*
|
|
1257
|
+
* @throws {Error} If packet cloning fails (out of memory)
|
|
1258
|
+
*
|
|
1259
|
+
* @internal
|
|
1260
|
+
*/
|
|
1261
|
+
consumeReceived(recvRet) {
|
|
1262
|
+
if (recvRet === 0) {
|
|
1263
|
+
// Filters like aac_adtstoasc deliver new global headers via packet side
|
|
1264
|
+
// data rather than par_out; fold it into the output parameters so callers
|
|
1265
|
+
// (and Muxer's bsf integration) can read it from outputCodecParameters.
|
|
1266
|
+
this.foldNewExtradata(this.packet);
|
|
1267
|
+
// Got a packet, clone it for the user
|
|
1268
|
+
const cloned = this.packet.clone();
|
|
1269
|
+
if (!cloned) {
|
|
1270
|
+
throw new Error('Failed to clone packet (out of memory)');
|
|
1271
|
+
}
|
|
1272
|
+
return cloned;
|
|
1273
|
+
}
|
|
1274
|
+
else if (recvRet === AVERROR_EAGAIN || recvRet === AVERROR_EOF) {
|
|
1275
|
+
// Need more data or end of stream
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
// Error
|
|
1280
|
+
FFmpegError.throwIfError(recvRet, 'Failed to receive packet from bitstream filter');
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Free and clear any packets buffered by an EAGAIN drain.
|
|
1286
|
+
*
|
|
1287
|
+
* @internal
|
|
1288
|
+
*/
|
|
1289
|
+
clearPendingOutput() {
|
|
1290
|
+
for (const packet of this.pendingOutput) {
|
|
1291
|
+
packet.free();
|
|
1292
|
+
}
|
|
1293
|
+
this.pendingOutput = [];
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Send packet to input queue or flush the pipeline.
|
|
1297
|
+
*
|
|
1298
|
+
* When packet is provided, queues it for filtering.
|
|
1299
|
+
* When null is provided, triggers flush sequence:
|
|
1300
|
+
* - Closes input queue
|
|
1301
|
+
* - Waits for worker completion
|
|
1302
|
+
* - Closes output queue (no buffering, bitstream filters are stateless)
|
|
1303
|
+
* - Waits for pipeTo task completion
|
|
1304
|
+
* - Propagates flush to next component (if any)
|
|
1305
|
+
*
|
|
1306
|
+
* Used by scheduler system for pipeline control.
|
|
1307
|
+
*
|
|
1308
|
+
* @param packet - Packet to send, or null to flush
|
|
1309
|
+
*
|
|
1310
|
+
* @internal
|
|
1311
|
+
*/
|
|
1312
|
+
async sendToQueue(packet) {
|
|
1313
|
+
if (packet) {
|
|
1314
|
+
await this.inputQueue.send(packet);
|
|
1315
|
+
}
|
|
1316
|
+
else {
|
|
1317
|
+
// Close input queue to signal end of stream to worker
|
|
1318
|
+
this.inputQueue.close();
|
|
1319
|
+
// Wait for worker to finish processing all packets
|
|
1320
|
+
if (this.workerPromise) {
|
|
1321
|
+
await this.workerPromise;
|
|
1322
|
+
}
|
|
1323
|
+
// Close output queue to signal end of stream to pipeTo() task
|
|
1324
|
+
this.outputQueue.close();
|
|
1325
|
+
// Wait for pipeTo() task to finish processing all packets (if exists)
|
|
1326
|
+
if (this.pipeToPromise) {
|
|
1327
|
+
await this.pipeToPromise;
|
|
1328
|
+
}
|
|
1329
|
+
// Then propagate flush to next component
|
|
1330
|
+
if (this.nextComponent) {
|
|
1331
|
+
await this.nextComponent.sendToQueue(null);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Receive packet from output queue.
|
|
1337
|
+
*
|
|
1338
|
+
* @returns Packet from queue or null if closed
|
|
1339
|
+
*
|
|
1340
|
+
* @internal
|
|
1341
|
+
*/
|
|
1342
|
+
async receiveFromQueue() {
|
|
1343
|
+
return await this.outputQueue.receive();
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Worker loop for push-based processing.
|
|
1347
|
+
*
|
|
1348
|
+
* @internal
|
|
1349
|
+
*/
|
|
1350
|
+
async runWorker() {
|
|
1351
|
+
try {
|
|
1352
|
+
// Outer loop - receive packets
|
|
1353
|
+
while (!this.inputQueue.isClosed) {
|
|
1354
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1355
|
+
try {
|
|
1356
|
+
const packet = __addDisposableResource(env_4, await this.inputQueue.receive(), false);
|
|
1357
|
+
if (!packet)
|
|
1358
|
+
break;
|
|
1359
|
+
if (this.isClosed) {
|
|
1360
|
+
break;
|
|
1361
|
+
}
|
|
1362
|
+
this.ensureInitialized();
|
|
1363
|
+
// Send packet to filter; on EAGAIN the pending output is buffered
|
|
1364
|
+
// internally and delivered by the receive() calls below
|
|
1365
|
+
await this.sendPacketWithRetry(packet);
|
|
1366
|
+
// Receive ALL available packets immediately
|
|
1367
|
+
while (!this.outputQueue.isClosed) {
|
|
1368
|
+
const outPacket = await this.receive();
|
|
1369
|
+
if (!outPacket)
|
|
1370
|
+
break;
|
|
1371
|
+
await this.outputQueue.send(outPacket);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
catch (e_4) {
|
|
1375
|
+
env_4.error = e_4;
|
|
1376
|
+
env_4.hasError = true;
|
|
1377
|
+
}
|
|
1378
|
+
finally {
|
|
1379
|
+
__disposeResources(env_4);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
// Flush filter at end
|
|
1383
|
+
await this.flush();
|
|
1384
|
+
while (!this.outputQueue.isClosed) {
|
|
1385
|
+
const outPacket = await this.receive();
|
|
1386
|
+
if (!outPacket)
|
|
1387
|
+
break;
|
|
1388
|
+
await this.outputQueue.send(outPacket);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
// Propagate error to both queues so upstream and downstream know
|
|
1393
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1394
|
+
this.inputQueue?.closeWithError(err);
|
|
1395
|
+
this.outputQueue?.closeWithError(err);
|
|
1396
|
+
}
|
|
1397
|
+
finally {
|
|
1398
|
+
// Close output queue when done (if not already closed with error)
|
|
1399
|
+
this.outputQueue?.close();
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Fold a filtered packet's new-extradata side data into the output parameters.
|
|
1404
|
+
*
|
|
1405
|
+
* Bitstream filters that adapt a stream's container framing — `aac_adtstoasc`,
|
|
1406
|
+
* `extract_extradata`, `dump_extra`, `*_mp4toannexb` — emit the resulting global
|
|
1407
|
+
* headers as {@link AV_PKT_DATA_NEW_EXTRADATA} side data on their output packets
|
|
1408
|
+
* rather than on `par_out`, matching FFmpeg's mid-stream extradata model. Without
|
|
1409
|
+
* this, {@link outputCodecParameters} would never reflect that extradata, so a
|
|
1410
|
+
* stream copy that relies on it (including {@link Muxer}'s `bsf` option) emits a
|
|
1411
|
+
* header with no codec configuration. Lifting it onto `par_out` keeps
|
|
1412
|
+
* `outputCodecParameters` current while leaving the side data on the packet too.
|
|
1413
|
+
*
|
|
1414
|
+
* @param packet - A freshly received filtered packet.
|
|
1415
|
+
*
|
|
1416
|
+
* @internal
|
|
1417
|
+
*/
|
|
1418
|
+
foldNewExtradata(packet) {
|
|
1419
|
+
const extradata = packet.getSideData(AV_PKT_DATA_NEW_EXTRADATA);
|
|
1420
|
+
if (!extradata)
|
|
1421
|
+
return;
|
|
1422
|
+
const params = this.ctx.outputCodecParameters;
|
|
1423
|
+
if (params)
|
|
1424
|
+
params.extradata = extradata;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
//# sourceMappingURL=bitstream-filter.js.map
|