node-av 3.1.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -52
- package/binding.gyp +4 -0
- package/dist/api/audio-frame-buffer.d.ts +201 -0
- package/dist/api/audio-frame-buffer.js +275 -0
- package/dist/api/audio-frame-buffer.js.map +1 -0
- package/dist/api/bitstream-filter.d.ts +319 -78
- package/dist/api/bitstream-filter.js +680 -151
- package/dist/api/bitstream-filter.js.map +1 -1
- 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_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 +279 -17
- package/dist/api/decoder.js +998 -209
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/{media-input.d.ts → demuxer.d.ts} +294 -44
- package/dist/api/demuxer.js +1968 -0
- package/dist/api/demuxer.js.map +1 -0
- package/dist/api/encoder.d.ts +308 -50
- package/dist/api/encoder.js +1133 -111
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-presets.d.ts +12 -5
- package/dist/api/filter-presets.js +21 -7
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.d.ts +406 -40
- package/dist/api/filter.js +966 -139
- package/dist/api/filter.js.map +1 -1
- package/dist/api/{fmp4.d.ts → fmp4-stream.d.ts} +141 -140
- package/dist/api/fmp4-stream.js +539 -0
- package/dist/api/fmp4-stream.js.map +1 -0
- package/dist/api/hardware.d.ts +58 -6
- package/dist/api/hardware.js +127 -11
- package/dist/api/hardware.js.map +1 -1
- package/dist/api/index.d.ts +6 -4
- package/dist/api/index.js +14 -8
- package/dist/api/index.js.map +1 -1
- package/dist/api/io-stream.d.ts +3 -3
- package/dist/api/io-stream.js +5 -4
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/{media-output.d.ts → muxer.d.ts} +274 -60
- package/dist/api/muxer.js +1934 -0
- package/dist/api/muxer.js.map +1 -0
- package/dist/api/pipeline.d.ts +77 -29
- package/dist/api/pipeline.js +435 -425
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/rtp-stream.d.ts +312 -0
- package/dist/api/rtp-stream.js +630 -0
- package/dist/api/rtp-stream.js.map +1 -0
- package/dist/api/types.d.ts +476 -55
- package/dist/api/utilities/async-queue.d.ts +91 -0
- package/dist/api/utilities/async-queue.js +162 -0
- package/dist/api/utilities/async-queue.js.map +1 -0
- package/dist/api/utilities/audio-sample.d.ts +1 -1
- package/dist/api/utilities/image.d.ts +1 -1
- package/dist/api/utilities/index.d.ts +2 -0
- package/dist/api/utilities/index.js +4 -0
- package/dist/api/utilities/index.js.map +1 -1
- package/dist/api/utilities/media-type.d.ts +1 -1
- package/dist/api/utilities/pixel-format.d.ts +1 -1
- package/dist/api/utilities/sample-format.d.ts +1 -1
- package/dist/api/utilities/scheduler.d.ts +169 -0
- package/dist/api/utilities/scheduler.js +136 -0
- package/dist/api/utilities/scheduler.js.map +1 -0
- package/dist/api/utilities/streaming.d.ts +74 -15
- package/dist/api/utilities/streaming.js +170 -12
- package/dist/api/utilities/streaming.js.map +1 -1
- package/dist/api/utilities/timestamp.d.ts +1 -1
- package/dist/api/webrtc-stream.d.ts +288 -0
- package/dist/api/webrtc-stream.js +440 -0
- package/dist/api/webrtc-stream.js.map +1 -0
- package/dist/constants/constants.d.ts +51 -1
- package/dist/constants/constants.js +47 -1
- package/dist/constants/constants.js.map +1 -1
- package/dist/constants/encoders.d.ts +2 -1
- package/dist/constants/encoders.js +4 -3
- package/dist/constants/encoders.js.map +1 -1
- 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 +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/lib/binding.d.ts +19 -8
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/codec-context.d.ts +87 -0
- package/dist/lib/codec-context.js +125 -4
- package/dist/lib/codec-context.js.map +1 -1
- package/dist/lib/codec-parameters.d.ts +183 -1
- package/dist/lib/codec-parameters.js +209 -0
- package/dist/lib/codec-parameters.js.map +1 -1
- package/dist/lib/codec-parser.d.ts +23 -0
- package/dist/lib/codec-parser.js +25 -0
- package/dist/lib/codec-parser.js.map +1 -1
- package/dist/lib/codec.d.ts +26 -4
- package/dist/lib/codec.js +35 -0
- package/dist/lib/codec.js.map +1 -1
- package/dist/lib/dictionary.js +1 -0
- package/dist/lib/dictionary.js.map +1 -1
- package/dist/lib/error.js +1 -1
- package/dist/lib/error.js.map +1 -1
- package/dist/lib/filter-context.d.ts +52 -11
- package/dist/lib/filter-context.js +56 -12
- package/dist/lib/filter-context.js.map +1 -1
- package/dist/lib/filter-graph.d.ts +9 -0
- package/dist/lib/filter-graph.js +13 -0
- package/dist/lib/filter-graph.js.map +1 -1
- package/dist/lib/filter.d.ts +21 -0
- package/dist/lib/filter.js +28 -0
- package/dist/lib/filter.js.map +1 -1
- package/dist/lib/format-context.d.ts +48 -14
- package/dist/lib/format-context.js +76 -7
- package/dist/lib/format-context.js.map +1 -1
- package/dist/lib/frame.d.ts +168 -0
- package/dist/lib/frame.js +212 -0
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/hardware-device-context.d.ts +3 -2
- package/dist/lib/hardware-device-context.js.map +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/input-format.d.ts +21 -0
- package/dist/lib/input-format.js +42 -2
- package/dist/lib/input-format.js.map +1 -1
- package/dist/lib/native-types.d.ts +48 -26
- package/dist/lib/option.d.ts +25 -13
- package/dist/lib/option.js +28 -0
- package/dist/lib/option.js.map +1 -1
- package/dist/lib/output-format.d.ts +22 -1
- package/dist/lib/output-format.js +28 -0
- package/dist/lib/output-format.js.map +1 -1
- package/dist/lib/packet.d.ts +35 -0
- package/dist/lib/packet.js +52 -2
- package/dist/lib/packet.js.map +1 -1
- package/dist/lib/stream.d.ts +126 -0
- package/dist/lib/stream.js +188 -5
- package/dist/lib/stream.js.map +1 -1
- 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 +27 -1
- package/dist/lib/utilities.d.ts +281 -53
- package/dist/lib/utilities.js +298 -55
- package/dist/lib/utilities.js.map +1 -1
- package/package.json +20 -19
- package/dist/api/fmp4.js +0 -710
- package/dist/api/fmp4.js.map +0 -1
- package/dist/api/media-input.js +0 -1075
- package/dist/api/media-input.js.map +0 -1
- package/dist/api/media-output.js +0 -1040
- package/dist/api/media-output.js.map +0 -1
- package/dist/api/webrtc.d.ts +0 -664
- package/dist/api/webrtc.js +0 -1132
- package/dist/api/webrtc.js.map +0 -1
|
@@ -1,10 +1,70 @@
|
|
|
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
|
+
});
|
|
1
53
|
import { AVERROR_EAGAIN, AVERROR_EOF } from '../constants/constants.js';
|
|
2
|
-
import {
|
|
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 { PACKET_THREAD_QUEUE_SIZE } from './constants.js';
|
|
59
|
+
import { Muxer } from './muxer.js';
|
|
60
|
+
import { AsyncQueue } from './utilities/async-queue.js';
|
|
61
|
+
import { Scheduler, SchedulerControl } from './utilities/scheduler.js';
|
|
3
62
|
/**
|
|
4
63
|
* High-level bitstream filter for packet processing.
|
|
5
64
|
*
|
|
6
65
|
* Provides simplified interface for applying bitstream filters to packets.
|
|
7
66
|
* Handles filter initialization, packet processing, and memory management.
|
|
67
|
+
* Supports both synchronous packet-by-packet filtering and async iteration over packets.
|
|
8
68
|
* Supports filters like h264_mp4toannexb, hevc_mp4toannexb, aac_adtstoasc.
|
|
9
69
|
* Essential for format conversion and stream compatibility in transcoding pipelines.
|
|
10
70
|
*
|
|
@@ -15,8 +75,8 @@ import { BitStreamFilter, BitStreamFilterContext, FFmpegError, Packet } from '..
|
|
|
15
75
|
* // Create H.264 Annex B converter
|
|
16
76
|
* const filter = BitStreamFilterAPI.create('h264_mp4toannexb', stream);
|
|
17
77
|
*
|
|
18
|
-
* //
|
|
19
|
-
* const outputPackets = await filter.
|
|
78
|
+
* // Filter packet
|
|
79
|
+
* const outputPackets = await filter.filterAll(inputPacket);
|
|
20
80
|
* for (const packet of outputPackets) {
|
|
21
81
|
* await output.writePacket(packet);
|
|
22
82
|
* packet.free();
|
|
@@ -25,7 +85,7 @@ import { BitStreamFilter, BitStreamFilterContext, FFmpegError, Packet } from '..
|
|
|
25
85
|
*
|
|
26
86
|
* @example
|
|
27
87
|
* ```typescript
|
|
28
|
-
* //
|
|
88
|
+
* // Filter packet stream
|
|
29
89
|
* const filter = BitStreamFilterAPI.create('hevc_mp4toannexb', videoStream);
|
|
30
90
|
*
|
|
31
91
|
* for await (const packet of filter.packets(input.packets())) {
|
|
@@ -36,15 +96,22 @@ import { BitStreamFilter, BitStreamFilterContext, FFmpegError, Packet } from '..
|
|
|
36
96
|
*
|
|
37
97
|
* @see {@link BitStreamFilter} For available filters
|
|
38
98
|
* @see {@link BitStreamFilterContext} For low-level API
|
|
39
|
-
* @see {@link
|
|
99
|
+
* @see {@link Muxer} For writing filtered packets
|
|
40
100
|
*/
|
|
41
101
|
export class BitStreamFilterAPI {
|
|
42
102
|
ctx;
|
|
43
|
-
|
|
103
|
+
bsf;
|
|
44
104
|
stream;
|
|
105
|
+
packet;
|
|
45
106
|
isClosed = false;
|
|
107
|
+
// Worker pattern for push-based processing
|
|
108
|
+
inputQueue;
|
|
109
|
+
outputQueue;
|
|
110
|
+
workerPromise = null;
|
|
111
|
+
nextComponent = null;
|
|
112
|
+
pipeToPromise = null;
|
|
46
113
|
/**
|
|
47
|
-
* @param
|
|
114
|
+
* @param bsf - Bitstream filter
|
|
48
115
|
*
|
|
49
116
|
* @param ctx - Filter context
|
|
50
117
|
*
|
|
@@ -52,10 +119,14 @@ export class BitStreamFilterAPI {
|
|
|
52
119
|
*
|
|
53
120
|
* @internal
|
|
54
121
|
*/
|
|
55
|
-
constructor(
|
|
56
|
-
this.
|
|
122
|
+
constructor(bsf, ctx, stream) {
|
|
123
|
+
this.bsf = bsf;
|
|
57
124
|
this.ctx = ctx;
|
|
58
125
|
this.stream = stream;
|
|
126
|
+
this.packet = new Packet();
|
|
127
|
+
this.packet.alloc();
|
|
128
|
+
this.inputQueue = new AsyncQueue(PACKET_THREAD_QUEUE_SIZE);
|
|
129
|
+
this.outputQueue = new AsyncQueue(PACKET_THREAD_QUEUE_SIZE);
|
|
59
130
|
}
|
|
60
131
|
/**
|
|
61
132
|
* Create a bitstream filter for a stream.
|
|
@@ -136,7 +207,7 @@ export class BitStreamFilterAPI {
|
|
|
136
207
|
* ```
|
|
137
208
|
*/
|
|
138
209
|
get name() {
|
|
139
|
-
return this.
|
|
210
|
+
return this.bsf.name ?? 'unknown';
|
|
140
211
|
}
|
|
141
212
|
/**
|
|
142
213
|
* Get output codec parameters.
|
|
@@ -181,125 +252,211 @@ export class BitStreamFilterAPI {
|
|
|
181
252
|
return !this.isClosed;
|
|
182
253
|
}
|
|
183
254
|
/**
|
|
184
|
-
*
|
|
255
|
+
* Filter a packet.
|
|
256
|
+
*
|
|
257
|
+
* Sends a packet to the filter and attempts to receive a filtered packet.
|
|
258
|
+
* Handles internal buffering - may return null if more packets needed.
|
|
259
|
+
*
|
|
260
|
+
* **Note**: This method receives only ONE packet per call.
|
|
261
|
+
* A single packet can produce multiple output packets (e.g., codec buffering).
|
|
262
|
+
* To receive all packets from a packet, use {@link filterAll} or {@link packets} instead.
|
|
263
|
+
*
|
|
264
|
+
* Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
|
|
265
|
+
*
|
|
266
|
+
* @param packet - Packet to filter
|
|
267
|
+
*
|
|
268
|
+
* @returns Filtered packet, null if more data needed, or null if filter is closed
|
|
269
|
+
*
|
|
270
|
+
* @throws {FFmpegError} If filtering fails
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* const outPacket = await filter.filter(inputPacket);
|
|
275
|
+
* if (outPacket) {
|
|
276
|
+
* console.log(`Filtered packet: pts=${outPacket.pts}`);
|
|
277
|
+
* await output.writePacket(outPacket);
|
|
278
|
+
* outPacket.free();
|
|
279
|
+
* }
|
|
280
|
+
* ```
|
|
281
|
+
*
|
|
282
|
+
* @see {@link filterAll} For multiple packet filtering
|
|
283
|
+
* @see {@link packets} For stream processing
|
|
284
|
+
* @see {@link flush} For end-of-stream handling
|
|
285
|
+
* @see {@link filterSync} For synchronous version
|
|
286
|
+
*/
|
|
287
|
+
async filter(packet) {
|
|
288
|
+
if (this.isClosed) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
// Send packet to filter
|
|
292
|
+
const sendRet = await this.ctx.sendPacket(packet);
|
|
293
|
+
// Handle EAGAIN: filter buffer is full, need to read packets first
|
|
294
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
295
|
+
// Filter is full, receive a packet first
|
|
296
|
+
const outPacket = await this.receive();
|
|
297
|
+
if (outPacket) {
|
|
298
|
+
return outPacket;
|
|
299
|
+
}
|
|
300
|
+
// If receive() returned null, this is unexpected - treat as error
|
|
301
|
+
throw new Error('Filter returned EAGAIN but no packet available');
|
|
302
|
+
}
|
|
303
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
304
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
305
|
+
}
|
|
306
|
+
// Try to receive packet
|
|
307
|
+
return await this.receive();
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Filter a packet synchronously.
|
|
311
|
+
* Synchronous version of filter.
|
|
312
|
+
*
|
|
313
|
+
* Sends a packet to the filter and attempts to receive a filtered packet.
|
|
314
|
+
* Handles internal buffering - may return null if more packets needed.
|
|
185
315
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
316
|
+
* **Note**: This method receives only ONE packet per call.
|
|
317
|
+
* A single packet can produce multiple output packets (e.g., codec buffering).
|
|
318
|
+
* To receive all packets from a packet, use {@link filterAllSync} or {@link packetsSync} instead.
|
|
189
319
|
*
|
|
190
320
|
* Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
|
|
191
321
|
*
|
|
192
|
-
* @param packet - Packet to filter
|
|
322
|
+
* @param packet - Packet to filter
|
|
193
323
|
*
|
|
194
|
-
* @returns
|
|
324
|
+
* @returns Filtered packet, null if more data needed, or null if filter is closed
|
|
195
325
|
*
|
|
196
326
|
* @throws {FFmpegError} If filtering fails
|
|
197
327
|
*
|
|
198
328
|
* @example
|
|
199
329
|
* ```typescript
|
|
200
|
-
* const
|
|
201
|
-
* if (
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* }
|
|
330
|
+
* const outPacket = filter.filterSync(inputPacket);
|
|
331
|
+
* if (outPacket) {
|
|
332
|
+
* console.log(`Filtered packet: pts=${outPacket.pts}`);
|
|
333
|
+
* output.writePacketSync(outPacket);
|
|
334
|
+
* outPacket.free();
|
|
206
335
|
* }
|
|
207
336
|
* ```
|
|
208
337
|
*
|
|
338
|
+
* @see {@link filterAllSync} For multiple packet filtering
|
|
339
|
+
* @see {@link packetsSync} For stream processing
|
|
340
|
+
* @see {@link flushSync} For end-of-stream handling
|
|
341
|
+
* @see {@link filter} For async version
|
|
342
|
+
*/
|
|
343
|
+
filterSync(packet) {
|
|
344
|
+
if (this.isClosed) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
// Send packet to filter
|
|
348
|
+
const sendRet = this.ctx.sendPacketSync(packet);
|
|
349
|
+
// Handle EAGAIN: filter buffer is full, need to read packets first
|
|
350
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
351
|
+
// Filter is full, receive a packet first
|
|
352
|
+
const outPacket = this.receiveSync();
|
|
353
|
+
if (outPacket) {
|
|
354
|
+
return outPacket;
|
|
355
|
+
}
|
|
356
|
+
// If receive() returned null, this is unexpected - treat as error
|
|
357
|
+
throw new Error('Filter returned EAGAIN but no packet available');
|
|
358
|
+
}
|
|
359
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
360
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
361
|
+
}
|
|
362
|
+
// Try to receive packet
|
|
363
|
+
return this.receiveSync();
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Filter a packet to packets.
|
|
367
|
+
*
|
|
368
|
+
* Sends a packet to the filter and receives all available filtered packets.
|
|
369
|
+
* Returns array of packets - may be empty if filter needs more data.
|
|
370
|
+
* One packet can produce zero, one, or multiple packets depending on filter.
|
|
371
|
+
*
|
|
372
|
+
* Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
|
|
373
|
+
*
|
|
374
|
+
* @param packet - Packet to filter
|
|
375
|
+
*
|
|
376
|
+
* @returns Array of filtered packets (empty if more data needed or filter is closed)
|
|
377
|
+
*
|
|
378
|
+
* @throws {FFmpegError} If filtering fails
|
|
379
|
+
*
|
|
209
380
|
* @example
|
|
210
381
|
* ```typescript
|
|
211
|
-
*
|
|
212
|
-
* const
|
|
382
|
+
* const outputPackets = await filter.filterAll(inputPacket);
|
|
383
|
+
* for (const packet of outputPackets) {
|
|
384
|
+
* console.log(`Filtered packet: pts=${packet.pts}`);
|
|
385
|
+
* await output.writePacket(packet);
|
|
386
|
+
* packet.free();
|
|
387
|
+
* }
|
|
213
388
|
* ```
|
|
214
389
|
*
|
|
215
|
-
* @see {@link
|
|
390
|
+
* @see {@link filter} For single packet filtering
|
|
216
391
|
* @see {@link packets} For stream processing
|
|
392
|
+
* @see {@link flush} For end-of-stream handling
|
|
393
|
+
* @see {@link filterAllSync} For synchronous version
|
|
217
394
|
*/
|
|
218
|
-
async
|
|
395
|
+
async filterAll(packet) {
|
|
219
396
|
if (this.isClosed) {
|
|
220
397
|
return [];
|
|
221
398
|
}
|
|
222
399
|
const outputPackets = [];
|
|
223
400
|
// Send packet to filter
|
|
224
401
|
const sendRet = await this.ctx.sendPacket(packet);
|
|
225
|
-
if (sendRet < 0 && sendRet !==
|
|
402
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
226
403
|
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
227
404
|
}
|
|
228
405
|
// Receive all output packets
|
|
229
|
-
while (
|
|
230
|
-
const outPacket =
|
|
231
|
-
outPacket
|
|
232
|
-
const recvRet = await this.ctx.receivePacket(outPacket);
|
|
233
|
-
if (recvRet === AVERROR_EAGAIN || recvRet === AVERROR_EOF) {
|
|
234
|
-
outPacket.unref();
|
|
406
|
+
while (true) {
|
|
407
|
+
const outPacket = await this.receive();
|
|
408
|
+
if (!outPacket)
|
|
235
409
|
break;
|
|
236
|
-
}
|
|
237
|
-
if (recvRet < 0) {
|
|
238
|
-
outPacket.unref();
|
|
239
|
-
FFmpegError.throwIfError(recvRet, 'Failed to receive packet from bitstream filter');
|
|
240
|
-
}
|
|
241
410
|
outputPackets.push(outPacket);
|
|
242
411
|
}
|
|
243
412
|
return outputPackets;
|
|
244
413
|
}
|
|
245
414
|
/**
|
|
246
|
-
*
|
|
247
|
-
* Synchronous version of
|
|
415
|
+
* Filter a packet to packets synchronously.
|
|
416
|
+
* Synchronous version of filterAll.
|
|
248
417
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
418
|
+
* Sends a packet to the filter and receives all available filtered packets.
|
|
419
|
+
* Returns array of packets - may be empty if filter needs more data.
|
|
420
|
+
* One packet can produce zero, one, or multiple packets depending on filter.
|
|
252
421
|
*
|
|
253
422
|
* Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
|
|
254
423
|
*
|
|
255
|
-
* @param packet - Packet to filter
|
|
424
|
+
* @param packet - Packet to filter
|
|
256
425
|
*
|
|
257
|
-
* @returns Array of filtered packets
|
|
426
|
+
* @returns Array of filtered packets (empty if more data needed or filter is closed)
|
|
258
427
|
*
|
|
259
428
|
* @throws {FFmpegError} If filtering fails
|
|
260
429
|
*
|
|
261
430
|
* @example
|
|
262
431
|
* ```typescript
|
|
263
|
-
* const outputPackets = filter.
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* }
|
|
432
|
+
* const outputPackets = filter.filterAllSync(inputPacket);
|
|
433
|
+
* for (const packet of outputPackets) {
|
|
434
|
+
* console.log(`Filtered packet: pts=${packet.pts}`);
|
|
435
|
+
* output.writePacketSync(packet);
|
|
436
|
+
* packet.free();
|
|
269
437
|
* }
|
|
270
438
|
* ```
|
|
271
439
|
*
|
|
272
|
-
* @
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* ```
|
|
277
|
-
*
|
|
278
|
-
* @see {@link process} For async version
|
|
440
|
+
* @see {@link filterSync} For single packet filtering
|
|
441
|
+
* @see {@link packetsSync} For stream processing
|
|
442
|
+
* @see {@link flushSync} For end-of-stream handling
|
|
443
|
+
* @see {@link filterAll} For async version
|
|
279
444
|
*/
|
|
280
|
-
|
|
445
|
+
filterAllSync(packet) {
|
|
281
446
|
if (this.isClosed) {
|
|
282
447
|
return [];
|
|
283
448
|
}
|
|
284
449
|
const outputPackets = [];
|
|
285
450
|
// Send packet to filter
|
|
286
451
|
const sendRet = this.ctx.sendPacketSync(packet);
|
|
287
|
-
if (sendRet < 0 && sendRet !==
|
|
452
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
288
453
|
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
289
454
|
}
|
|
290
455
|
// Receive all output packets
|
|
291
|
-
while (
|
|
292
|
-
const outPacket =
|
|
293
|
-
outPacket
|
|
294
|
-
const recvRet = this.ctx.receivePacketSync(outPacket);
|
|
295
|
-
if (recvRet === AVERROR_EAGAIN || recvRet === AVERROR_EOF) {
|
|
296
|
-
outPacket.unref();
|
|
456
|
+
while (true) {
|
|
457
|
+
const outPacket = this.receiveSync();
|
|
458
|
+
if (!outPacket)
|
|
297
459
|
break;
|
|
298
|
-
}
|
|
299
|
-
if (recvRet < 0) {
|
|
300
|
-
outPacket.unref();
|
|
301
|
-
FFmpegError.throwIfError(recvRet, 'Failed to receive packet from bitstream filter');
|
|
302
|
-
}
|
|
303
460
|
outputPackets.push(outPacket);
|
|
304
461
|
}
|
|
305
462
|
return outputPackets;
|
|
@@ -338,33 +495,67 @@ export class BitStreamFilterAPI {
|
|
|
338
495
|
* }
|
|
339
496
|
* ```
|
|
340
497
|
*
|
|
341
|
-
* @see {@link
|
|
498
|
+
* @see {@link filterAll} For filtering single packets
|
|
342
499
|
* @see {@link flush} For end-of-stream handling
|
|
343
500
|
*/
|
|
344
501
|
async *packets(packets) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
502
|
+
// Process all input packets
|
|
503
|
+
for await (const packet of packets) {
|
|
504
|
+
// Handle EOF signal
|
|
505
|
+
if (packet === null) {
|
|
506
|
+
// Flush filter
|
|
507
|
+
await this.flush();
|
|
508
|
+
while (true) {
|
|
509
|
+
const remaining = await this.receive();
|
|
510
|
+
if (!remaining)
|
|
511
|
+
break;
|
|
512
|
+
yield remaining;
|
|
353
513
|
}
|
|
514
|
+
// Signal EOF and stop processing
|
|
515
|
+
yield null;
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (this.isClosed) {
|
|
519
|
+
break;
|
|
354
520
|
}
|
|
355
|
-
// Send
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
521
|
+
// Send packet to filter
|
|
522
|
+
const sendRet = await this.ctx.sendPacket(packet);
|
|
523
|
+
// Handle EAGAIN
|
|
524
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
525
|
+
// Filter buffer full, receive packets first
|
|
526
|
+
while (true) {
|
|
527
|
+
const outPacket = await this.receive();
|
|
528
|
+
if (!outPacket)
|
|
529
|
+
break;
|
|
530
|
+
yield outPacket;
|
|
360
531
|
}
|
|
532
|
+
// Retry sending
|
|
533
|
+
const retryRet = await this.ctx.sendPacket(packet);
|
|
534
|
+
if (retryRet < 0 && retryRet !== AVERROR_EOF && retryRet !== AVERROR_EAGAIN) {
|
|
535
|
+
FFmpegError.throwIfError(retryRet, 'Failed to send packet to bitstream filter');
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
else if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
539
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
540
|
+
}
|
|
541
|
+
// Receive ALL available packets immediately
|
|
542
|
+
while (true) {
|
|
543
|
+
const outPacket = await this.receive();
|
|
544
|
+
if (!outPacket)
|
|
545
|
+
break; // EAGAIN or EOF
|
|
546
|
+
yield outPacket;
|
|
361
547
|
}
|
|
362
548
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
549
|
+
// Flush filter after all packets (fallback if no null was sent)
|
|
550
|
+
await this.flush();
|
|
551
|
+
while (true) {
|
|
552
|
+
const remaining = await this.receive();
|
|
553
|
+
if (!remaining)
|
|
554
|
+
break;
|
|
555
|
+
yield remaining;
|
|
367
556
|
}
|
|
557
|
+
// Signal EOF
|
|
558
|
+
yield null;
|
|
368
559
|
}
|
|
369
560
|
/**
|
|
370
561
|
* Process packet stream through filter synchronously.
|
|
@@ -404,149 +595,331 @@ export class BitStreamFilterAPI {
|
|
|
404
595
|
* @see {@link packets} For async version
|
|
405
596
|
*/
|
|
406
597
|
*packetsSync(packets) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
598
|
+
// Process all input packets
|
|
599
|
+
for (const packet of packets) {
|
|
600
|
+
// Handle EOF signal
|
|
601
|
+
if (packet === null) {
|
|
602
|
+
// Flush filter
|
|
603
|
+
this.flushSync();
|
|
604
|
+
while (true) {
|
|
605
|
+
const remaining = this.receiveSync();
|
|
606
|
+
if (!remaining)
|
|
607
|
+
break;
|
|
608
|
+
yield remaining;
|
|
415
609
|
}
|
|
610
|
+
// Signal EOF and stop processing
|
|
611
|
+
yield null;
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (this.isClosed) {
|
|
615
|
+
break;
|
|
416
616
|
}
|
|
417
|
-
// Send
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
617
|
+
// Send packet to filter
|
|
618
|
+
const sendRet = this.ctx.sendPacketSync(packet);
|
|
619
|
+
// Handle EAGAIN
|
|
620
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
621
|
+
// Filter buffer full, receive packets first
|
|
622
|
+
while (true) {
|
|
623
|
+
const outPacket = this.receiveSync();
|
|
624
|
+
if (!outPacket)
|
|
625
|
+
break;
|
|
626
|
+
yield outPacket;
|
|
422
627
|
}
|
|
628
|
+
// Retry sending
|
|
629
|
+
const retryRet = this.ctx.sendPacketSync(packet);
|
|
630
|
+
if (retryRet < 0 && retryRet !== AVERROR_EOF && retryRet !== AVERROR_EAGAIN) {
|
|
631
|
+
FFmpegError.throwIfError(retryRet, 'Failed to send packet to bitstream filter');
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
else if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
635
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
636
|
+
}
|
|
637
|
+
// Receive ALL available packets immediately
|
|
638
|
+
while (true) {
|
|
639
|
+
const outPacket = this.receiveSync();
|
|
640
|
+
if (!outPacket)
|
|
641
|
+
break; // EAGAIN or EOF
|
|
642
|
+
yield outPacket;
|
|
423
643
|
}
|
|
424
644
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
645
|
+
// Flush filter after all packets (fallback if no null was sent)
|
|
646
|
+
this.flushSync();
|
|
647
|
+
while (true) {
|
|
648
|
+
const remaining = this.receiveSync();
|
|
649
|
+
if (!remaining)
|
|
650
|
+
break;
|
|
651
|
+
yield remaining;
|
|
429
652
|
}
|
|
653
|
+
// Signal EOF
|
|
654
|
+
yield null;
|
|
430
655
|
}
|
|
431
656
|
/**
|
|
432
|
-
* Flush filter and
|
|
657
|
+
* Flush filter and signal end-of-stream.
|
|
433
658
|
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
659
|
+
* Sends null packet to filter to signal end-of-stream.
|
|
660
|
+
* Does nothing if filter is closed.
|
|
661
|
+
* Must call receive() or flushPackets() to get remaining buffered packets.
|
|
436
662
|
*
|
|
437
|
-
* Direct mapping to av_bsf_flush().
|
|
663
|
+
* Direct mapping to av_bsf_send_packet(NULL) and av_bsf_flush().
|
|
438
664
|
*
|
|
439
|
-
* @
|
|
665
|
+
* @throws {FFmpegError} If flush fails
|
|
440
666
|
*
|
|
441
667
|
* @example
|
|
442
668
|
* ```typescript
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
669
|
+
* // Signal end of stream
|
|
670
|
+
* await filter.flush();
|
|
671
|
+
*
|
|
672
|
+
* // Then get remaining packets
|
|
673
|
+
* let packet;
|
|
674
|
+
* while ((packet = await filter.receive()) !== null) {
|
|
675
|
+
* console.log('Got buffered packet');
|
|
676
|
+
* await output.writePacket(packet);
|
|
677
|
+
* packet.free();
|
|
449
678
|
* }
|
|
450
679
|
* ```
|
|
451
680
|
*
|
|
452
681
|
* @see {@link flushPackets} For async iteration
|
|
682
|
+
* @see {@link receive} For getting buffered packets
|
|
453
683
|
* @see {@link reset} For state reset only
|
|
684
|
+
* @see {@link flushSync} For synchronous version
|
|
454
685
|
*/
|
|
455
686
|
async flush() {
|
|
456
687
|
if (this.isClosed) {
|
|
457
|
-
return
|
|
688
|
+
return;
|
|
458
689
|
}
|
|
459
690
|
// Send EOF
|
|
460
|
-
const
|
|
691
|
+
const sendRet = await this.ctx.sendPacket(null);
|
|
692
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
693
|
+
FFmpegError.throwIfError(sendRet, 'Failed to flush bitstream filter');
|
|
694
|
+
}
|
|
461
695
|
// Also flush the context to reset internal state
|
|
462
696
|
this.ctx.flush();
|
|
463
|
-
return filtered;
|
|
464
697
|
}
|
|
465
698
|
/**
|
|
466
|
-
* Flush filter and
|
|
699
|
+
* Flush filter and signal end-of-stream synchronously.
|
|
467
700
|
* Synchronous version of flush.
|
|
468
701
|
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
702
|
+
* Sends null packet to filter to signal end-of-stream.
|
|
703
|
+
* Does nothing if filter is closed.
|
|
704
|
+
* Must call receiveSync() or flushPacketsSync() to get remaining buffered packets.
|
|
471
705
|
*
|
|
472
|
-
* Direct mapping to av_bsf_flush().
|
|
706
|
+
* Direct mapping to av_bsf_send_packet(NULL) and av_bsf_flush().
|
|
473
707
|
*
|
|
474
|
-
* @
|
|
708
|
+
* @throws {FFmpegError} If flush fails
|
|
475
709
|
*
|
|
476
710
|
* @example
|
|
477
711
|
* ```typescript
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
712
|
+
* // Signal end of stream
|
|
713
|
+
* filter.flushSync();
|
|
714
|
+
*
|
|
715
|
+
* // Then get remaining packets
|
|
716
|
+
* let packet;
|
|
717
|
+
* while ((packet = filter.receiveSync()) !== null) {
|
|
718
|
+
* console.log('Got buffered packet');
|
|
719
|
+
* output.writePacketSync(packet);
|
|
720
|
+
* packet.free();
|
|
484
721
|
* }
|
|
485
722
|
* ```
|
|
486
723
|
*
|
|
724
|
+
* @see {@link flushPacketsSync} For sync iteration
|
|
725
|
+
* @see {@link receiveSync} For getting buffered packets
|
|
726
|
+
* @see {@link reset} For state reset only
|
|
487
727
|
* @see {@link flush} For async version
|
|
488
728
|
*/
|
|
489
729
|
flushSync() {
|
|
490
730
|
if (this.isClosed) {
|
|
491
|
-
return
|
|
731
|
+
return;
|
|
492
732
|
}
|
|
493
733
|
// Send EOF
|
|
494
|
-
const
|
|
734
|
+
const sendRet = this.ctx.sendPacketSync(null);
|
|
735
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
736
|
+
FFmpegError.throwIfError(sendRet, 'Failed to flush bitstream filter');
|
|
737
|
+
}
|
|
495
738
|
// Also flush the context to reset internal state
|
|
496
739
|
this.ctx.flush();
|
|
497
|
-
return filtered;
|
|
498
740
|
}
|
|
499
741
|
/**
|
|
500
|
-
*
|
|
742
|
+
* Receive packet from filter.
|
|
743
|
+
*
|
|
744
|
+
* Gets filtered packets from the filter's internal buffer.
|
|
745
|
+
* Handles packet allocation and error checking.
|
|
746
|
+
* Returns null if filter is closed or no packets available.
|
|
747
|
+
* Call repeatedly until null to drain all buffered packets.
|
|
748
|
+
*
|
|
749
|
+
* Direct mapping to av_bsf_receive_packet().
|
|
750
|
+
*
|
|
751
|
+
* @returns Cloned packet or null if no packets available
|
|
752
|
+
*
|
|
753
|
+
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```typescript
|
|
757
|
+
* const packet = await filter.receive();
|
|
758
|
+
* if (packet) {
|
|
759
|
+
* console.log('Got filtered packet');
|
|
760
|
+
* await output.writePacket(packet);
|
|
761
|
+
* packet.free();
|
|
762
|
+
* }
|
|
763
|
+
* ```
|
|
764
|
+
*
|
|
765
|
+
* @example
|
|
766
|
+
* ```typescript
|
|
767
|
+
* // Drain all buffered packets
|
|
768
|
+
* let packet;
|
|
769
|
+
* while ((packet = await filter.receive()) !== null) {
|
|
770
|
+
* console.log(`Packet PTS: ${packet.pts}`);
|
|
771
|
+
* await output.writePacket(packet);
|
|
772
|
+
* packet.free();
|
|
773
|
+
* }
|
|
774
|
+
* ```
|
|
775
|
+
*
|
|
776
|
+
* @see {@link filter} For filtering packets
|
|
777
|
+
* @see {@link flush} For signaling end-of-stream
|
|
778
|
+
* @see {@link receiveSync} For synchronous version
|
|
779
|
+
*/
|
|
780
|
+
async receive() {
|
|
781
|
+
if (this.isClosed) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
// Clear previous packet data
|
|
785
|
+
this.packet.unref();
|
|
786
|
+
const recvRet = await this.ctx.receivePacket(this.packet);
|
|
787
|
+
if (recvRet === 0) {
|
|
788
|
+
// Got a packet, clone it for the user
|
|
789
|
+
return this.packet.clone();
|
|
790
|
+
}
|
|
791
|
+
else if (recvRet === AVERROR_EAGAIN || recvRet === AVERROR_EOF) {
|
|
792
|
+
// Need more data or end of stream
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
// Error
|
|
797
|
+
FFmpegError.throwIfError(recvRet, 'Failed to receive packet from bitstream filter');
|
|
798
|
+
return null;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Receive packet from filter synchronously.
|
|
803
|
+
* Synchronous version of receive.
|
|
804
|
+
*
|
|
805
|
+
* Gets filtered packets from the filter's internal buffer.
|
|
806
|
+
* Handles packet allocation and error checking.
|
|
807
|
+
* Returns null if filter is closed or no packets available.
|
|
808
|
+
* Call repeatedly until null to drain all buffered packets.
|
|
809
|
+
*
|
|
810
|
+
* Direct mapping to av_bsf_receive_packet().
|
|
811
|
+
*
|
|
812
|
+
* @returns Cloned packet or null if no packets available
|
|
813
|
+
*
|
|
814
|
+
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```typescript
|
|
818
|
+
* const packet = filter.receiveSync();
|
|
819
|
+
* if (packet) {
|
|
820
|
+
* console.log('Got filtered packet');
|
|
821
|
+
* output.writePacketSync(packet);
|
|
822
|
+
* packet.free();
|
|
823
|
+
* }
|
|
824
|
+
* ```
|
|
825
|
+
*
|
|
826
|
+
* @example
|
|
827
|
+
* ```typescript
|
|
828
|
+
* // Drain all buffered packets
|
|
829
|
+
* let packet;
|
|
830
|
+
* while ((packet = filter.receiveSync()) !== null) {
|
|
831
|
+
* console.log(`Packet PTS: ${packet.pts}`);
|
|
832
|
+
* output.writePacketSync(packet);
|
|
833
|
+
* packet.free();
|
|
834
|
+
* }
|
|
835
|
+
* ```
|
|
836
|
+
*
|
|
837
|
+
* @see {@link filterSync} For filtering packets
|
|
838
|
+
* @see {@link flushSync} For signaling end-of-stream
|
|
839
|
+
* @see {@link receive} For async version
|
|
840
|
+
*/
|
|
841
|
+
receiveSync() {
|
|
842
|
+
if (this.isClosed) {
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
// Clear previous packet data
|
|
846
|
+
this.packet.unref();
|
|
847
|
+
const recvRet = this.ctx.receivePacketSync(this.packet);
|
|
848
|
+
if (recvRet === 0) {
|
|
849
|
+
// Got a packet, clone it for the user
|
|
850
|
+
return this.packet.clone();
|
|
851
|
+
}
|
|
852
|
+
else if (recvRet === AVERROR_EAGAIN || recvRet === AVERROR_EOF) {
|
|
853
|
+
// Need more data or end of stream
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
// Error
|
|
858
|
+
FFmpegError.throwIfError(recvRet, 'Failed to receive packet from bitstream filter');
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Flush all buffered packets as async generator.
|
|
501
864
|
*
|
|
502
865
|
* Convenient async iteration over remaining packets.
|
|
503
|
-
*
|
|
866
|
+
* Automatically sends flush signal and retrieves buffered packets.
|
|
867
|
+
* Useful for end-of-stream processing.
|
|
504
868
|
*
|
|
505
|
-
* @yields {Packet}
|
|
869
|
+
* @yields {Packet} Buffered packets
|
|
506
870
|
*
|
|
507
871
|
* @example
|
|
508
872
|
* ```typescript
|
|
873
|
+
* // Flush at end of filtering
|
|
509
874
|
* for await (const packet of filter.flushPackets()) {
|
|
875
|
+
* console.log('Processing buffered packet');
|
|
510
876
|
* await output.writePacket(packet);
|
|
511
877
|
* packet.free();
|
|
512
878
|
* }
|
|
513
879
|
* ```
|
|
514
880
|
*
|
|
515
|
-
* @see {@link
|
|
881
|
+
* @see {@link filter} For filtering packets
|
|
882
|
+
* @see {@link flush} For signaling end-of-stream
|
|
883
|
+
* @see {@link flushPacketsSync} For synchronous version
|
|
516
884
|
*/
|
|
517
885
|
async *flushPackets() {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
886
|
+
while (true) {
|
|
887
|
+
const packet = await this.receive();
|
|
888
|
+
if (!packet)
|
|
889
|
+
break;
|
|
890
|
+
yield packet;
|
|
523
891
|
}
|
|
524
892
|
}
|
|
525
893
|
/**
|
|
526
|
-
* Flush
|
|
894
|
+
* Flush all buffered packets as generator synchronously.
|
|
527
895
|
* Synchronous version of flushPackets.
|
|
528
896
|
*
|
|
529
897
|
* Convenient sync iteration over remaining packets.
|
|
530
|
-
*
|
|
898
|
+
* Automatically retrieves buffered packets after flush.
|
|
899
|
+
* Useful for end-of-stream processing.
|
|
531
900
|
*
|
|
532
|
-
* @yields {Packet}
|
|
901
|
+
* @yields {Packet} Buffered packets
|
|
533
902
|
*
|
|
534
903
|
* @example
|
|
535
904
|
* ```typescript
|
|
905
|
+
* // Flush at end of filtering
|
|
536
906
|
* for (const packet of filter.flushPacketsSync()) {
|
|
907
|
+
* console.log('Processing buffered packet');
|
|
537
908
|
* output.writePacketSync(packet);
|
|
538
909
|
* packet.free();
|
|
539
910
|
* }
|
|
540
911
|
* ```
|
|
541
912
|
*
|
|
913
|
+
* @see {@link filterSync} For filtering packets
|
|
914
|
+
* @see {@link flushSync} For signaling end-of-stream
|
|
542
915
|
* @see {@link flushPackets} For async version
|
|
543
916
|
*/
|
|
544
917
|
*flushPacketsSync() {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
918
|
+
while (true) {
|
|
919
|
+
const packet = this.receiveSync();
|
|
920
|
+
if (!packet)
|
|
921
|
+
break;
|
|
922
|
+
yield packet;
|
|
550
923
|
}
|
|
551
924
|
}
|
|
552
925
|
/**
|
|
@@ -605,6 +978,10 @@ export class BitStreamFilterAPI {
|
|
|
605
978
|
return;
|
|
606
979
|
}
|
|
607
980
|
this.isClosed = true;
|
|
981
|
+
// Close queues
|
|
982
|
+
this.inputQueue.close();
|
|
983
|
+
this.outputQueue.close();
|
|
984
|
+
this.packet.free();
|
|
608
985
|
this.ctx.free();
|
|
609
986
|
}
|
|
610
987
|
/**
|
|
@@ -626,5 +1003,157 @@ export class BitStreamFilterAPI {
|
|
|
626
1003
|
[Symbol.dispose]() {
|
|
627
1004
|
this.close();
|
|
628
1005
|
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Send packet to input queue.
|
|
1008
|
+
*
|
|
1009
|
+
* @param packet - Packet to send
|
|
1010
|
+
*
|
|
1011
|
+
* @internal
|
|
1012
|
+
*/
|
|
1013
|
+
async sendToQueue(packet) {
|
|
1014
|
+
await this.inputQueue.send(packet);
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Receive packet from output queue.
|
|
1018
|
+
*
|
|
1019
|
+
* @returns Packet from queue or null if closed
|
|
1020
|
+
*
|
|
1021
|
+
* @internal
|
|
1022
|
+
*/
|
|
1023
|
+
async receiveFromQueue() {
|
|
1024
|
+
return await this.outputQueue.receive();
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Worker loop for push-based processing.
|
|
1028
|
+
*
|
|
1029
|
+
* @internal
|
|
1030
|
+
*/
|
|
1031
|
+
async runWorker() {
|
|
1032
|
+
try {
|
|
1033
|
+
// Outer loop - receive packets
|
|
1034
|
+
while (!this.inputQueue.isClosed) {
|
|
1035
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
1036
|
+
try {
|
|
1037
|
+
const packet = __addDisposableResource(env_1, await this.inputQueue.receive(), false);
|
|
1038
|
+
if (!packet)
|
|
1039
|
+
break;
|
|
1040
|
+
if (this.isClosed) {
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
// Send packet to filter
|
|
1044
|
+
const sendRet = await this.ctx.sendPacket(packet);
|
|
1045
|
+
// Handle EAGAIN
|
|
1046
|
+
if (sendRet === AVERROR_EAGAIN) {
|
|
1047
|
+
// Filter buffer full, receive packets first
|
|
1048
|
+
while (!this.outputQueue.isClosed) {
|
|
1049
|
+
const outPacket = await this.receive();
|
|
1050
|
+
if (!outPacket)
|
|
1051
|
+
break;
|
|
1052
|
+
await this.outputQueue.send(outPacket);
|
|
1053
|
+
}
|
|
1054
|
+
// Retry sending
|
|
1055
|
+
const retryRet = await this.ctx.sendPacket(packet);
|
|
1056
|
+
if (retryRet < 0 && retryRet !== AVERROR_EOF && retryRet !== AVERROR_EAGAIN) {
|
|
1057
|
+
FFmpegError.throwIfError(retryRet, 'Failed to send packet to bitstream filter');
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
else if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
1061
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
|
|
1062
|
+
}
|
|
1063
|
+
// Receive ALL available packets immediately
|
|
1064
|
+
while (!this.outputQueue.isClosed) {
|
|
1065
|
+
const outPacket = await this.receive();
|
|
1066
|
+
if (!outPacket)
|
|
1067
|
+
break;
|
|
1068
|
+
await this.outputQueue.send(outPacket);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
catch (e_1) {
|
|
1072
|
+
env_1.error = e_1;
|
|
1073
|
+
env_1.hasError = true;
|
|
1074
|
+
}
|
|
1075
|
+
finally {
|
|
1076
|
+
__disposeResources(env_1);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
// Flush filter at end
|
|
1080
|
+
await this.flush();
|
|
1081
|
+
while (!this.outputQueue.isClosed) {
|
|
1082
|
+
const outPacket = await this.receive();
|
|
1083
|
+
if (!outPacket)
|
|
1084
|
+
break;
|
|
1085
|
+
await this.outputQueue.send(outPacket);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
catch {
|
|
1089
|
+
// Ignore error
|
|
1090
|
+
}
|
|
1091
|
+
finally {
|
|
1092
|
+
// Close output queue when done
|
|
1093
|
+
this.outputQueue?.close();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
pipeTo(target, streamIndex) {
|
|
1097
|
+
if (target instanceof Muxer) {
|
|
1098
|
+
// Start worker if not already running
|
|
1099
|
+
this.workerPromise ??= this.runWorker();
|
|
1100
|
+
// Start pipe task: filter.outputQueue -> output
|
|
1101
|
+
this.pipeToPromise = (async () => {
|
|
1102
|
+
while (true) {
|
|
1103
|
+
const packet = await this.receiveFromQueue();
|
|
1104
|
+
if (!packet)
|
|
1105
|
+
break;
|
|
1106
|
+
await target.writePacket(packet, streamIndex);
|
|
1107
|
+
}
|
|
1108
|
+
})();
|
|
1109
|
+
// Return control without pipeTo (terminal stage)
|
|
1110
|
+
return new SchedulerControl(this);
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
// BitStreamFilterAPI
|
|
1114
|
+
const t = target;
|
|
1115
|
+
// Store reference to next component for flush propagation
|
|
1116
|
+
this.nextComponent = t;
|
|
1117
|
+
// Start worker if not already running
|
|
1118
|
+
this.workerPromise ??= this.runWorker();
|
|
1119
|
+
// Start pipe task: filter.outputQueue -> target.inputQueue (via target.send)
|
|
1120
|
+
this.pipeToPromise = (async () => {
|
|
1121
|
+
while (true) {
|
|
1122
|
+
const packet = await this.receiveFromQueue();
|
|
1123
|
+
if (!packet)
|
|
1124
|
+
break;
|
|
1125
|
+
await t.sendToQueue(packet);
|
|
1126
|
+
}
|
|
1127
|
+
})();
|
|
1128
|
+
// Return scheduler for chaining (target is now the last component)
|
|
1129
|
+
return new Scheduler(this, t);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Flush pipeline.
|
|
1134
|
+
*
|
|
1135
|
+
* Closes input queue, waits for worker to finish,
|
|
1136
|
+
* then propagates flush to next component.
|
|
1137
|
+
*
|
|
1138
|
+
* @internal
|
|
1139
|
+
*/
|
|
1140
|
+
async flushPipeline() {
|
|
1141
|
+
// Close input queue to signal end of stream to worker
|
|
1142
|
+
this.inputQueue.close();
|
|
1143
|
+
// Wait for worker to finish processing all packets
|
|
1144
|
+
if (this.workerPromise) {
|
|
1145
|
+
await this.workerPromise;
|
|
1146
|
+
}
|
|
1147
|
+
// Close output queue to signal end of stream to pipeTo() task
|
|
1148
|
+
this.outputQueue.close();
|
|
1149
|
+
// Wait for pipeTo() task to finish processing all packets (if exists)
|
|
1150
|
+
if (this.pipeToPromise) {
|
|
1151
|
+
await this.pipeToPromise;
|
|
1152
|
+
}
|
|
1153
|
+
// Then propagate flush to next component
|
|
1154
|
+
if (this.nextComponent) {
|
|
1155
|
+
await this.nextComponent.flushPipeline();
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
629
1158
|
}
|
|
630
1159
|
//# sourceMappingURL=bitstream-filter.js.map
|