node-av 4.0.0 → 5.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 +23 -0
- package/binding.gyp +19 -11
- package/dist/api/bitstream-filter.d.ts +13 -12
- package/dist/api/bitstream-filter.js +33 -29
- package/dist/api/bitstream-filter.js.map +1 -1
- package/dist/api/decoder.d.ts +211 -96
- package/dist/api/decoder.js +396 -375
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/demuxer.d.ts +10 -10
- package/dist/api/demuxer.js +7 -10
- package/dist/api/demuxer.js.map +1 -1
- package/dist/api/encoder.d.ts +155 -122
- package/dist/api/encoder.js +368 -541
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-complex.d.ts +769 -0
- package/dist/api/filter-complex.js +1596 -0
- package/dist/api/filter-complex.js.map +1 -0
- package/dist/api/filter-presets.d.ts +68 -0
- package/dist/api/filter-presets.js +96 -0
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.d.ts +183 -113
- package/dist/api/filter.js +347 -365
- package/dist/api/filter.js.map +1 -1
- package/dist/api/fmp4-stream.d.ts +2 -2
- package/dist/api/fmp4-stream.js.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +3 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/io-stream.d.ts +3 -3
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/muxer.d.ts +10 -10
- package/dist/api/muxer.js +6 -6
- package/dist/api/muxer.js.map +1 -1
- package/dist/api/pipeline.d.ts +2 -2
- package/dist/api/pipeline.js +22 -22
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/rtp-stream.d.ts +2 -2
- package/dist/api/rtp-stream.js.map +1 -1
- package/dist/api/types.d.ts +63 -7
- package/dist/api/utilities/audio-sample.d.ts +10 -0
- package/dist/api/utilities/audio-sample.js +10 -0
- package/dist/api/utilities/audio-sample.js.map +1 -1
- package/dist/api/utilities/channel-layout.d.ts +1 -0
- package/dist/api/utilities/channel-layout.js +1 -0
- package/dist/api/utilities/channel-layout.js.map +1 -1
- package/dist/api/utilities/image.d.ts +38 -0
- package/dist/api/utilities/image.js +38 -0
- package/dist/api/utilities/image.js.map +1 -1
- package/dist/api/utilities/index.d.ts +1 -0
- package/dist/api/utilities/index.js +2 -0
- package/dist/api/utilities/index.js.map +1 -1
- package/dist/api/utilities/media-type.d.ts +1 -0
- package/dist/api/utilities/media-type.js +1 -0
- package/dist/api/utilities/media-type.js.map +1 -1
- package/dist/api/utilities/pixel-format.d.ts +3 -0
- package/dist/api/utilities/pixel-format.js +3 -0
- package/dist/api/utilities/pixel-format.js.map +1 -1
- package/dist/api/utilities/sample-format.d.ts +5 -0
- package/dist/api/utilities/sample-format.js +5 -0
- package/dist/api/utilities/sample-format.js.map +1 -1
- package/dist/api/utilities/scheduler.d.ts +21 -52
- package/dist/api/utilities/scheduler.js +20 -58
- package/dist/api/utilities/scheduler.js.map +1 -1
- package/dist/api/utilities/streaming.d.ts +32 -1
- package/dist/api/utilities/streaming.js +32 -1
- package/dist/api/utilities/streaming.js.map +1 -1
- package/dist/api/utilities/timestamp.d.ts +14 -0
- package/dist/api/utilities/timestamp.js +14 -0
- package/dist/api/utilities/timestamp.js.map +1 -1
- package/dist/api/utilities/whisper-model.d.ts +310 -0
- package/dist/api/utilities/whisper-model.js +528 -0
- package/dist/api/utilities/whisper-model.js.map +1 -0
- package/dist/api/whisper.d.ts +324 -0
- package/dist/api/whisper.js +362 -0
- package/dist/api/whisper.js.map +1 -0
- package/dist/constants/constants.d.ts +3 -1
- package/dist/constants/constants.js +1 -0
- package/dist/constants/constants.js.map +1 -1
- package/dist/ffmpeg/index.d.ts +3 -3
- package/dist/ffmpeg/index.js +3 -3
- package/dist/ffmpeg/utils.d.ts +27 -0
- package/dist/ffmpeg/utils.js +28 -16
- package/dist/ffmpeg/utils.js.map +1 -1
- package/dist/lib/binding.d.ts +4 -4
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/codec-parameters.d.ts +47 -1
- package/dist/lib/codec-parameters.js +55 -0
- package/dist/lib/codec-parameters.js.map +1 -1
- 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/frame.d.ts +96 -1
- package/dist/lib/frame.js +139 -1
- package/dist/lib/frame.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/native-types.d.ts +29 -2
- package/dist/lib/rational.d.ts +18 -0
- package/dist/lib/rational.js +19 -0
- package/dist/lib/rational.js.map +1 -1
- package/dist/lib/types.d.ts +23 -1
- package/install/check.js +2 -2
- package/package.json +30 -20
package/dist/api/encoder.js
CHANGED
|
@@ -50,11 +50,12 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
50
50
|
var e = new Error(message);
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
|
-
import { AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, AV_CODEC_CAP_PARAM_CHANGE, AV_CODEC_FLAG_COPY_OPAQUE, AV_CODEC_FLAG_FRAME_DURATION, AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, AV_PIX_FMT_NONE, AV_PKT_FLAG_TRUSTED, AVCHROMA_LOC_UNSPECIFIED, AVERROR_EAGAIN, AVERROR_EOF, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO, } from '../constants/constants.js';
|
|
53
|
+
import { AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, AV_CODEC_CAP_PARAM_CHANGE, AV_CODEC_FLAG_COPY_OPAQUE, AV_CODEC_FLAG_FRAME_DURATION, AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, AV_PICTURE_TYPE_NONE, AV_PIX_FMT_NONE, AV_PKT_FLAG_TRUSTED, AVCHROMA_LOC_UNSPECIFIED, AVERROR_EAGAIN, AVERROR_EOF, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO, EOF, } from '../constants/constants.js';
|
|
54
54
|
import { CodecContext } from '../lib/codec-context.js';
|
|
55
55
|
import { Codec } from '../lib/codec.js';
|
|
56
56
|
import { Dictionary } from '../lib/dictionary.js';
|
|
57
57
|
import { FFmpegError } from '../lib/error.js';
|
|
58
|
+
import { Frame } from '../lib/frame.js';
|
|
58
59
|
import { Packet } from '../lib/packet.js';
|
|
59
60
|
import { Rational } from '../lib/rational.js';
|
|
60
61
|
import { avRescaleQ } from '../lib/utilities.js';
|
|
@@ -542,28 +543,31 @@ export class Encoder {
|
|
|
542
543
|
return this.initialized && !this.isClosed;
|
|
543
544
|
}
|
|
544
545
|
/**
|
|
545
|
-
*
|
|
546
|
+
* Send a frame to the encoder.
|
|
546
547
|
*
|
|
547
|
-
* Sends a frame to the encoder
|
|
548
|
+
* Sends a raw frame to the encoder for encoding.
|
|
549
|
+
* Does not return encoded packets - use {@link receive} to retrieve packets.
|
|
548
550
|
* On first frame, automatically initializes encoder with frame properties.
|
|
549
|
-
*
|
|
551
|
+
* A single frame can produce zero, one, or multiple packets depending on codec buffering.
|
|
550
552
|
*
|
|
551
|
-
* **
|
|
552
|
-
*
|
|
553
|
-
* To receive all packets from a frame, use {@link encodeAll} or {@link packets} instead.
|
|
553
|
+
* **Important**: This method only SENDS the frame to the encoder.
|
|
554
|
+
* You must call {@link receive} separately (potentially multiple times) to get encoded packets.
|
|
554
555
|
*
|
|
555
|
-
* Direct mapping to avcodec_send_frame()
|
|
556
|
-
*
|
|
557
|
-
* @param frame - Raw frame to encode (or null to flush)
|
|
556
|
+
* Direct mapping to avcodec_send_frame().
|
|
558
557
|
*
|
|
559
|
-
* @
|
|
558
|
+
* @param frame - Raw frame to send to encoder
|
|
560
559
|
*
|
|
561
|
-
* @throws {FFmpegError} If
|
|
560
|
+
* @throws {FFmpegError} If sending frame fails
|
|
562
561
|
*
|
|
563
562
|
* @example
|
|
564
563
|
* ```typescript
|
|
565
|
-
*
|
|
566
|
-
*
|
|
564
|
+
* // Send frame and receive packets
|
|
565
|
+
* await encoder.encode(frame);
|
|
566
|
+
*
|
|
567
|
+
* // Receive all available packets
|
|
568
|
+
* while (true) {
|
|
569
|
+
* const packet = await encoder.receive();
|
|
570
|
+
* if (!packet) break;
|
|
567
571
|
* console.log(`Encoded packet with PTS: ${packet.pts}`);
|
|
568
572
|
* await output.writePacket(packet);
|
|
569
573
|
* packet.free();
|
|
@@ -572,10 +576,13 @@ export class Encoder {
|
|
|
572
576
|
*
|
|
573
577
|
* @example
|
|
574
578
|
* ```typescript
|
|
575
|
-
* // Encode loop
|
|
576
579
|
* for await (const frame of decoder.frames(input.packets())) {
|
|
577
|
-
*
|
|
578
|
-
*
|
|
580
|
+
* // Send frame
|
|
581
|
+
* await encoder.encode(frame);
|
|
582
|
+
*
|
|
583
|
+
* // Receive available packets
|
|
584
|
+
* let packet;
|
|
585
|
+
* while ((packet = await encoder.receive())) {
|
|
579
586
|
* await output.writePacket(packet);
|
|
580
587
|
* packet.free();
|
|
581
588
|
* }
|
|
@@ -583,131 +590,98 @@ export class Encoder {
|
|
|
583
590
|
* }
|
|
584
591
|
* ```
|
|
585
592
|
*
|
|
586
|
-
* @see {@link
|
|
593
|
+
* @see {@link receive} For receiving encoded packets
|
|
594
|
+
* @see {@link encodeAll} For combined send+receive operation
|
|
587
595
|
* @see {@link packets} For automatic frame iteration
|
|
588
596
|
* @see {@link flush} For end-of-stream handling
|
|
589
597
|
* @see {@link encodeSync} For synchronous version
|
|
590
598
|
*/
|
|
591
599
|
async encode(frame) {
|
|
592
600
|
if (this.isClosed) {
|
|
593
|
-
return
|
|
601
|
+
return;
|
|
594
602
|
}
|
|
595
603
|
// Open encoder if not already done
|
|
596
|
-
|
|
597
|
-
if (!frame) {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
this.initializePromise ??= this.initialize(frame);
|
|
601
|
-
}
|
|
604
|
+
this.initializePromise ??= this.initialize(frame);
|
|
602
605
|
await this.initializePromise;
|
|
603
606
|
// Prepare frame for encoding (set quality, validate channel count)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
// Unlike FFmpeg CLI which reads ALL packets in a loop, our encode() returns
|
|
611
|
-
// only one packet at a time. This means the encoder can still have packets
|
|
612
|
-
// from previous frames when we try to send a new frame.
|
|
613
|
-
if (sendRet === AVERROR_EAGAIN) {
|
|
614
|
-
// Encoder is full, receive a packet first
|
|
615
|
-
const packet = await this.receive();
|
|
616
|
-
if (packet) {
|
|
617
|
-
return packet;
|
|
607
|
+
this.prepareFrameForEncoding(frame);
|
|
608
|
+
const encode = async (newFrame) => {
|
|
609
|
+
const sendRet = await this.codecContext.sendFrame(newFrame);
|
|
610
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
611
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send frame to encoder');
|
|
612
|
+
return;
|
|
618
613
|
}
|
|
619
|
-
|
|
620
|
-
|
|
614
|
+
};
|
|
615
|
+
if (this.audioFrameBuffer) {
|
|
616
|
+
// Push frame into buffer - actual sending happens in receive()
|
|
617
|
+
await this.audioFrameBuffer.push(frame);
|
|
621
618
|
}
|
|
622
|
-
|
|
623
|
-
|
|
619
|
+
else {
|
|
620
|
+
await encode(frame);
|
|
624
621
|
}
|
|
625
|
-
// Try to receive packet
|
|
626
|
-
return await this.receive();
|
|
627
622
|
}
|
|
628
623
|
/**
|
|
629
|
-
*
|
|
624
|
+
* Send a frame to the encoder synchronously.
|
|
630
625
|
* Synchronous version of encode.
|
|
631
626
|
*
|
|
632
|
-
* Sends a frame to the encoder
|
|
627
|
+
* Sends a raw frame to the encoder for encoding.
|
|
628
|
+
* Does not return encoded packets - use {@link receiveSync} to retrieve packets.
|
|
633
629
|
* On first frame, automatically initializes encoder with frame properties.
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
* **Note**: This method receives only ONE packet per call.
|
|
637
|
-
* A single frame can produce multiple packets (e.g., B-frames, codec buffering).
|
|
638
|
-
* To receive all packets from a frame, use {@link encodeAllSync} or {@link packetsSync} instead.
|
|
630
|
+
* A single frame can produce zero, one, or multiple packets depending on codec buffering.
|
|
639
631
|
*
|
|
640
|
-
*
|
|
632
|
+
* **Important**: This method only SENDS the frame to the encoder.
|
|
633
|
+
* You must call {@link receiveSync} separately (potentially multiple times) to get encoded packets.
|
|
641
634
|
*
|
|
642
|
-
*
|
|
635
|
+
* Direct mapping to avcodec_send_frame().
|
|
643
636
|
*
|
|
644
|
-
* @
|
|
637
|
+
* @param frame - Raw frame to send to encoder
|
|
645
638
|
*
|
|
646
|
-
* @throws {FFmpegError} If
|
|
639
|
+
* @throws {FFmpegError} If sending frame fails
|
|
647
640
|
*
|
|
648
641
|
* @example
|
|
649
642
|
* ```typescript
|
|
650
|
-
*
|
|
651
|
-
*
|
|
643
|
+
* // Send frame and receive packets
|
|
644
|
+
* encoder.encodeSync(frame);
|
|
645
|
+
*
|
|
646
|
+
* // Receive all available packets
|
|
647
|
+
* let packet;
|
|
648
|
+
* while ((packet = encoder.receiveSync())) {
|
|
652
649
|
* console.log(`Encoded packet with PTS: ${packet.pts}`);
|
|
653
650
|
* output.writePacketSync(packet);
|
|
654
651
|
* packet.free();
|
|
655
652
|
* }
|
|
656
653
|
* ```
|
|
657
654
|
*
|
|
658
|
-
* @
|
|
659
|
-
*
|
|
660
|
-
* // Encode loop
|
|
661
|
-
* for (const frame of decoder.framesSync(packets)) {
|
|
662
|
-
* const packet = encoder.encodeSync(frame);
|
|
663
|
-
* if (packet) {
|
|
664
|
-
* output.writePacketSync(packet);
|
|
665
|
-
* packet.free();
|
|
666
|
-
* }
|
|
667
|
-
* frame.free();
|
|
668
|
-
* }
|
|
669
|
-
* ```
|
|
670
|
-
*
|
|
671
|
-
* @see {@link encodeAllSync} For multiple packet encoding
|
|
655
|
+
* @see {@link receiveSync} For receiving encoded packets
|
|
656
|
+
* @see {@link encodeAllSync} For combined send+receive operation
|
|
672
657
|
* @see {@link packetsSync} For automatic frame iteration
|
|
673
658
|
* @see {@link flushSync} For end-of-stream handling
|
|
674
659
|
* @see {@link encode} For async version
|
|
675
660
|
*/
|
|
676
661
|
encodeSync(frame) {
|
|
677
662
|
if (this.isClosed) {
|
|
678
|
-
return
|
|
663
|
+
return;
|
|
679
664
|
}
|
|
680
665
|
// Open encoder if not already done
|
|
681
666
|
if (!this.initialized) {
|
|
682
|
-
if (!frame) {
|
|
683
|
-
return null;
|
|
684
|
-
}
|
|
685
667
|
this.initializeSync(frame);
|
|
686
668
|
}
|
|
687
669
|
// Prepare frame for encoding (set quality, validate channel count)
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
// Unlike FFmpeg CLI which reads ALL packets in a loop, our encode() returns
|
|
695
|
-
// only one packet at a time. This means the encoder can still have packets
|
|
696
|
-
// from previous frames when we try to send a new frame.
|
|
697
|
-
if (sendRet === AVERROR_EAGAIN) {
|
|
698
|
-
// Encoder is full, receive a packet first
|
|
699
|
-
const packet = this.receiveSync();
|
|
700
|
-
if (packet) {
|
|
701
|
-
return packet;
|
|
670
|
+
this.prepareFrameForEncoding(frame);
|
|
671
|
+
const encode = (newFrame) => {
|
|
672
|
+
const sendRet = this.codecContext.sendFrameSync(newFrame);
|
|
673
|
+
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
674
|
+
FFmpegError.throwIfError(sendRet, 'Failed to send frame to encoder');
|
|
675
|
+
return;
|
|
702
676
|
}
|
|
703
|
-
|
|
704
|
-
|
|
677
|
+
};
|
|
678
|
+
if (this.audioFrameBuffer) {
|
|
679
|
+
// Push frame into buffer - actual sending happens in receiveSync()
|
|
680
|
+
this.audioFrameBuffer.pushSync(frame);
|
|
705
681
|
}
|
|
706
|
-
|
|
707
|
-
|
|
682
|
+
else {
|
|
683
|
+
encode(frame);
|
|
708
684
|
}
|
|
709
|
-
// Try to receive packet
|
|
710
|
-
return this.receiveSync();
|
|
711
685
|
}
|
|
712
686
|
/**
|
|
713
687
|
* Encode a frame to packets.
|
|
@@ -754,68 +728,19 @@ export class Encoder {
|
|
|
754
728
|
* @see {@link encodeAllSync} For synchronous version
|
|
755
729
|
*/
|
|
756
730
|
async encodeAll(frame) {
|
|
757
|
-
if (this.isClosed) {
|
|
758
|
-
return [];
|
|
759
|
-
}
|
|
760
|
-
// Open encoder if not already done
|
|
761
|
-
if (!this.initialized) {
|
|
762
|
-
if (!frame) {
|
|
763
|
-
return [];
|
|
764
|
-
}
|
|
765
|
-
this.initializePromise ??= this.initialize(frame);
|
|
766
|
-
}
|
|
767
|
-
await this.initializePromise;
|
|
768
|
-
// Prepare frame for encoding (set quality, validate channel count)
|
|
769
731
|
if (frame) {
|
|
770
|
-
this.
|
|
771
|
-
}
|
|
772
|
-
// If audio encoder with fixed frame size, use AudioFrameBuffer
|
|
773
|
-
if (this.audioFrameBuffer && frame) {
|
|
774
|
-
// Push frame into buffer
|
|
775
|
-
await this.audioFrameBuffer.push(frame);
|
|
776
|
-
// Pull and encode all available fixed-size frames
|
|
777
|
-
const packets = [];
|
|
778
|
-
let _bufferedFrame;
|
|
779
|
-
while (!this.isClosed && (_bufferedFrame = await this.audioFrameBuffer.pull()) !== null) {
|
|
780
|
-
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
781
|
-
try {
|
|
782
|
-
const bufferedFrame = __addDisposableResource(env_1, _bufferedFrame, false);
|
|
783
|
-
// Send buffered frame to encoder
|
|
784
|
-
const sendRet = await this.codecContext.sendFrame(bufferedFrame);
|
|
785
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
786
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
787
|
-
}
|
|
788
|
-
// Receive packets
|
|
789
|
-
while (true) {
|
|
790
|
-
const packet = await this.receive();
|
|
791
|
-
if (!packet)
|
|
792
|
-
break;
|
|
793
|
-
packets.push(packet);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
catch (e_1) {
|
|
797
|
-
env_1.error = e_1;
|
|
798
|
-
env_1.hasError = true;
|
|
799
|
-
}
|
|
800
|
-
finally {
|
|
801
|
-
__disposeResources(env_1);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
return packets;
|
|
732
|
+
await this.encode(frame);
|
|
805
733
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
809
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame to encoder');
|
|
810
|
-
return [];
|
|
734
|
+
else {
|
|
735
|
+
await this.flush();
|
|
811
736
|
}
|
|
812
737
|
// Receive all available packets
|
|
813
738
|
const packets = [];
|
|
814
739
|
while (true) {
|
|
815
740
|
const packet = await this.receive();
|
|
816
741
|
if (!packet)
|
|
817
|
-
break;
|
|
818
|
-
packets.push(packet);
|
|
742
|
+
break; // Stop on EAGAIN or EOF
|
|
743
|
+
packets.push(packet); // Only push actual packets
|
|
819
744
|
}
|
|
820
745
|
return packets;
|
|
821
746
|
}
|
|
@@ -865,67 +790,19 @@ export class Encoder {
|
|
|
865
790
|
* @see {@link encodeAll} For async version
|
|
866
791
|
*/
|
|
867
792
|
encodeAllSync(frame) {
|
|
868
|
-
if (this.isClosed) {
|
|
869
|
-
return [];
|
|
870
|
-
}
|
|
871
|
-
// Open encoder if not already done
|
|
872
|
-
if (!this.initialized) {
|
|
873
|
-
if (!frame) {
|
|
874
|
-
return [];
|
|
875
|
-
}
|
|
876
|
-
this.initializeSync(frame);
|
|
877
|
-
}
|
|
878
|
-
// Prepare frame for encoding (set quality, validate channel count)
|
|
879
793
|
if (frame) {
|
|
880
|
-
this.
|
|
794
|
+
this.encodeSync(frame);
|
|
881
795
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
// Push frame into buffer
|
|
885
|
-
this.audioFrameBuffer.pushSync(frame);
|
|
886
|
-
// Pull and encode all available fixed-size frames
|
|
887
|
-
const packets = [];
|
|
888
|
-
let _bufferedFrame;
|
|
889
|
-
while (!this.isClosed && (_bufferedFrame = this.audioFrameBuffer.pullSync()) !== null) {
|
|
890
|
-
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
891
|
-
try {
|
|
892
|
-
const bufferedFrame = __addDisposableResource(env_2, _bufferedFrame, false);
|
|
893
|
-
// Send buffered frame to encoder
|
|
894
|
-
const sendRet = this.codecContext.sendFrameSync(bufferedFrame);
|
|
895
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
896
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
897
|
-
}
|
|
898
|
-
// Receive packets
|
|
899
|
-
while (true) {
|
|
900
|
-
const packet = this.receiveSync();
|
|
901
|
-
if (!packet)
|
|
902
|
-
break;
|
|
903
|
-
packets.push(packet);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
catch (e_2) {
|
|
907
|
-
env_2.error = e_2;
|
|
908
|
-
env_2.hasError = true;
|
|
909
|
-
}
|
|
910
|
-
finally {
|
|
911
|
-
__disposeResources(env_2);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
return packets;
|
|
915
|
-
}
|
|
916
|
-
// Send frame first, error immediately if send fails
|
|
917
|
-
const sendRet = this.codecContext.sendFrameSync(frame);
|
|
918
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF) {
|
|
919
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame to encoder');
|
|
920
|
-
return [];
|
|
796
|
+
else {
|
|
797
|
+
this.flushSync();
|
|
921
798
|
}
|
|
922
799
|
// Receive all available packets
|
|
923
800
|
const packets = [];
|
|
924
801
|
while (true) {
|
|
925
802
|
const packet = this.receiveSync();
|
|
926
803
|
if (!packet)
|
|
927
|
-
break;
|
|
928
|
-
packets.push(packet);
|
|
804
|
+
break; // Stop on EAGAIN or EOF
|
|
805
|
+
packets.push(packet); // Only push actual packets
|
|
929
806
|
}
|
|
930
807
|
return packets;
|
|
931
808
|
}
|
|
@@ -933,20 +810,28 @@ export class Encoder {
|
|
|
933
810
|
* Encode frame stream to packet stream.
|
|
934
811
|
*
|
|
935
812
|
* High-level async generator for complete encoding pipeline.
|
|
936
|
-
*
|
|
937
|
-
* and flushes buffered packets at end.
|
|
813
|
+
* Encoder is only flushed when EOF (null) signal is explicitly received.
|
|
938
814
|
* Primary interface for stream-based encoding.
|
|
939
815
|
*
|
|
940
|
-
*
|
|
816
|
+
* **EOF Handling:**
|
|
817
|
+
* - Send null to flush encoder and get remaining buffered packets
|
|
818
|
+
* - Generator yields null after flushing when null is received
|
|
819
|
+
* - No automatic flushing - encoder stays open until EOF or close()
|
|
820
|
+
*
|
|
821
|
+
* @param frames - Async iterable of frames, single frame, or null to flush
|
|
941
822
|
*
|
|
942
|
-
* @yields {Packet} Encoded packets
|
|
823
|
+
* @yields {Packet | null} Encoded packets, followed by null when explicitly flushed
|
|
943
824
|
*
|
|
944
825
|
* @throws {FFmpegError} If encoding fails
|
|
945
826
|
*
|
|
946
827
|
* @example
|
|
947
828
|
* ```typescript
|
|
948
|
-
* //
|
|
829
|
+
* // Stream of frames with automatic EOF propagation
|
|
949
830
|
* for await (const packet of encoder.packets(decoder.frames(input.packets()))) {
|
|
831
|
+
* if (packet === null) {
|
|
832
|
+
* console.log('Encoder flushed');
|
|
833
|
+
* break;
|
|
834
|
+
* }
|
|
950
835
|
* await output.writePacket(packet);
|
|
951
836
|
* packet.free(); // Must free output packets
|
|
952
837
|
* }
|
|
@@ -954,35 +839,26 @@ export class Encoder {
|
|
|
954
839
|
*
|
|
955
840
|
* @example
|
|
956
841
|
* ```typescript
|
|
957
|
-
* //
|
|
958
|
-
*
|
|
959
|
-
* for await (const frame of decoder.frames(input.packets())) {
|
|
960
|
-
* const filtered = await filter.process(frame);
|
|
961
|
-
* if (filtered) {
|
|
962
|
-
* yield filtered;
|
|
963
|
-
* }
|
|
964
|
-
* frame.free();
|
|
965
|
-
* }
|
|
966
|
-
* }
|
|
967
|
-
*
|
|
968
|
-
* for await (const packet of encoder.packets(filteredFrames())) {
|
|
842
|
+
* // Single frame - no automatic flush
|
|
843
|
+
* for await (const packet of encoder.packets(singleFrame)) {
|
|
969
844
|
* await output.writePacket(packet);
|
|
970
845
|
* packet.free();
|
|
971
846
|
* }
|
|
847
|
+
* // Encoder remains open, buffered packets not flushed
|
|
972
848
|
* ```
|
|
973
849
|
*
|
|
974
850
|
* @example
|
|
975
851
|
* ```typescript
|
|
976
|
-
* //
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
* output
|
|
984
|
-
*
|
|
985
|
-
*
|
|
852
|
+
* // Explicit flush with EOF
|
|
853
|
+
* for await (const packet of encoder.packets(null)) {
|
|
854
|
+
* if (packet === null) {
|
|
855
|
+
* console.log('All buffered packets flushed');
|
|
856
|
+
* break;
|
|
857
|
+
* }
|
|
858
|
+
* console.log('Buffered packet:', packet.pts);
|
|
859
|
+
* await output.writePacket(packet);
|
|
860
|
+
* packet.free();
|
|
861
|
+
* }
|
|
986
862
|
* ```
|
|
987
863
|
*
|
|
988
864
|
* @see {@link encode} For single frame encoding
|
|
@@ -990,123 +866,76 @@ export class Encoder {
|
|
|
990
866
|
* @see {@link packetsSync} For sync version
|
|
991
867
|
*/
|
|
992
868
|
async *packets(frames) {
|
|
993
|
-
|
|
869
|
+
const self = this;
|
|
870
|
+
const processFrame = async function* (frame) {
|
|
871
|
+
await self.encode(frame);
|
|
872
|
+
while (true) {
|
|
873
|
+
const packet = await self.receive();
|
|
874
|
+
if (!packet)
|
|
875
|
+
break;
|
|
876
|
+
yield packet;
|
|
877
|
+
}
|
|
878
|
+
}.bind(this);
|
|
879
|
+
const finalize = async function* () {
|
|
880
|
+
for await (const remaining of self.flushPackets()) {
|
|
881
|
+
yield remaining;
|
|
882
|
+
}
|
|
883
|
+
yield null;
|
|
884
|
+
}.bind(this);
|
|
885
|
+
if (frames === null) {
|
|
886
|
+
yield* finalize();
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (frames instanceof Frame) {
|
|
890
|
+
yield* processFrame(frames);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
994
893
|
for await (const frame_1 of frames) {
|
|
995
|
-
const
|
|
894
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
996
895
|
try {
|
|
997
|
-
const frame = __addDisposableResource(
|
|
998
|
-
// Handle EOF signal
|
|
896
|
+
const frame = __addDisposableResource(env_1, frame_1, false);
|
|
999
897
|
if (frame === null) {
|
|
1000
|
-
|
|
1001
|
-
await this.flush();
|
|
1002
|
-
while (true) {
|
|
1003
|
-
const remaining = await this.receive();
|
|
1004
|
-
if (!remaining)
|
|
1005
|
-
break;
|
|
1006
|
-
yield remaining;
|
|
1007
|
-
}
|
|
1008
|
-
// Signal EOF and stop processing
|
|
1009
|
-
yield null;
|
|
898
|
+
yield* finalize();
|
|
1010
899
|
return;
|
|
1011
900
|
}
|
|
1012
|
-
|
|
1013
|
-
break;
|
|
1014
|
-
}
|
|
1015
|
-
// Open encoder if not already done
|
|
1016
|
-
if (!this.initialized) {
|
|
1017
|
-
this.initializePromise ??= this.initialize(frame);
|
|
1018
|
-
}
|
|
1019
|
-
await this.initializePromise;
|
|
1020
|
-
// Prepare frame for encoding (set quality, validate channel count)
|
|
1021
|
-
if (frame) {
|
|
1022
|
-
this.prepareFrameForEncoding(frame);
|
|
1023
|
-
}
|
|
1024
|
-
// If audio encoder with fixed frame size, use AudioFrameBuffer
|
|
1025
|
-
if (this.audioFrameBuffer) {
|
|
1026
|
-
// Push frame into buffer
|
|
1027
|
-
await this.audioFrameBuffer.push(frame);
|
|
1028
|
-
// Pull and encode all available fixed-size frames
|
|
1029
|
-
let _bufferedFrame;
|
|
1030
|
-
while (!this.isClosed && (_bufferedFrame = await this.audioFrameBuffer.pull()) !== null) {
|
|
1031
|
-
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1032
|
-
try {
|
|
1033
|
-
const bufferedFrame = __addDisposableResource(env_4, _bufferedFrame, false);
|
|
1034
|
-
// Send buffered frame to encoder
|
|
1035
|
-
const sendRet = await this.codecContext.sendFrame(bufferedFrame);
|
|
1036
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1037
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
1038
|
-
}
|
|
1039
|
-
// Receive packets
|
|
1040
|
-
while (true) {
|
|
1041
|
-
const packet = await this.receive();
|
|
1042
|
-
if (!packet)
|
|
1043
|
-
break;
|
|
1044
|
-
yield packet;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
catch (e_3) {
|
|
1048
|
-
env_4.error = e_3;
|
|
1049
|
-
env_4.hasError = true;
|
|
1050
|
-
}
|
|
1051
|
-
finally {
|
|
1052
|
-
__disposeResources(env_4);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
else {
|
|
1057
|
-
// Send frame to encoder
|
|
1058
|
-
const sendRet = await this.codecContext.sendFrame(frame);
|
|
1059
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1060
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
1061
|
-
}
|
|
1062
|
-
// Receive ALL packets
|
|
1063
|
-
// A single frame can produce multiple packets (e.g., B-frames, lookahead)
|
|
1064
|
-
while (true) {
|
|
1065
|
-
const packet = await this.receive();
|
|
1066
|
-
if (!packet)
|
|
1067
|
-
break;
|
|
1068
|
-
yield packet;
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
901
|
+
yield* processFrame(frame);
|
|
1071
902
|
}
|
|
1072
|
-
catch (
|
|
1073
|
-
|
|
1074
|
-
|
|
903
|
+
catch (e_1) {
|
|
904
|
+
env_1.error = e_1;
|
|
905
|
+
env_1.hasError = true;
|
|
1075
906
|
}
|
|
1076
907
|
finally {
|
|
1077
|
-
__disposeResources(
|
|
908
|
+
__disposeResources(env_1);
|
|
1078
909
|
}
|
|
1079
910
|
}
|
|
1080
|
-
// Flush encoder after all frames (fallback if no null was sent)
|
|
1081
|
-
await this.flush();
|
|
1082
|
-
while (true) {
|
|
1083
|
-
const remaining = await this.receive();
|
|
1084
|
-
if (!remaining)
|
|
1085
|
-
break;
|
|
1086
|
-
yield remaining;
|
|
1087
|
-
}
|
|
1088
|
-
// Signal EOF
|
|
1089
|
-
yield null;
|
|
1090
911
|
}
|
|
1091
912
|
/**
|
|
1092
913
|
* Encode frame stream to packet stream synchronously.
|
|
1093
914
|
* Synchronous version of packets.
|
|
1094
915
|
*
|
|
1095
916
|
* High-level sync generator for complete encoding pipeline.
|
|
1096
|
-
*
|
|
1097
|
-
* and flushes buffered packets at end.
|
|
917
|
+
* Encoder is only flushed when EOF (null) signal is explicitly received.
|
|
1098
918
|
* Primary interface for stream-based encoding.
|
|
1099
919
|
*
|
|
1100
|
-
*
|
|
920
|
+
* **EOF Handling:**
|
|
921
|
+
* - Send null to flush encoder and get remaining buffered packets
|
|
922
|
+
* - Generator yields null after flushing when null is received
|
|
923
|
+
* - No automatic flushing - encoder stays open until EOF or close()
|
|
1101
924
|
*
|
|
1102
|
-
* @
|
|
925
|
+
* @param frames - Iterable of frames, single frame, or null to flush
|
|
926
|
+
*
|
|
927
|
+
* @yields {Packet | null} Encoded packets, followed by null when explicitly flushed
|
|
1103
928
|
*
|
|
1104
929
|
* @throws {FFmpegError} If encoding fails
|
|
1105
930
|
*
|
|
1106
931
|
* @example
|
|
1107
932
|
* ```typescript
|
|
1108
|
-
* //
|
|
933
|
+
* // Stream of frames with automatic EOF propagation
|
|
1109
934
|
* for (const packet of encoder.packetsSync(decoder.framesSync(packets))) {
|
|
935
|
+
* if (packet === null) {
|
|
936
|
+
* console.log('Encoder flushed');
|
|
937
|
+
* break;
|
|
938
|
+
* }
|
|
1110
939
|
* output.writePacketSync(packet);
|
|
1111
940
|
* packet.free(); // Must free output packets
|
|
1112
941
|
* }
|
|
@@ -1114,18 +943,23 @@ export class Encoder {
|
|
|
1114
943
|
*
|
|
1115
944
|
* @example
|
|
1116
945
|
* ```typescript
|
|
1117
|
-
* //
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1121
|
-
* if (filtered) {
|
|
1122
|
-
* yield filtered;
|
|
1123
|
-
* }
|
|
1124
|
-
* frame.free();
|
|
1125
|
-
* }
|
|
946
|
+
* // Single frame - no automatic flush
|
|
947
|
+
* for (const packet of encoder.packetsSync(singleFrame)) {
|
|
948
|
+
* output.writePacketSync(packet);
|
|
949
|
+
* packet.free();
|
|
1126
950
|
* }
|
|
951
|
+
* // Encoder remains open, buffered packets not flushed
|
|
952
|
+
* ```
|
|
1127
953
|
*
|
|
1128
|
-
*
|
|
954
|
+
* @example
|
|
955
|
+
* ```typescript
|
|
956
|
+
* // Explicit flush with EOF
|
|
957
|
+
* for (const packet of encoder.packetsSync(null)) {
|
|
958
|
+
* if (packet === null) {
|
|
959
|
+
* console.log('All buffered packets flushed');
|
|
960
|
+
* break;
|
|
961
|
+
* }
|
|
962
|
+
* console.log('Buffered packet:', packet.pts);
|
|
1129
963
|
* output.writePacketSync(packet);
|
|
1130
964
|
* packet.free();
|
|
1131
965
|
* }
|
|
@@ -1136,102 +970,56 @@ export class Encoder {
|
|
|
1136
970
|
* @see {@link packets} For async version
|
|
1137
971
|
*/
|
|
1138
972
|
*packetsSync(frames) {
|
|
1139
|
-
|
|
973
|
+
const self = this;
|
|
974
|
+
// Helper: Encode frame and yield all available packets (filters out EAGAIN nulls and EOF)
|
|
975
|
+
const processFrame = function* (frame) {
|
|
976
|
+
self.encodeSync(frame);
|
|
977
|
+
// Receive ALL packets (filter out null/EAGAIN and EOF)
|
|
978
|
+
while (true) {
|
|
979
|
+
const packet = self.receiveSync();
|
|
980
|
+
if (!packet)
|
|
981
|
+
break; // EAGAIN or EOF - no more packets available
|
|
982
|
+
yield packet; // Only yield actual packets
|
|
983
|
+
}
|
|
984
|
+
}.bind(this);
|
|
985
|
+
// Helper: Flush encoder and signal EOF
|
|
986
|
+
const finalize = function* () {
|
|
987
|
+
for (const remaining of self.flushPacketsSync()) {
|
|
988
|
+
yield remaining; // Only yield actual packets
|
|
989
|
+
}
|
|
990
|
+
yield null; // Signal end-of-stream
|
|
991
|
+
}.bind(this);
|
|
992
|
+
// Case 1: EOF input -> flush only
|
|
993
|
+
if (frames === null) {
|
|
994
|
+
yield* finalize();
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
// Case 2: Single frame - NO AUTOMATIC FLUSH
|
|
998
|
+
if (frames instanceof Frame) {
|
|
999
|
+
yield* processFrame(frames);
|
|
1000
|
+
return; // No finalize() call!
|
|
1001
|
+
}
|
|
1002
|
+
// Case 3: Iterable of frames
|
|
1140
1003
|
for (const frame_2 of frames) {
|
|
1141
|
-
const
|
|
1004
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
1142
1005
|
try {
|
|
1143
|
-
const frame = __addDisposableResource(
|
|
1144
|
-
//
|
|
1006
|
+
const frame = __addDisposableResource(env_2, frame_2, false);
|
|
1007
|
+
// Check for EOF signal from upstream
|
|
1145
1008
|
if (frame === null) {
|
|
1146
|
-
|
|
1147
|
-
this.flushSync();
|
|
1148
|
-
while (true) {
|
|
1149
|
-
const remaining = this.receiveSync();
|
|
1150
|
-
if (!remaining)
|
|
1151
|
-
break;
|
|
1152
|
-
yield remaining;
|
|
1153
|
-
}
|
|
1154
|
-
// Signal EOF and stop processing
|
|
1155
|
-
yield null;
|
|
1009
|
+
yield* finalize();
|
|
1156
1010
|
return;
|
|
1157
1011
|
}
|
|
1158
|
-
|
|
1159
|
-
break;
|
|
1160
|
-
}
|
|
1161
|
-
// Open encoder if not already done
|
|
1162
|
-
if (!this.initialized) {
|
|
1163
|
-
this.initializeSync(frame);
|
|
1164
|
-
}
|
|
1165
|
-
// Prepare frame for encoding (set quality, validate channel count)
|
|
1166
|
-
if (frame) {
|
|
1167
|
-
this.prepareFrameForEncoding(frame);
|
|
1168
|
-
}
|
|
1169
|
-
// If audio encoder with fixed frame size, use AudioFrameBuffer
|
|
1170
|
-
if (this.audioFrameBuffer) {
|
|
1171
|
-
// Push frame into buffer
|
|
1172
|
-
this.audioFrameBuffer.pushSync(frame);
|
|
1173
|
-
// Pull and encode all available fixed-size frames
|
|
1174
|
-
let _bufferedFrame;
|
|
1175
|
-
while (!this.isClosed && (_bufferedFrame = this.audioFrameBuffer.pullSync()) !== null) {
|
|
1176
|
-
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
1177
|
-
try {
|
|
1178
|
-
const bufferedFrame = __addDisposableResource(env_6, _bufferedFrame, false);
|
|
1179
|
-
// Send buffered frame to encoder
|
|
1180
|
-
const sendRet = this.codecContext.sendFrameSync(bufferedFrame);
|
|
1181
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1182
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
1183
|
-
}
|
|
1184
|
-
// Receive packets
|
|
1185
|
-
while (true) {
|
|
1186
|
-
const packet = this.receiveSync();
|
|
1187
|
-
if (!packet)
|
|
1188
|
-
break;
|
|
1189
|
-
yield packet;
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
catch (e_5) {
|
|
1193
|
-
env_6.error = e_5;
|
|
1194
|
-
env_6.hasError = true;
|
|
1195
|
-
}
|
|
1196
|
-
finally {
|
|
1197
|
-
__disposeResources(env_6);
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
else {
|
|
1202
|
-
// Send frame to encoder
|
|
1203
|
-
const sendRet = this.codecContext.sendFrameSync(frame);
|
|
1204
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1205
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
1206
|
-
}
|
|
1207
|
-
// Receive ALL packets
|
|
1208
|
-
// A single frame can produce multiple packets (e.g., B-frames, lookahead)
|
|
1209
|
-
while (true) {
|
|
1210
|
-
const packet = this.receiveSync();
|
|
1211
|
-
if (!packet)
|
|
1212
|
-
break;
|
|
1213
|
-
yield packet;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1012
|
+
yield* processFrame(frame);
|
|
1216
1013
|
}
|
|
1217
|
-
catch (
|
|
1218
|
-
|
|
1219
|
-
|
|
1014
|
+
catch (e_2) {
|
|
1015
|
+
env_2.error = e_2;
|
|
1016
|
+
env_2.hasError = true;
|
|
1220
1017
|
}
|
|
1221
1018
|
finally {
|
|
1222
|
-
__disposeResources(
|
|
1019
|
+
__disposeResources(env_2);
|
|
1223
1020
|
}
|
|
1224
1021
|
}
|
|
1225
|
-
//
|
|
1226
|
-
this.flushSync();
|
|
1227
|
-
while (true) {
|
|
1228
|
-
const remaining = this.receiveSync();
|
|
1229
|
-
if (!remaining)
|
|
1230
|
-
break;
|
|
1231
|
-
yield remaining;
|
|
1232
|
-
}
|
|
1233
|
-
// Signal EOF
|
|
1234
|
-
yield null;
|
|
1022
|
+
// No fallback flush - only flush on explicit EOF
|
|
1235
1023
|
}
|
|
1236
1024
|
/**
|
|
1237
1025
|
* Flush encoder and signal end-of-stream.
|
|
@@ -1270,17 +1058,17 @@ export class Encoder {
|
|
|
1270
1058
|
// For the final frame, we pad or truncate as needed
|
|
1271
1059
|
let _bufferedFrame;
|
|
1272
1060
|
while (!this.isClosed && (_bufferedFrame = await this.audioFrameBuffer.pull()) !== null) {
|
|
1273
|
-
const
|
|
1061
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
1274
1062
|
try {
|
|
1275
|
-
const bufferedFrame = __addDisposableResource(
|
|
1063
|
+
const bufferedFrame = __addDisposableResource(env_3, _bufferedFrame, false);
|
|
1276
1064
|
await this.codecContext.sendFrame(bufferedFrame);
|
|
1277
1065
|
}
|
|
1278
|
-
catch (
|
|
1279
|
-
|
|
1280
|
-
|
|
1066
|
+
catch (e_3) {
|
|
1067
|
+
env_3.error = e_3;
|
|
1068
|
+
env_3.hasError = true;
|
|
1281
1069
|
}
|
|
1282
1070
|
finally {
|
|
1283
|
-
__disposeResources(
|
|
1071
|
+
__disposeResources(env_3);
|
|
1284
1072
|
}
|
|
1285
1073
|
}
|
|
1286
1074
|
}
|
|
@@ -1330,17 +1118,17 @@ export class Encoder {
|
|
|
1330
1118
|
// For the final frame, we pad or truncate as needed
|
|
1331
1119
|
let _bufferedFrame;
|
|
1332
1120
|
while (!this.isClosed && (_bufferedFrame = this.audioFrameBuffer.pullSync()) !== null) {
|
|
1333
|
-
const
|
|
1121
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1334
1122
|
try {
|
|
1335
|
-
const bufferedFrame = __addDisposableResource(
|
|
1123
|
+
const bufferedFrame = __addDisposableResource(env_4, _bufferedFrame, false);
|
|
1336
1124
|
this.codecContext.sendFrameSync(bufferedFrame);
|
|
1337
1125
|
}
|
|
1338
|
-
catch (
|
|
1339
|
-
|
|
1340
|
-
|
|
1126
|
+
catch (e_4) {
|
|
1127
|
+
env_4.error = e_4;
|
|
1128
|
+
env_4.hasError = true;
|
|
1341
1129
|
}
|
|
1342
1130
|
finally {
|
|
1343
|
-
__disposeResources(
|
|
1131
|
+
__disposeResources(env_4);
|
|
1344
1132
|
}
|
|
1345
1133
|
}
|
|
1346
1134
|
}
|
|
@@ -1378,11 +1166,12 @@ export class Encoder {
|
|
|
1378
1166
|
async *flushPackets() {
|
|
1379
1167
|
// Send flush signal
|
|
1380
1168
|
await this.flush();
|
|
1169
|
+
// Yield all remaining packets (filter out null/EAGAIN and EOF)
|
|
1381
1170
|
while (true) {
|
|
1382
1171
|
const packet = await this.receive();
|
|
1383
1172
|
if (!packet)
|
|
1384
|
-
break;
|
|
1385
|
-
yield packet;
|
|
1173
|
+
break; // Stop on EAGAIN or EOF
|
|
1174
|
+
yield packet; // Only yield actual packets
|
|
1386
1175
|
}
|
|
1387
1176
|
}
|
|
1388
1177
|
/**
|
|
@@ -1412,11 +1201,12 @@ export class Encoder {
|
|
|
1412
1201
|
*flushPacketsSync() {
|
|
1413
1202
|
// Send flush signal
|
|
1414
1203
|
this.flushSync();
|
|
1204
|
+
// Yield all remaining packets (filter out null/EAGAIN and EOF)
|
|
1415
1205
|
while (true) {
|
|
1416
1206
|
const packet = this.receiveSync();
|
|
1417
1207
|
if (!packet)
|
|
1418
|
-
break;
|
|
1419
|
-
yield packet;
|
|
1208
|
+
break; // Stop on EAGAIN or EOF
|
|
1209
|
+
yield packet; // Only yield actual packets
|
|
1420
1210
|
}
|
|
1421
1211
|
}
|
|
1422
1212
|
/**
|
|
@@ -1424,19 +1214,25 @@ export class Encoder {
|
|
|
1424
1214
|
*
|
|
1425
1215
|
* Gets encoded packets from the codec's internal buffer.
|
|
1426
1216
|
* Handles packet cloning and error checking.
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1217
|
+
* Implements FFmpeg's send/receive pattern.
|
|
1218
|
+
*
|
|
1219
|
+
* **Return Values:**
|
|
1220
|
+
* - `Packet` - Successfully encoded packet (AVERROR >= 0)
|
|
1221
|
+
* - `null` - Need more input frames (AVERROR_EAGAIN), or encoder not initialized
|
|
1222
|
+
* - `undefined` - End of stream reached (AVERROR_EOF), or encoder is closed
|
|
1429
1223
|
*
|
|
1430
1224
|
* Direct mapping to avcodec_receive_packet().
|
|
1431
1225
|
*
|
|
1432
|
-
* @returns Cloned packet or
|
|
1226
|
+
* @returns Cloned packet, null if need more data, or undefined if stream ended
|
|
1433
1227
|
*
|
|
1434
1228
|
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
1435
1229
|
*
|
|
1436
1230
|
* @example
|
|
1437
1231
|
* ```typescript
|
|
1438
|
-
*
|
|
1439
|
-
*
|
|
1232
|
+
* // Process all buffered packets
|
|
1233
|
+
* while (true) {
|
|
1234
|
+
* const packet = await encoder.receive();
|
|
1235
|
+
* if (!packet) break; // Stop on EAGAIN or EOF
|
|
1440
1236
|
* console.log(`Got packet with PTS: ${packet.pts}`);
|
|
1441
1237
|
* await output.writePacket(packet);
|
|
1442
1238
|
* packet.free();
|
|
@@ -1445,10 +1241,14 @@ export class Encoder {
|
|
|
1445
1241
|
*
|
|
1446
1242
|
* @example
|
|
1447
1243
|
* ```typescript
|
|
1448
|
-
* //
|
|
1449
|
-
*
|
|
1450
|
-
*
|
|
1451
|
-
* console.log(
|
|
1244
|
+
* // Handle each return value explicitly
|
|
1245
|
+
* const packet = await encoder.receive();
|
|
1246
|
+
* if (packet === EOF) {
|
|
1247
|
+
* console.log('Encoder stream ended');
|
|
1248
|
+
* } else if (packet === null) {
|
|
1249
|
+
* console.log('Need more input frames');
|
|
1250
|
+
* } else {
|
|
1251
|
+
* console.log(`Got packet: pts=${packet.pts}`);
|
|
1452
1252
|
* await output.writePacket(packet);
|
|
1453
1253
|
* packet.free();
|
|
1454
1254
|
* }
|
|
@@ -1457,13 +1257,33 @@ export class Encoder {
|
|
|
1457
1257
|
* @see {@link encode} For sending frames and receiving packets
|
|
1458
1258
|
* @see {@link flush} For signaling end-of-stream
|
|
1459
1259
|
* @see {@link receiveSync} For synchronous version
|
|
1260
|
+
* @see {@link EOF} For end-of-stream signal
|
|
1460
1261
|
*/
|
|
1461
1262
|
async receive() {
|
|
1462
|
-
if (this.isClosed
|
|
1263
|
+
if (this.isClosed) {
|
|
1264
|
+
return EOF;
|
|
1265
|
+
}
|
|
1266
|
+
if (!this.initialized) {
|
|
1463
1267
|
return null;
|
|
1464
1268
|
}
|
|
1465
1269
|
// Clear previous packet data
|
|
1466
1270
|
this.packet.unref();
|
|
1271
|
+
if (this.audioFrameBuffer?.hasFrame()) {
|
|
1272
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
1273
|
+
try {
|
|
1274
|
+
const bufferedFrame = __addDisposableResource(env_5, await this.audioFrameBuffer.pull(), false);
|
|
1275
|
+
if (bufferedFrame) {
|
|
1276
|
+
await this.codecContext.sendFrame(bufferedFrame);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
catch (e_5) {
|
|
1280
|
+
env_5.error = e_5;
|
|
1281
|
+
env_5.hasError = true;
|
|
1282
|
+
}
|
|
1283
|
+
finally {
|
|
1284
|
+
__disposeResources(env_5);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1467
1287
|
const ret = await this.codecContext.receivePacket(this.packet);
|
|
1468
1288
|
if (ret === 0) {
|
|
1469
1289
|
// Set packet timebase to codec timebase
|
|
@@ -1473,10 +1293,14 @@ export class Encoder {
|
|
|
1473
1293
|
// Got a packet, clone it for the user
|
|
1474
1294
|
return this.packet.clone();
|
|
1475
1295
|
}
|
|
1476
|
-
else if (ret === AVERROR_EAGAIN
|
|
1477
|
-
// Need more data
|
|
1296
|
+
else if (ret === AVERROR_EAGAIN) {
|
|
1297
|
+
// Need more data
|
|
1478
1298
|
return null;
|
|
1479
1299
|
}
|
|
1300
|
+
else if (ret === AVERROR_EOF) {
|
|
1301
|
+
// End of stream
|
|
1302
|
+
return EOF;
|
|
1303
|
+
}
|
|
1480
1304
|
else {
|
|
1481
1305
|
// Error
|
|
1482
1306
|
FFmpegError.throwIfError(ret, 'Failed to receive packet');
|
|
@@ -1489,19 +1313,25 @@ export class Encoder {
|
|
|
1489
1313
|
*
|
|
1490
1314
|
* Gets encoded packets from the codec's internal buffer.
|
|
1491
1315
|
* Handles packet cloning and error checking.
|
|
1492
|
-
*
|
|
1493
|
-
*
|
|
1316
|
+
* Implements FFmpeg's send/receive pattern.
|
|
1317
|
+
*
|
|
1318
|
+
* **Return Values:**
|
|
1319
|
+
* - `Packet` - Successfully encoded packet (AVERROR >= 0)
|
|
1320
|
+
* - `null` - Need more input frames (AVERROR_EAGAIN), or encoder not initialized
|
|
1321
|
+
* - `undefined` - End of stream reached (AVERROR_EOF), or encoder is closed
|
|
1494
1322
|
*
|
|
1495
1323
|
* Direct mapping to avcodec_receive_packet().
|
|
1496
1324
|
*
|
|
1497
|
-
* @returns Cloned packet or
|
|
1325
|
+
* @returns Cloned packet, null if need more data, or undefined if stream ended
|
|
1498
1326
|
*
|
|
1499
1327
|
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
1500
1328
|
*
|
|
1501
1329
|
* @example
|
|
1502
1330
|
* ```typescript
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1331
|
+
* // Process all buffered packets
|
|
1332
|
+
* while (true) {
|
|
1333
|
+
* const packet = encoder.receiveSync();
|
|
1334
|
+
* if (!packet) break; // Stop on EAGAIN or EOF
|
|
1505
1335
|
* console.log(`Got packet with PTS: ${packet.pts}`);
|
|
1506
1336
|
* output.writePacketSync(packet);
|
|
1507
1337
|
* packet.free();
|
|
@@ -1510,10 +1340,14 @@ export class Encoder {
|
|
|
1510
1340
|
*
|
|
1511
1341
|
* @example
|
|
1512
1342
|
* ```typescript
|
|
1513
|
-
* //
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1516
|
-
* console.log(
|
|
1343
|
+
* // Handle each return value explicitly
|
|
1344
|
+
* const packet = encoder.receiveSync();
|
|
1345
|
+
* if (packet === EOF) {
|
|
1346
|
+
* console.log('Encoder stream ended');
|
|
1347
|
+
* } else if (packet === null) {
|
|
1348
|
+
* console.log('Need more input frames');
|
|
1349
|
+
* } else {
|
|
1350
|
+
* console.log(`Got packet: pts=${packet.pts}`);
|
|
1517
1351
|
* output.writePacketSync(packet);
|
|
1518
1352
|
* packet.free();
|
|
1519
1353
|
* }
|
|
@@ -1522,13 +1356,33 @@ export class Encoder {
|
|
|
1522
1356
|
* @see {@link encodeSync} For sending frames and receiving packets
|
|
1523
1357
|
* @see {@link flushSync} For signaling end-of-stream
|
|
1524
1358
|
* @see {@link receive} For async version
|
|
1359
|
+
* @see {@link EOF} For end-of-stream signal
|
|
1525
1360
|
*/
|
|
1526
1361
|
receiveSync() {
|
|
1527
|
-
if (this.isClosed
|
|
1362
|
+
if (this.isClosed) {
|
|
1363
|
+
return EOF;
|
|
1364
|
+
}
|
|
1365
|
+
if (!this.initialized) {
|
|
1528
1366
|
return null;
|
|
1529
1367
|
}
|
|
1530
1368
|
// Clear previous packet data
|
|
1531
1369
|
this.packet.unref();
|
|
1370
|
+
if (this.audioFrameBuffer?.hasFrame()) {
|
|
1371
|
+
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
1372
|
+
try {
|
|
1373
|
+
const bufferedFrame = __addDisposableResource(env_6, this.audioFrameBuffer.pullSync(), false);
|
|
1374
|
+
if (bufferedFrame) {
|
|
1375
|
+
this.codecContext.sendFrameSync(bufferedFrame);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
catch (e_6) {
|
|
1379
|
+
env_6.error = e_6;
|
|
1380
|
+
env_6.hasError = true;
|
|
1381
|
+
}
|
|
1382
|
+
finally {
|
|
1383
|
+
__disposeResources(env_6);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1532
1386
|
const ret = this.codecContext.receivePacketSync(this.packet);
|
|
1533
1387
|
if (ret === 0) {
|
|
1534
1388
|
// Set packet timebase to codec timebase
|
|
@@ -1538,10 +1392,14 @@ export class Encoder {
|
|
|
1538
1392
|
// Got a packet, clone it for the user
|
|
1539
1393
|
return this.packet.clone();
|
|
1540
1394
|
}
|
|
1541
|
-
else if (ret === AVERROR_EAGAIN
|
|
1542
|
-
// Need more data
|
|
1395
|
+
else if (ret === AVERROR_EAGAIN) {
|
|
1396
|
+
// Need more data
|
|
1543
1397
|
return null;
|
|
1544
1398
|
}
|
|
1399
|
+
else if (ret === AVERROR_EOF) {
|
|
1400
|
+
// End of stream
|
|
1401
|
+
return EOF;
|
|
1402
|
+
}
|
|
1545
1403
|
else {
|
|
1546
1404
|
// Error
|
|
1547
1405
|
FFmpegError.throwIfError(ret, 'Failed to receive packet');
|
|
@@ -1648,9 +1506,9 @@ export class Encoder {
|
|
|
1648
1506
|
try {
|
|
1649
1507
|
// Outer loop - receive frames
|
|
1650
1508
|
while (!this.inputQueue.isClosed) {
|
|
1651
|
-
const
|
|
1509
|
+
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
1652
1510
|
try {
|
|
1653
|
-
const frame = __addDisposableResource(
|
|
1511
|
+
const frame = __addDisposableResource(env_7, await this.inputQueue.receive(), false);
|
|
1654
1512
|
if (!frame)
|
|
1655
1513
|
break;
|
|
1656
1514
|
// Open encoder if not already done
|
|
@@ -1660,60 +1518,21 @@ export class Encoder {
|
|
|
1660
1518
|
await this.initializePromise;
|
|
1661
1519
|
// Prepare frame for encoding (set quality, validate channel count)
|
|
1662
1520
|
this.prepareFrameForEncoding(frame);
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
await this.
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
const env_10 = { stack: [], error: void 0, hasError: false };
|
|
1671
|
-
try {
|
|
1672
|
-
const bufferedFrame = __addDisposableResource(env_10, _bufferedFrame, false);
|
|
1673
|
-
// Send buffered frame to encoder
|
|
1674
|
-
const sendRet = await this.codecContext.sendFrame(bufferedFrame);
|
|
1675
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1676
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
1677
|
-
}
|
|
1678
|
-
// Receive packets
|
|
1679
|
-
while (true) {
|
|
1680
|
-
const packet = await this.receive();
|
|
1681
|
-
if (!packet)
|
|
1682
|
-
break;
|
|
1683
|
-
await this.outputQueue.send(packet);
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
catch (e_9) {
|
|
1687
|
-
env_10.error = e_9;
|
|
1688
|
-
env_10.hasError = true;
|
|
1689
|
-
}
|
|
1690
|
-
finally {
|
|
1691
|
-
__disposeResources(env_10);
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
else {
|
|
1696
|
-
// Send frame to encoder
|
|
1697
|
-
const sendRet = await this.codecContext.sendFrame(frame);
|
|
1698
|
-
if (sendRet < 0 && sendRet !== AVERROR_EOF && sendRet !== AVERROR_EAGAIN) {
|
|
1699
|
-
FFmpegError.throwIfError(sendRet, 'Failed to send frame');
|
|
1700
|
-
}
|
|
1701
|
-
// Receive ALL packets
|
|
1702
|
-
// A single frame can produce multiple packets (e.g., B-frames, lookahead)
|
|
1703
|
-
while (!this.outputQueue.isClosed) {
|
|
1704
|
-
const packet = await this.receive();
|
|
1705
|
-
if (!packet)
|
|
1706
|
-
break;
|
|
1707
|
-
await this.outputQueue.send(packet);
|
|
1708
|
-
}
|
|
1521
|
+
await this.encode(frame);
|
|
1522
|
+
// Receive packets
|
|
1523
|
+
while (!this.outputQueue.isClosed) {
|
|
1524
|
+
const packet = await this.receive();
|
|
1525
|
+
if (!packet)
|
|
1526
|
+
break; // Stop on EAGAIN or EOF
|
|
1527
|
+
await this.outputQueue.send(packet); // Only send actual packets
|
|
1709
1528
|
}
|
|
1710
1529
|
}
|
|
1711
|
-
catch (
|
|
1712
|
-
|
|
1713
|
-
|
|
1530
|
+
catch (e_7) {
|
|
1531
|
+
env_7.error = e_7;
|
|
1532
|
+
env_7.hasError = true;
|
|
1714
1533
|
}
|
|
1715
1534
|
finally {
|
|
1716
|
-
__disposeResources(
|
|
1535
|
+
__disposeResources(env_7);
|
|
1717
1536
|
}
|
|
1718
1537
|
}
|
|
1719
1538
|
// Flush encoder at end
|
|
@@ -1721,8 +1540,8 @@ export class Encoder {
|
|
|
1721
1540
|
while (!this.outputQueue.isClosed) {
|
|
1722
1541
|
const packet = await this.receive();
|
|
1723
1542
|
if (!packet)
|
|
1724
|
-
break;
|
|
1725
|
-
await this.outputQueue.send(packet);
|
|
1543
|
+
break; // Stop on EAGAIN or EOF
|
|
1544
|
+
await this.outputQueue.send(packet); // Only send actual packets
|
|
1726
1545
|
}
|
|
1727
1546
|
}
|
|
1728
1547
|
catch {
|
|
@@ -1734,14 +1553,45 @@ export class Encoder {
|
|
|
1734
1553
|
}
|
|
1735
1554
|
}
|
|
1736
1555
|
/**
|
|
1737
|
-
* Send frame to input queue.
|
|
1556
|
+
* Send frame to input queue or flush the pipeline.
|
|
1557
|
+
*
|
|
1558
|
+
* When frame is provided, queues it for encoding.
|
|
1559
|
+
* When null is provided, triggers flush sequence:
|
|
1560
|
+
* - Closes input queue
|
|
1561
|
+
* - Waits for worker completion
|
|
1562
|
+
* - Flushes encoder and sends remaining packets to output queue
|
|
1563
|
+
* - Closes output queue
|
|
1564
|
+
* - Waits for pipeTo task completion (writes to muxer)
|
|
1565
|
+
*
|
|
1566
|
+
* Used by scheduler system for pipeline control.
|
|
1738
1567
|
*
|
|
1739
|
-
* @param frame - Frame to send
|
|
1568
|
+
* @param frame - Frame to send, or null to flush
|
|
1740
1569
|
*
|
|
1741
1570
|
* @internal
|
|
1742
1571
|
*/
|
|
1743
1572
|
async sendToQueue(frame) {
|
|
1744
|
-
|
|
1573
|
+
if (frame) {
|
|
1574
|
+
await this.inputQueue.send(frame);
|
|
1575
|
+
}
|
|
1576
|
+
else {
|
|
1577
|
+
// Close input queue to signal end of stream to worker
|
|
1578
|
+
this.inputQueue.close();
|
|
1579
|
+
// Wait for worker to finish processing all frames (if exists)
|
|
1580
|
+
if (this.workerPromise) {
|
|
1581
|
+
await this.workerPromise;
|
|
1582
|
+
}
|
|
1583
|
+
// Flush encoder at end
|
|
1584
|
+
await this.flush();
|
|
1585
|
+
while (true) {
|
|
1586
|
+
const packet = await this.receive();
|
|
1587
|
+
if (!packet)
|
|
1588
|
+
break; // Stop on EAGAIN or EOF
|
|
1589
|
+
await this.outputQueue.send(packet); // Only send actual packets
|
|
1590
|
+
}
|
|
1591
|
+
if (this.pipeToPromise) {
|
|
1592
|
+
await this.pipeToPromise;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1745
1595
|
}
|
|
1746
1596
|
/**
|
|
1747
1597
|
* Receive packet from output queue.
|
|
@@ -1753,32 +1603,6 @@ export class Encoder {
|
|
|
1753
1603
|
async receiveFromQueue() {
|
|
1754
1604
|
return await this.outputQueue.receive();
|
|
1755
1605
|
}
|
|
1756
|
-
/**
|
|
1757
|
-
* Flush the entire filter pipeline.
|
|
1758
|
-
*
|
|
1759
|
-
* Propagates flush through worker, output queue, and next component.
|
|
1760
|
-
*
|
|
1761
|
-
* @internal
|
|
1762
|
-
*/
|
|
1763
|
-
async flushPipeline() {
|
|
1764
|
-
// Close input queue to signal end of stream to worker
|
|
1765
|
-
this.inputQueue.close();
|
|
1766
|
-
// Wait for worker to finish processing all frames (if exists)
|
|
1767
|
-
if (this.workerPromise) {
|
|
1768
|
-
await this.workerPromise;
|
|
1769
|
-
}
|
|
1770
|
-
// Flush encoder at end
|
|
1771
|
-
await this.flush();
|
|
1772
|
-
while (true) {
|
|
1773
|
-
const packet = await this.receive();
|
|
1774
|
-
if (!packet)
|
|
1775
|
-
break;
|
|
1776
|
-
await this.outputQueue.send(packet);
|
|
1777
|
-
}
|
|
1778
|
-
if (this.pipeToPromise) {
|
|
1779
|
-
await this.pipeToPromise;
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
1606
|
/**
|
|
1783
1607
|
* Initialize encoder from first frame.
|
|
1784
1608
|
*
|
|
@@ -2048,6 +1872,9 @@ export class Encoder {
|
|
|
2048
1872
|
* @internal
|
|
2049
1873
|
*/
|
|
2050
1874
|
prepareFrameForEncoding(frame) {
|
|
1875
|
+
// Clear pict_type - encoder will determine frame types based on its own settings
|
|
1876
|
+
// Input stream's frame type hints are irrelevant when re-encoding
|
|
1877
|
+
frame.pictType = AV_PICTURE_TYPE_NONE;
|
|
2051
1878
|
// Adjust frame PTS and timebase to encoder timebase
|
|
2052
1879
|
// This matches FFmpeg's adjust_frame_pts_to_encoder_tb() behavior which:
|
|
2053
1880
|
// 1. Converts PTS from frame's timebase to encoder's timebase (av_rescale_q)
|