node-av 5.0.4 → 5.1.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.
Files changed (43) hide show
  1. package/BENCHMARK.md +113 -0
  2. package/README.md +59 -1
  3. package/dist/api/bitstream-filter.d.ts +190 -74
  4. package/dist/api/bitstream-filter.js +276 -222
  5. package/dist/api/bitstream-filter.js.map +1 -1
  6. package/dist/api/decoder.d.ts +23 -29
  7. package/dist/api/decoder.js +37 -47
  8. package/dist/api/decoder.js.map +1 -1
  9. package/dist/api/encoder.d.ts +4 -4
  10. package/dist/api/encoder.js +16 -20
  11. package/dist/api/encoder.js.map +1 -1
  12. package/dist/api/filter-complex.js +7 -3
  13. package/dist/api/filter-complex.js.map +1 -1
  14. package/dist/api/filter.d.ts +4 -4
  15. package/dist/api/filter.js +18 -16
  16. package/dist/api/filter.js.map +1 -1
  17. package/dist/api/hardware.d.ts +17 -0
  18. package/dist/api/hardware.js +73 -27
  19. package/dist/api/hardware.js.map +1 -1
  20. package/dist/api/muxer.js +8 -4
  21. package/dist/api/muxer.js.map +1 -1
  22. package/dist/api/rtp-stream.js +0 -6
  23. package/dist/api/rtp-stream.js.map +1 -1
  24. package/dist/api/types.d.ts +29 -9
  25. package/dist/constants/constants.d.ts +6 -3
  26. package/dist/constants/constants.js +5 -5
  27. package/dist/constants/constants.js.map +1 -1
  28. package/dist/ffmpeg/index.js +3 -4
  29. package/dist/ffmpeg/index.js.map +1 -1
  30. package/dist/lib/binding.js +3 -4
  31. package/dist/lib/binding.js.map +1 -1
  32. package/dist/lib/codec-context.d.ts +5 -3
  33. package/dist/lib/codec-context.js +2 -0
  34. package/dist/lib/codec-context.js.map +1 -1
  35. package/dist/lib/native-types.d.ts +2 -2
  36. package/dist/utils/electron.d.ts +49 -0
  37. package/dist/utils/electron.js +63 -0
  38. package/dist/utils/electron.js.map +1 -0
  39. package/dist/utils/index.d.ts +4 -0
  40. package/dist/utils/index.js +5 -0
  41. package/dist/utils/index.js.map +1 -0
  42. package/package.json +32 -15
  43. package/binding.gyp +0 -166
@@ -140,6 +140,8 @@ export class BitStreamFilterAPI {
140
140
  *
141
141
  * @param stream - Stream to apply filter to
142
142
  *
143
+ * @param filterOptions - Optional filter-specific options
144
+ *
143
145
  * @returns Configured bitstream filter
144
146
  *
145
147
  * @throws {Error} If initialization fails
@@ -160,13 +162,24 @@ export class BitStreamFilterAPI {
160
162
  *
161
163
  * @example
162
164
  * ```typescript
163
- * // Remove metadata
164
- * const filter = BitStreamFilterAPI.create('filter_units', stream);
165
+ * // Remove AUDs from H.264 stream
166
+ * const filter = BitStreamFilterAPI.create('h264_metadata', stream, {
167
+ * options: { aud: 'remove' }
168
+ * });
169
+ * ```
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Set H.264 level
174
+ * const filter = BitStreamFilterAPI.create('h264_metadata', stream, {
175
+ * options: { level: 51 }
176
+ * });
165
177
  * ```
166
178
  *
167
179
  * @see {@link BitStreamFilter.getByName} For filter discovery
180
+ * @see {@link BitstreamFilterOptions} For available options
168
181
  */
169
- static create(filterName, stream) {
182
+ static create(filterName, stream, filterOptions) {
170
183
  if (!stream) {
171
184
  throw new Error('Stream is required');
172
185
  }
@@ -187,6 +200,13 @@ export class BitStreamFilterAPI {
187
200
  stream.codecpar.copy(ctx.inputCodecParameters);
188
201
  // Set time base
189
202
  ctx.inputTimeBase = stream.timeBase;
203
+ // Apply filter-specific options before init
204
+ if (filterOptions?.options) {
205
+ for (const [key, value] of Object.entries(filterOptions.options)) {
206
+ const ret = ctx.setOption(key, value);
207
+ FFmpegError.throwIfError(ret, `Failed to set bitstream filter option '${key}'`);
208
+ }
209
+ }
190
210
  // Initialize the filter
191
211
  const initRet = ctx.init();
192
212
  FFmpegError.throwIfError(initRet, 'Failed to initialize bitstream filter');
@@ -252,115 +272,129 @@ export class BitStreamFilterAPI {
252
272
  return !this.isClosed;
253
273
  }
254
274
  /**
255
- * Filter a packet.
275
+ * Send a packet to the filter.
256
276
  *
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.
277
+ * Sends a packet to the filter for processing.
278
+ * Does not return filtered packets - use {@link receive} to retrieve packets.
279
+ * A single packet can produce zero, one, or multiple packets depending on filter.
259
280
  *
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.
281
+ * **Important**: This method only SENDS the packet to the filter.
282
+ * You must call {@link receive} separately (potentially multiple times) to get filtered packets.
263
283
  *
264
- * Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
284
+ * Direct mapping to av_bsf_send_packet().
265
285
  *
266
- * @param packet - Packet to filter
286
+ * @param packet - Packet to send to filter, or null to flush
267
287
  *
268
- * @returns Filtered packet, null if more data needed, or null if filter is closed
269
- *
270
- * @throws {FFmpegError} If filtering fails
288
+ * @throws {FFmpegError} If sending fails
271
289
  *
272
290
  * @example
273
291
  * ```typescript
274
- * const outPacket = await filter.filter(inputPacket);
275
- * if (outPacket) {
276
- * console.log(`Filtered packet: pts=${outPacket.pts}`);
292
+ * // Send packet and receive filtered packets
293
+ * await filter.filter(inputPacket);
294
+ *
295
+ * // Receive all available filtered packets
296
+ * while (true) {
297
+ * const outPacket = await filter.receive();
298
+ * if (!outPacket) break;
299
+ * console.log(`Filtered packet with PTS: ${outPacket.pts}`);
277
300
  * await output.writePacket(outPacket);
278
301
  * outPacket.free();
279
302
  * }
280
303
  * ```
281
304
  *
282
- * @see {@link filterAll} For multiple packet filtering
283
- * @see {@link packets} For stream processing
305
+ * @example
306
+ * ```typescript
307
+ * for await (const packet of input.packets()) {
308
+ * // packet is null at end of stream - automatically flushes filter
309
+ * await filter.filter(packet);
310
+ *
311
+ * // Receive available filtered packets
312
+ * let outPacket;
313
+ * while ((outPacket = await filter.receive())) {
314
+ * await output.writePacket(outPacket);
315
+ * outPacket.free();
316
+ * }
317
+ * }
318
+ * ```
319
+ *
320
+ * @see {@link receive} For receiving filtered packets
321
+ * @see {@link filterAll} For combined send+receive operation
322
+ * @see {@link packets} For automatic packet iteration
284
323
  * @see {@link flush} For end-of-stream handling
285
324
  * @see {@link filterSync} For synchronous version
286
325
  */
287
326
  async filter(packet) {
288
327
  if (this.isClosed) {
289
- return null;
328
+ return;
290
329
  }
291
- // Send packet to filter
330
+ // Send packet to filter (null signals EOF/flush)
292
331
  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) {
332
+ if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
304
333
  FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
305
334
  }
306
- // Try to receive packet
307
- return await this.receive();
308
335
  }
309
336
  /**
310
- * Filter a packet synchronously.
337
+ * Send a packet to the filter synchronously.
311
338
  * Synchronous version of filter.
312
339
  *
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.
315
- *
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.
340
+ * Sends a packet to the filter for processing.
341
+ * Does not return filtered packets - use {@link receiveSync} to retrieve packets.
342
+ * A single packet can produce zero, one, or multiple packets depending on filter.
319
343
  *
320
- * Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
344
+ * **Important**: This method only SENDS the packet to the filter.
345
+ * You must call {@link receiveSync} separately (potentially multiple times) to get filtered packets.
321
346
  *
322
- * @param packet - Packet to filter
347
+ * Direct mapping to av_bsf_send_packet().
323
348
  *
324
- * @returns Filtered packet, null if more data needed, or null if filter is closed
349
+ * @param packet - Packet to send to filter, or null to flush
325
350
  *
326
- * @throws {FFmpegError} If filtering fails
351
+ * @throws {FFmpegError} If sending fails
327
352
  *
328
353
  * @example
329
354
  * ```typescript
330
- * const outPacket = filter.filterSync(inputPacket);
331
- * if (outPacket) {
332
- * console.log(`Filtered packet: pts=${outPacket.pts}`);
355
+ * // Send packet and receive filtered packets
356
+ * filter.filterSync(inputPacket);
357
+ *
358
+ * // Receive all available filtered packets
359
+ * while (true) {
360
+ * const outPacket = filter.receiveSync();
361
+ * if (!outPacket) break;
362
+ * console.log(`Filtered packet with PTS: ${outPacket.pts}`);
333
363
  * output.writePacketSync(outPacket);
334
364
  * outPacket.free();
335
365
  * }
336
366
  * ```
337
367
  *
338
- * @see {@link filterAllSync} For multiple packet filtering
339
- * @see {@link packetsSync} For stream processing
368
+ * @example
369
+ * ```typescript
370
+ * for (const packet of packets) {
371
+ * // packet is null at end of stream - automatically flushes filter
372
+ * filter.filterSync(packet);
373
+ *
374
+ * // Receive available filtered packets
375
+ * let outPacket;
376
+ * while ((outPacket = filter.receiveSync())) {
377
+ * output.writePacketSync(outPacket);
378
+ * outPacket.free();
379
+ * }
380
+ * }
381
+ * ```
382
+ *
383
+ * @see {@link receiveSync} For receiving filtered packets
384
+ * @see {@link filterAllSync} For combined send+receive operation
385
+ * @see {@link packetsSync} For automatic packet iteration
340
386
  * @see {@link flushSync} For end-of-stream handling
341
387
  * @see {@link filter} For async version
342
388
  */
343
389
  filterSync(packet) {
344
390
  if (this.isClosed) {
345
- return null;
391
+ return;
346
392
  }
347
- // Send packet to filter
393
+ // Send packet to filter (null signals EOF/flush)
348
394
  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) {
395
+ if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
360
396
  FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
361
397
  }
362
- // Try to receive packet
363
- return this.receiveSync();
364
398
  }
365
399
  /**
366
400
  * Filter a packet to packets.
@@ -371,7 +405,7 @@ export class BitStreamFilterAPI {
371
405
  *
372
406
  * Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
373
407
  *
374
- * @param packet - Packet to filter
408
+ * @param packet - Packet to filter, or null to flush
375
409
  *
376
410
  * @returns Array of filtered packets (empty if more data needed or filter is closed)
377
411
  *
@@ -387,22 +421,25 @@ export class BitStreamFilterAPI {
387
421
  * }
388
422
  * ```
389
423
  *
424
+ * @example
425
+ * ```typescript
426
+ * // Flush remaining packets at end of stream
427
+ * const remaining = await filter.filterAll(null);
428
+ * for (const packet of remaining) {
429
+ * await output.writePacket(packet);
430
+ * packet.free();
431
+ * }
432
+ * ```
433
+ *
390
434
  * @see {@link filter} For single packet filtering
391
435
  * @see {@link packets} For stream processing
392
436
  * @see {@link flush} For end-of-stream handling
393
437
  * @see {@link filterAllSync} For synchronous version
394
438
  */
395
439
  async filterAll(packet) {
396
- if (this.isClosed) {
397
- return [];
398
- }
399
- const outputPackets = [];
400
- // Send packet to filter
401
- const sendRet = await this.ctx.sendPacket(packet);
402
- if (sendRet < 0 && sendRet !== AVERROR_EOF) {
403
- FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
404
- }
440
+ await this.filter(packet);
405
441
  // Receive all output packets
442
+ const outputPackets = [];
406
443
  while (true) {
407
444
  const outPacket = await this.receive();
408
445
  if (!outPacket)
@@ -421,7 +458,7 @@ export class BitStreamFilterAPI {
421
458
  *
422
459
  * Direct mapping to av_bsf_send_packet() and av_bsf_receive_packet().
423
460
  *
424
- * @param packet - Packet to filter
461
+ * @param packet - Packet to filter, or null to flush
425
462
  *
426
463
  * @returns Array of filtered packets (empty if more data needed or filter is closed)
427
464
  *
@@ -437,22 +474,25 @@ export class BitStreamFilterAPI {
437
474
  * }
438
475
  * ```
439
476
  *
477
+ * @example
478
+ * ```typescript
479
+ * // Flush remaining packets at end of stream
480
+ * const remaining = filter.filterAllSync(null);
481
+ * for (const packet of remaining) {
482
+ * output.writePacketSync(packet);
483
+ * packet.free();
484
+ * }
485
+ * ```
486
+ *
440
487
  * @see {@link filterSync} For single packet filtering
441
488
  * @see {@link packetsSync} For stream processing
442
489
  * @see {@link flushSync} For end-of-stream handling
443
490
  * @see {@link filterAll} For async version
444
491
  */
445
492
  filterAllSync(packet) {
446
- if (this.isClosed) {
447
- return [];
448
- }
449
- const outputPackets = [];
450
- // Send packet to filter
451
- const sendRet = this.ctx.sendPacketSync(packet);
452
- if (sendRet < 0 && sendRet !== AVERROR_EOF) {
453
- FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
454
- }
493
+ this.filterSync(packet);
455
494
  // Receive all output packets
495
+ const outputPackets = [];
456
496
  while (true) {
457
497
  const outPacket = this.receiveSync();
458
498
  if (!outPacket)
@@ -462,196 +502,210 @@ export class BitStreamFilterAPI {
462
502
  return outputPackets;
463
503
  }
464
504
  /**
465
- * Process packet stream through filter.
505
+ * Filter packet stream to filtered packet stream.
506
+ *
507
+ * High-level async generator for complete filtering pipeline.
508
+ * Filter is only flushed when EOF (null) signal is explicitly received.
509
+ * Primary interface for stream-based filtering.
466
510
  *
467
- * High-level async generator for filtering packet streams.
468
- * Automatically handles flushing at end of stream.
469
- * Yields filtered packets ready for output.
511
+ * **EOF Handling:**
512
+ * - Send null to flush filter and get remaining buffered packets
513
+ * - Generator yields null after flushing when null is received
514
+ * - No automatic flushing - filter stays open until EOF or close()
470
515
  *
471
- * @param packets - Async iterable of packets
516
+ * @param packets - Async iterable of packets, single packet, or null to flush
472
517
  *
473
- * @yields {Packet} Filtered packets
518
+ * @yields {Packet | null} Filtered packets, followed by null when explicitly flushed
474
519
  *
475
520
  * @throws {FFmpegError} If filtering fails
476
521
  *
477
522
  * @example
478
523
  * ```typescript
479
- * // Filter entire stream
524
+ * // Stream of packets with automatic EOF propagation
480
525
  * for await (const packet of filter.packets(input.packets())) {
526
+ * if (packet === null) {
527
+ * console.log('Filter flushed');
528
+ * break;
529
+ * }
481
530
  * await output.writePacket(packet);
482
- * packet.free();
531
+ * packet.free(); // Must free output packets
483
532
  * }
484
533
  * ```
485
534
  *
486
535
  * @example
487
536
  * ```typescript
488
- * // Chain with decoder
489
- * const decoder = await Decoder.create(stream);
490
- * const filter = BitStreamFilterAPI.create('h264_mp4toannexb', stream);
537
+ * // Single packet - no automatic flush
538
+ * for await (const packet of filter.packets(singlePacket)) {
539
+ * await output.writePacket(packet);
540
+ * packet.free();
541
+ * }
542
+ * // Filter remains open, buffered packets not flushed
543
+ * ```
491
544
  *
492
- * for await (const frame of decoder.frames(filter.packets(input.packets()))) {
493
- * // Process frames
494
- * frame.free();
545
+ * @example
546
+ * ```typescript
547
+ * // Explicit flush with EOF
548
+ * for await (const packet of filter.packets(null)) {
549
+ * if (packet === null) {
550
+ * console.log('All buffered packets flushed');
551
+ * break;
552
+ * }
553
+ * console.log('Buffered packet:', packet.pts);
554
+ * await output.writePacket(packet);
555
+ * packet.free();
495
556
  * }
496
557
  * ```
497
558
  *
498
- * @see {@link filterAll} For filtering single packets
499
- * @see {@link flush} For end-of-stream handling
559
+ * @see {@link filter} For single packet filtering
560
+ * @see {@link Demuxer.packets} For packet source
561
+ * @see {@link packetsSync} For sync version
500
562
  */
501
563
  async *packets(packets) {
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;
513
- }
514
- // Signal EOF and stop processing
515
- yield null;
516
- return;
564
+ const self = this;
565
+ const processPacket = async function* (packet) {
566
+ await self.filter(packet);
567
+ while (true) {
568
+ const outPacket = await self.receive();
569
+ if (!outPacket)
570
+ break;
571
+ yield outPacket;
517
572
  }
518
- if (this.isClosed) {
519
- break;
573
+ }.bind(this);
574
+ const finalize = async function* () {
575
+ for await (const remaining of self.flushPackets()) {
576
+ yield remaining;
520
577
  }
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;
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');
578
+ yield null;
579
+ }.bind(this);
580
+ if (packets === null) {
581
+ yield* finalize();
582
+ return;
583
+ }
584
+ if (packets instanceof Packet) {
585
+ yield* processPacket(packets);
586
+ return;
587
+ }
588
+ for await (const packet_1 of packets) {
589
+ const env_1 = { stack: [], error: void 0, hasError: false };
590
+ try {
591
+ const packet = __addDisposableResource(env_1, packet_1, false);
592
+ if (packet === null) {
593
+ yield* finalize();
594
+ return;
536
595
  }
596
+ yield* processPacket(packet);
537
597
  }
538
- else if (sendRet < 0 && sendRet !== AVERROR_EOF) {
539
- FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
598
+ catch (e_1) {
599
+ env_1.error = e_1;
600
+ env_1.hasError = true;
540
601
  }
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;
602
+ finally {
603
+ __disposeResources(env_1);
547
604
  }
548
605
  }
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;
556
- }
557
- // Signal EOF
558
- yield null;
559
606
  }
560
607
  /**
561
- * Process packet stream through filter synchronously.
608
+ * Filter packet stream to filtered packet stream synchronously.
562
609
  * Synchronous version of packets.
563
610
  *
564
- * High-level sync generator for filtering packet streams.
565
- * Automatically handles flushing at end of stream.
566
- * Yields filtered packets ready for output.
611
+ * High-level sync generator for complete filtering pipeline.
612
+ * Filter is only flushed when EOF (null) signal is explicitly received.
613
+ * Primary interface for stream-based filtering.
567
614
  *
568
- * @param packets - Iterable of packets
615
+ * **EOF Handling:**
616
+ * - Send null to flush filter and get remaining buffered packets
617
+ * - Generator yields null after flushing when null is received
618
+ * - No automatic flushing - filter stays open until EOF or close()
569
619
  *
570
- * @yields {Packet} Filtered packets
620
+ * @param packets - Iterable of packets, single packet, or null to flush
621
+ *
622
+ * @yields {Packet | null} Filtered packets, followed by null when explicitly flushed
571
623
  *
572
624
  * @throws {FFmpegError} If filtering fails
573
625
  *
574
626
  * @example
575
627
  * ```typescript
576
- * // Filter entire stream
577
- * for (const packet of filter.packetsSync(packets)) {
628
+ * // Stream of packets with automatic EOF propagation
629
+ * for (const packet of filter.packetsSync(inputPackets)) {
630
+ * if (packet === null) {
631
+ * console.log('Filter flushed');
632
+ * break;
633
+ * }
578
634
  * output.writePacketSync(packet);
579
- * packet.free();
635
+ * packet.free(); // Must free output packets
580
636
  * }
581
637
  * ```
582
638
  *
583
639
  * @example
584
640
  * ```typescript
585
- * // Chain with decoder
586
- * const decoder = await Decoder.create(stream);
587
- * const filter = BitStreamFilterAPI.create('h264_mp4toannexb', stream);
641
+ * // Single packet - no automatic flush
642
+ * for (const packet of filter.packetsSync(singlePacket)) {
643
+ * output.writePacketSync(packet);
644
+ * packet.free();
645
+ * }
646
+ * // Filter remains open, buffered packets not flushed
647
+ * ```
588
648
  *
589
- * for (const frame of decoder.framesSync(filter.packetsSync(packets))) {
590
- * // Process frames
591
- * frame.free();
649
+ * @example
650
+ * ```typescript
651
+ * // Explicit flush with EOF
652
+ * for (const packet of filter.packetsSync(null)) {
653
+ * if (packet === null) {
654
+ * console.log('All buffered packets flushed');
655
+ * break;
656
+ * }
657
+ * console.log('Buffered packet:', packet.pts);
658
+ * output.writePacketSync(packet);
659
+ * packet.free();
592
660
  * }
593
661
  * ```
594
662
  *
663
+ * @see {@link filterSync} For single packet filtering
595
664
  * @see {@link packets} For async version
596
665
  */
597
666
  *packetsSync(packets) {
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;
609
- }
610
- // Signal EOF and stop processing
611
- yield null;
612
- return;
667
+ const self = this;
668
+ const processPacket = function* (packet) {
669
+ self.filterSync(packet);
670
+ while (true) {
671
+ const outPacket = self.receiveSync();
672
+ if (!outPacket)
673
+ break;
674
+ yield outPacket;
613
675
  }
614
- if (this.isClosed) {
615
- break;
676
+ }.bind(this);
677
+ const finalize = function* () {
678
+ for (const remaining of self.flushPacketsSync()) {
679
+ yield remaining;
616
680
  }
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;
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');
681
+ yield null;
682
+ }.bind(this);
683
+ if (packets === null) {
684
+ yield* finalize();
685
+ return;
686
+ }
687
+ if (packets instanceof Packet) {
688
+ yield* processPacket(packets);
689
+ return;
690
+ }
691
+ for (const packet_2 of packets) {
692
+ const env_2 = { stack: [], error: void 0, hasError: false };
693
+ try {
694
+ const packet = __addDisposableResource(env_2, packet_2, false);
695
+ if (packet === null) {
696
+ yield* finalize();
697
+ return;
632
698
  }
699
+ yield* processPacket(packet);
633
700
  }
634
- else if (sendRet < 0 && sendRet !== AVERROR_EOF) {
635
- FFmpegError.throwIfError(sendRet, 'Failed to send packet to bitstream filter');
701
+ catch (e_2) {
702
+ env_2.error = e_2;
703
+ env_2.hasError = true;
636
704
  }
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;
705
+ finally {
706
+ __disposeResources(env_2);
643
707
  }
644
708
  }
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;
652
- }
653
- // Signal EOF
654
- yield null;
655
709
  }
656
710
  /**
657
711
  * Flush filter and signal end-of-stream.
@@ -1074,9 +1128,9 @@ export class BitStreamFilterAPI {
1074
1128
  try {
1075
1129
  // Outer loop - receive packets
1076
1130
  while (!this.inputQueue.isClosed) {
1077
- const env_1 = { stack: [], error: void 0, hasError: false };
1131
+ const env_3 = { stack: [], error: void 0, hasError: false };
1078
1132
  try {
1079
- const packet = __addDisposableResource(env_1, await this.inputQueue.receive(), false);
1133
+ const packet = __addDisposableResource(env_3, await this.inputQueue.receive(), false);
1080
1134
  if (!packet)
1081
1135
  break;
1082
1136
  if (this.isClosed) {
@@ -1110,12 +1164,12 @@ export class BitStreamFilterAPI {
1110
1164
  await this.outputQueue.send(outPacket);
1111
1165
  }
1112
1166
  }
1113
- catch (e_1) {
1114
- env_1.error = e_1;
1115
- env_1.hasError = true;
1167
+ catch (e_3) {
1168
+ env_3.error = e_3;
1169
+ env_3.hasError = true;
1116
1170
  }
1117
1171
  finally {
1118
- __disposeResources(env_1);
1172
+ __disposeResources(env_3);
1119
1173
  }
1120
1174
  }
1121
1175
  // Flush filter at end