node-av 1.0.3 → 1.2.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 +56 -41
- package/dist/api/bitstream-filter.d.ts +180 -123
- package/dist/api/bitstream-filter.js +182 -126
- package/dist/api/bitstream-filter.js.map +1 -1
- package/dist/api/decoder.d.ts +286 -130
- package/dist/api/decoder.js +321 -159
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/encoder.d.ts +254 -158
- package/dist/api/encoder.js +326 -298
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-presets.d.ts +912 -0
- package/dist/api/filter-presets.js +1407 -0
- package/dist/api/filter-presets.js.map +1 -0
- package/dist/api/filter.d.ts +280 -284
- package/dist/api/filter.js +435 -509
- package/dist/api/filter.js.map +1 -1
- package/dist/api/hardware.d.ts +226 -159
- package/dist/api/hardware.js +405 -287
- package/dist/api/hardware.js.map +1 -1
- package/dist/api/index.d.ts +3 -2
- package/dist/api/index.js +1 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/io-stream.d.ts +65 -61
- package/dist/api/io-stream.js +45 -47
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/media-input.d.ts +244 -141
- package/dist/api/media-input.js +207 -104
- package/dist/api/media-input.js.map +1 -1
- package/dist/api/media-output.d.ts +206 -128
- package/dist/api/media-output.js +212 -129
- package/dist/api/media-output.js.map +1 -1
- package/dist/api/pipeline.d.ts +168 -38
- package/dist/api/pipeline.js +238 -14
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/types.d.ts +22 -182
- package/dist/api/utilities/audio-sample.d.ts +1 -1
- package/dist/api/utilities/image.d.ts +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/timestamp.d.ts +1 -1
- package/dist/api/utils.d.ts +1 -2
- package/dist/api/utils.js +9 -0
- package/dist/api/utils.js.map +1 -1
- package/dist/{lib → constants}/channel-layouts.d.ts +1 -1
- package/dist/constants/channel-layouts.js.map +1 -0
- package/dist/{lib → constants}/constants.d.ts +19 -4
- package/dist/{lib → constants}/constants.js +15 -1
- package/dist/constants/constants.js.map +1 -0
- package/dist/constants/decoders.d.ts +609 -0
- package/dist/constants/decoders.js +617 -0
- package/dist/constants/decoders.js.map +1 -0
- package/dist/constants/encoders.d.ts +285 -0
- package/dist/constants/encoders.js +298 -0
- package/dist/constants/encoders.js.map +1 -0
- package/dist/constants/index.d.ts +4 -0
- package/dist/constants/index.js +5 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/audio-fifo.d.ts +128 -171
- package/dist/lib/audio-fifo.js +130 -173
- package/dist/lib/audio-fifo.js.map +1 -1
- package/dist/lib/binding.d.ts +7 -5
- package/dist/lib/binding.js +5 -0
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/bitstream-filter-context.d.ts +139 -184
- package/dist/lib/bitstream-filter-context.js +139 -188
- package/dist/lib/bitstream-filter-context.js.map +1 -1
- package/dist/lib/bitstream-filter.d.ts +69 -55
- package/dist/lib/bitstream-filter.js +68 -54
- package/dist/lib/bitstream-filter.js.map +1 -1
- package/dist/lib/codec-context.d.ts +317 -381
- package/dist/lib/codec-context.js +316 -381
- package/dist/lib/codec-context.js.map +1 -1
- package/dist/lib/codec-parameters.d.ts +161 -171
- package/dist/lib/codec-parameters.js +162 -172
- package/dist/lib/codec-parameters.js.map +1 -1
- package/dist/lib/codec-parser.d.ts +92 -105
- package/dist/lib/codec-parser.js +92 -103
- package/dist/lib/codec-parser.js.map +1 -1
- package/dist/lib/codec.d.ts +328 -217
- package/dist/lib/codec.js +392 -218
- package/dist/lib/codec.js.map +1 -1
- package/dist/lib/dictionary.d.ts +150 -204
- package/dist/lib/dictionary.js +159 -213
- package/dist/lib/dictionary.js.map +1 -1
- package/dist/lib/error.d.ts +97 -131
- package/dist/lib/error.js +98 -128
- package/dist/lib/error.js.map +1 -1
- package/dist/lib/filter-context.d.ts +317 -194
- package/dist/lib/filter-context.js +335 -200
- package/dist/lib/filter-context.js.map +1 -1
- package/dist/lib/filter-graph.d.ts +252 -293
- package/dist/lib/filter-graph.js +253 -294
- package/dist/lib/filter-graph.js.map +1 -1
- package/dist/lib/filter-inout.d.ts +87 -95
- package/dist/lib/filter-inout.js +87 -95
- package/dist/lib/filter-inout.js.map +1 -1
- package/dist/lib/filter.d.ts +93 -111
- package/dist/lib/filter.js +94 -112
- package/dist/lib/filter.js.map +1 -1
- package/dist/lib/format-context.d.ts +321 -429
- package/dist/lib/format-context.js +314 -386
- package/dist/lib/format-context.js.map +1 -1
- package/dist/lib/frame.d.ts +263 -406
- package/dist/lib/frame.js +263 -408
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/hardware-device-context.d.ts +150 -204
- package/dist/lib/hardware-device-context.js +149 -203
- package/dist/lib/hardware-device-context.js.map +1 -1
- package/dist/lib/hardware-frames-context.d.ts +171 -181
- package/dist/lib/hardware-frames-context.js +171 -181
- package/dist/lib/hardware-frames-context.js.map +1 -1
- package/dist/lib/index.d.ts +2 -3
- package/dist/lib/index.js +2 -5
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/input-format.d.ts +90 -118
- package/dist/lib/input-format.js +89 -117
- package/dist/lib/input-format.js.map +1 -1
- package/dist/lib/io-context.d.ts +210 -242
- package/dist/lib/io-context.js +221 -253
- package/dist/lib/io-context.js.map +1 -1
- package/dist/lib/log.d.ts +86 -120
- package/dist/lib/log.js +85 -122
- package/dist/lib/log.js.map +1 -1
- package/dist/lib/native-types.d.ts +127 -112
- package/dist/lib/native-types.js +9 -0
- package/dist/lib/native-types.js.map +1 -1
- package/dist/lib/option.d.ts +285 -242
- package/dist/lib/option.js +310 -250
- package/dist/lib/option.js.map +1 -1
- package/dist/lib/output-format.d.ts +78 -102
- package/dist/lib/output-format.js +77 -101
- package/dist/lib/output-format.js.map +1 -1
- package/dist/lib/packet.d.ts +173 -241
- package/dist/lib/packet.js +172 -241
- package/dist/lib/packet.js.map +1 -1
- package/dist/lib/rational.d.ts +0 -2
- package/dist/lib/rational.js +0 -2
- package/dist/lib/rational.js.map +1 -1
- package/dist/lib/software-resample-context.d.ts +242 -326
- package/dist/lib/software-resample-context.js +242 -326
- package/dist/lib/software-resample-context.js.map +1 -1
- package/dist/lib/software-scale-context.d.ts +130 -174
- package/dist/lib/software-scale-context.js +132 -176
- package/dist/lib/software-scale-context.js.map +1 -1
- package/dist/lib/stream.d.ts +88 -198
- package/dist/lib/stream.js +87 -197
- package/dist/lib/stream.js.map +1 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/utilities.d.ts +372 -181
- package/dist/lib/utilities.js +373 -182
- package/dist/lib/utilities.js.map +1 -1
- package/install/check.js +0 -1
- package/package.json +32 -24
- package/release_notes.md +43 -13
- package/CHANGELOG.md +0 -8
- package/dist/lib/channel-layouts.js.map +0 -1
- package/dist/lib/constants.js.map +0 -1
- /package/dist/{lib → constants}/channel-layouts.js +0 -0
package/dist/api/decoder.js
CHANGED
|
@@ -1,59 +1,46 @@
|
|
|
1
|
+
import { AVERROR_EOF, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
|
|
2
|
+
import { AVERROR_EAGAIN, Codec, CodecContext, FFmpegError, Frame } from '../lib/index.js';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* High-level decoder for audio and video streams.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Supports hardware acceleration and zero-copy transcoding.
|
|
9
|
-
*
|
|
10
|
-
* @module api/decoder
|
|
11
|
-
*/
|
|
12
|
-
import { AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, AVERROR_EAGAIN, AVERROR_EOF, Codec, CodecContext, FFmpegError, Frame } from '../lib/index.js';
|
|
13
|
-
/**
|
|
14
|
-
* High-level decoder for media streams.
|
|
15
|
-
*
|
|
16
|
-
* Handles codec initialization, packet decoding, and frame output.
|
|
17
|
-
* Designed for simple, efficient decoding workflows.
|
|
18
|
-
*
|
|
19
|
-
* Manages codec context lifecycle and provides automatic cleanup.
|
|
20
|
-
* Supports hardware acceleration with zero-copy frame sharing.
|
|
6
|
+
* Provides a simplified interface for decoding media streams from packets to frames.
|
|
7
|
+
* Handles codec initialization, hardware acceleration setup, and frame management.
|
|
8
|
+
* Supports both synchronous packet-by-packet decoding and async iteration over frames.
|
|
9
|
+
* Essential component in media processing pipelines for converting compressed data to raw frames.
|
|
21
10
|
*
|
|
22
11
|
* @example
|
|
23
12
|
* ```typescript
|
|
24
|
-
*
|
|
25
|
-
* const media = await MediaInput.open('video.mp4');
|
|
26
|
-
* const stream = media.video(); // Get video stream
|
|
27
|
-
* const decoder = await Decoder.create(stream);
|
|
13
|
+
* import { MediaInput, Decoder } from 'node-av/api';
|
|
28
14
|
*
|
|
29
|
-
* //
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* const frame = await decoder.decode(packet);
|
|
33
|
-
* if (frame) {
|
|
34
|
-
* console.log(`Decoded frame: ${frame.width}x${frame.height}`);
|
|
35
|
-
* // Process frame...
|
|
36
|
-
* }
|
|
37
|
-
* }
|
|
38
|
-
* }
|
|
15
|
+
* // Open media and create decoder
|
|
16
|
+
* await using input = await MediaInput.open('video.mp4');
|
|
17
|
+
* using decoder = await Decoder.create(input.video());
|
|
39
18
|
*
|
|
40
|
-
* //
|
|
41
|
-
* const
|
|
42
|
-
*
|
|
19
|
+
* // Decode frames
|
|
20
|
+
* for await (const frame of decoder.frames(input.packets())) {
|
|
21
|
+
* console.log(`Decoded frame: ${frame.width}x${frame.height}`);
|
|
22
|
+
* frame.free();
|
|
23
|
+
* }
|
|
43
24
|
* ```
|
|
44
25
|
*
|
|
45
26
|
* @example
|
|
46
27
|
* ```typescript
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* });
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
28
|
+
* import { HardwareContext } from 'node-av/api';
|
|
29
|
+
* import { AV_HWDEVICE_TYPE_CUDA } from 'node-av/constants';
|
|
30
|
+
*
|
|
31
|
+
* // Setup hardware acceleration
|
|
32
|
+
* const hw = HardwareContext.create(AV_HWDEVICE_TYPE_CUDA);
|
|
33
|
+
* using decoder = await Decoder.create(stream, { hardware: hw });
|
|
34
|
+
*
|
|
35
|
+
* // Frames will be decoded on GPU
|
|
36
|
+
* for await (const frame of decoder.frames(packets)) {
|
|
37
|
+
* // frame.hwFramesCtx contains GPU memory reference
|
|
38
|
+
* }
|
|
56
39
|
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see {@link Encoder} For encoding frames to packets
|
|
42
|
+
* @see {@link MediaInput} For reading media files
|
|
43
|
+
* @see {@link HardwareContext} For GPU acceleration
|
|
57
44
|
*/
|
|
58
45
|
export class Decoder {
|
|
59
46
|
codecContext;
|
|
@@ -61,15 +48,14 @@ export class Decoder {
|
|
|
61
48
|
streamIndex;
|
|
62
49
|
stream;
|
|
63
50
|
isOpen = true;
|
|
64
|
-
hardware;
|
|
51
|
+
hardware;
|
|
65
52
|
/**
|
|
66
|
-
*
|
|
53
|
+
* @param codecContext - Configured codec context
|
|
54
|
+
* @param stream - Media stream being decoded
|
|
55
|
+
* @param hardware - Optional hardware context
|
|
56
|
+
* Use {@link create} factory method
|
|
67
57
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* @param codecContext - Initialized codec context
|
|
71
|
-
* @param stream - The stream this decoder is for
|
|
72
|
-
* @param hardware - Optional hardware context for auto-sharing frames context
|
|
58
|
+
* @internal
|
|
73
59
|
*/
|
|
74
60
|
constructor(codecContext, stream, hardware) {
|
|
75
61
|
this.codecContext = codecContext;
|
|
@@ -80,27 +66,49 @@ export class Decoder {
|
|
|
80
66
|
this.frame.alloc();
|
|
81
67
|
}
|
|
82
68
|
/**
|
|
83
|
-
* Create a decoder for a
|
|
69
|
+
* Create a decoder for a media stream.
|
|
84
70
|
*
|
|
85
|
-
*
|
|
86
|
-
* and
|
|
71
|
+
* Initializes a decoder with the appropriate codec and configuration.
|
|
72
|
+
* Automatically detects and configures hardware acceleration if provided.
|
|
73
|
+
* Applies custom codec options and threading configuration.
|
|
87
74
|
*
|
|
88
|
-
*
|
|
89
|
-
* then initializes and opens the codec context.
|
|
90
|
-
*
|
|
91
|
-
* @param stream - Stream to decode
|
|
75
|
+
* @param stream - Media stream to decode
|
|
92
76
|
* @param options - Decoder configuration options
|
|
77
|
+
* @returns Configured decoder instance
|
|
93
78
|
*
|
|
94
|
-
* @
|
|
79
|
+
* @throws {Error} If decoder not found for codec
|
|
80
|
+
* @throws {FFmpegError} If codec initialization fails
|
|
95
81
|
*
|
|
96
|
-
* @
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* import { MediaInput, Decoder } from 'node-av/api';
|
|
85
|
+
*
|
|
86
|
+
* await using input = await MediaInput.open('video.mp4');
|
|
87
|
+
* using decoder = await Decoder.create(input.video());
|
|
88
|
+
* ```
|
|
97
89
|
*
|
|
98
90
|
* @example
|
|
99
91
|
* ```typescript
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
92
|
+
* using decoder = await Decoder.create(stream, {
|
|
93
|
+
* threads: 4,
|
|
94
|
+
* options: {
|
|
95
|
+
* 'refcounted_frames': '1',
|
|
96
|
+
* 'skip_frame': 'nonkey' // Only decode keyframes
|
|
97
|
+
* }
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const hw = HardwareContext.auto();
|
|
104
|
+
* using decoder = await Decoder.create(stream, {
|
|
105
|
+
* hardware: hw,
|
|
106
|
+
* threads: 0 // Auto-detect thread count
|
|
107
|
+
* });
|
|
103
108
|
* ```
|
|
109
|
+
*
|
|
110
|
+
* @see {@link HardwareContext} For GPU acceleration setup
|
|
111
|
+
* @see {@link DecoderOptions} For configuration options
|
|
104
112
|
*/
|
|
105
113
|
static async create(stream, options = {}) {
|
|
106
114
|
if (!stream) {
|
|
@@ -127,24 +135,11 @@ export class Decoder {
|
|
|
127
135
|
codecContext.threadCount = options.threads;
|
|
128
136
|
}
|
|
129
137
|
// Check if this decoder supports hardware acceleration
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!config)
|
|
136
|
-
break;
|
|
137
|
-
// Check if decoder supports HW_DEVICE_CTX method with matching device type
|
|
138
|
-
if ((config.methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) !== 0 && config.deviceType === options.hardware.deviceType) {
|
|
139
|
-
supportsHardware = true;
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Only apply hardware acceleration if the decoder supports it
|
|
144
|
-
if (supportsHardware) {
|
|
145
|
-
codecContext.hwDeviceCtx = options.hardware.deviceContext;
|
|
146
|
-
}
|
|
147
|
-
// Silently ignore hardware for software decoders
|
|
138
|
+
// Only apply hardware acceleration if the decoder supports it
|
|
139
|
+
// Silently ignore hardware for software decoders
|
|
140
|
+
const isHWDecoder = codec.isHardwareAcceleratedDecoder();
|
|
141
|
+
if (isHWDecoder && options.hardware) {
|
|
142
|
+
codecContext.hwDeviceCtx = options.hardware.deviceContext;
|
|
148
143
|
}
|
|
149
144
|
// Apply codec-specific options via AVOptions
|
|
150
145
|
if (options.options) {
|
|
@@ -158,37 +153,133 @@ export class Decoder {
|
|
|
158
153
|
codecContext.freeContext();
|
|
159
154
|
FFmpegError.throwIfError(openRet, 'Failed to open codec');
|
|
160
155
|
}
|
|
161
|
-
const decoder = new Decoder(codecContext, stream, options.hardware);
|
|
156
|
+
const decoder = new Decoder(codecContext, stream, isHWDecoder ? options.hardware : undefined);
|
|
162
157
|
return decoder;
|
|
163
158
|
}
|
|
164
159
|
/**
|
|
165
160
|
* Check if decoder is open.
|
|
161
|
+
*
|
|
162
|
+
* @returns true if decoder is open and ready
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* if (decoder.isDecoderOpen) {
|
|
167
|
+
* const frame = await decoder.decode(packet);
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
166
170
|
*/
|
|
167
171
|
get isDecoderOpen() {
|
|
168
172
|
return this.isOpen;
|
|
169
173
|
}
|
|
170
174
|
/**
|
|
171
|
-
*
|
|
175
|
+
* Get output stream information.
|
|
172
176
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
177
|
+
* Returns format information about decoded frames.
|
|
178
|
+
* For hardware decoding, returns the hardware pixel format.
|
|
179
|
+
* Essential for configuring downstream components like encoders or filters.
|
|
175
180
|
*
|
|
176
|
-
*
|
|
177
|
-
* The decoder may buffer packets before producing frames.
|
|
181
|
+
* @returns Stream format information
|
|
178
182
|
*
|
|
179
|
-
* @
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const info = decoder.getOutputStreamInfo();
|
|
186
|
+
* if (info.type === 'video') {
|
|
187
|
+
* console.log(`Video: ${info.width}x${info.height} @ ${info.pixelFormat}`);
|
|
188
|
+
* console.log(`Frame rate: ${info.frameRate.num}/${info.frameRate.den}`);
|
|
189
|
+
* }
|
|
190
|
+
* ```
|
|
180
191
|
*
|
|
181
|
-
* @
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* const info = decoder.getOutputStreamInfo();
|
|
195
|
+
* if (info.type === 'audio') {
|
|
196
|
+
* console.log(`Audio: ${info.sampleRate}Hz ${info.sampleFormat}`);
|
|
197
|
+
* console.log(`Channels: ${info.channelLayout}`);
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
182
200
|
*
|
|
183
|
-
* @
|
|
201
|
+
* @see {@link StreamInfo} For format details
|
|
202
|
+
* @see {@link Encoder.create} For matching encoder configuration
|
|
203
|
+
*/
|
|
204
|
+
getOutputStreamInfo() {
|
|
205
|
+
if (this.stream.codecpar.codecType === AVMEDIA_TYPE_VIDEO) {
|
|
206
|
+
return {
|
|
207
|
+
type: 'video',
|
|
208
|
+
width: this.codecContext.width,
|
|
209
|
+
height: this.codecContext.height,
|
|
210
|
+
pixelFormat: this.hardware?.devicePixelFormat ?? this.codecContext.pixelFormat,
|
|
211
|
+
timeBase: this.stream.timeBase,
|
|
212
|
+
frameRate: this.stream.rFrameRate,
|
|
213
|
+
sampleAspectRatio: this.codecContext.sampleAspectRatio,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
return {
|
|
218
|
+
type: 'audio',
|
|
219
|
+
sampleRate: this.codecContext.sampleRate,
|
|
220
|
+
sampleFormat: this.codecContext.sampleFormat,
|
|
221
|
+
channelLayout: this.codecContext.channelLayout,
|
|
222
|
+
timeBase: this.stream.timeBase,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Check if decoder uses hardware acceleration.
|
|
228
|
+
*
|
|
229
|
+
* @returns true if hardware-accelerated
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* if (decoder.isHardware()) {
|
|
234
|
+
* console.log('Using GPU acceleration');
|
|
235
|
+
* }
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* @see {@link HardwareContext} For hardware setup
|
|
239
|
+
*/
|
|
240
|
+
isHardware() {
|
|
241
|
+
return !!this.hardware;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Decode a packet to a frame.
|
|
245
|
+
*
|
|
246
|
+
* Sends a packet to the decoder and attempts to receive a decoded frame.
|
|
247
|
+
* Handles internal buffering - may return null if more packets needed.
|
|
248
|
+
* Automatically manages decoder state and error recovery.
|
|
249
|
+
*
|
|
250
|
+
* Direct mapping to avcodec_send_packet() and avcodec_receive_frame().
|
|
251
|
+
*
|
|
252
|
+
* @param packet - Compressed packet to decode
|
|
253
|
+
* @returns Decoded frame or null if more data needed
|
|
254
|
+
*
|
|
255
|
+
* @throws {Error} If decoder is closed
|
|
256
|
+
* @throws {FFmpegError} If decoding fails
|
|
184
257
|
*
|
|
185
258
|
* @example
|
|
186
259
|
* ```typescript
|
|
187
260
|
* const frame = await decoder.decode(packet);
|
|
188
261
|
* if (frame) {
|
|
189
|
-
*
|
|
262
|
+
* console.log(`Decoded frame with PTS: ${frame.pts}`);
|
|
263
|
+
* frame.free();
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* for await (const packet of input.packets()) {
|
|
270
|
+
* if (packet.streamIndex === decoder.getStreamIndex()) {
|
|
271
|
+
* const frame = await decoder.decode(packet);
|
|
272
|
+
* if (frame) {
|
|
273
|
+
* await processFrame(frame);
|
|
274
|
+
* frame.free();
|
|
275
|
+
* }
|
|
276
|
+
* }
|
|
277
|
+
* packet.free();
|
|
190
278
|
* }
|
|
191
279
|
* ```
|
|
280
|
+
*
|
|
281
|
+
* @see {@link frames} For automatic packet iteration
|
|
282
|
+
* @see {@link flush} For end-of-stream handling
|
|
192
283
|
*/
|
|
193
284
|
async decode(packet) {
|
|
194
285
|
if (!this.isOpen) {
|
|
@@ -212,26 +303,31 @@ export class Decoder {
|
|
|
212
303
|
return frame;
|
|
213
304
|
}
|
|
214
305
|
/**
|
|
215
|
-
* Flush decoder and get
|
|
306
|
+
* Flush decoder and get buffered frame.
|
|
216
307
|
*
|
|
217
|
-
*
|
|
218
|
-
* Call repeatedly until
|
|
308
|
+
* Signals end-of-stream and retrieves remaining frames.
|
|
309
|
+
* Call repeatedly until null to get all buffered frames.
|
|
310
|
+
* Essential for ensuring all frames are decoded.
|
|
219
311
|
*
|
|
220
|
-
*
|
|
221
|
-
* Retrieves buffered frames from the decoder.
|
|
312
|
+
* Direct mapping to avcodec_send_packet(NULL).
|
|
222
313
|
*
|
|
223
|
-
* @returns
|
|
314
|
+
* @returns Buffered frame or null if none remaining
|
|
224
315
|
*
|
|
225
316
|
* @throws {Error} If decoder is closed
|
|
226
317
|
*
|
|
227
318
|
* @example
|
|
228
319
|
* ```typescript
|
|
229
|
-
* //
|
|
320
|
+
* // After all packets processed
|
|
230
321
|
* let frame;
|
|
231
322
|
* while ((frame = await decoder.flush()) !== null) {
|
|
232
|
-
*
|
|
323
|
+
* console.log('Got buffered frame');
|
|
324
|
+
* await processFrame(frame);
|
|
325
|
+
* frame.free();
|
|
233
326
|
* }
|
|
234
327
|
* ```
|
|
328
|
+
*
|
|
329
|
+
* @see {@link flushFrames} For async iteration
|
|
330
|
+
* @see {@link frames} For complete decoding pipeline
|
|
235
331
|
*/
|
|
236
332
|
async flush() {
|
|
237
333
|
if (!this.isOpen) {
|
|
@@ -244,26 +340,27 @@ export class Decoder {
|
|
|
244
340
|
return frame;
|
|
245
341
|
}
|
|
246
342
|
/**
|
|
247
|
-
* Flush
|
|
248
|
-
*
|
|
249
|
-
* More convenient than calling flush() in a loop.
|
|
250
|
-
* Automatically sends flush signal and yields all buffered frames.
|
|
343
|
+
* Flush all buffered frames as async generator.
|
|
251
344
|
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
* @returns Async generator of remaining frames
|
|
345
|
+
* Convenient async iteration over remaining frames.
|
|
346
|
+
* Automatically handles repeated flush calls.
|
|
347
|
+
* Useful for end-of-stream processing.
|
|
256
348
|
*
|
|
349
|
+
* @yields Buffered frames
|
|
257
350
|
* @throws {Error} If decoder is closed
|
|
258
351
|
*
|
|
259
352
|
* @example
|
|
260
353
|
* ```typescript
|
|
261
|
-
* //
|
|
354
|
+
* // Flush at end of decoding
|
|
262
355
|
* for await (const frame of decoder.flushFrames()) {
|
|
263
|
-
*
|
|
264
|
-
*
|
|
356
|
+
* console.log('Processing buffered frame');
|
|
357
|
+
* await encoder.encode(frame);
|
|
358
|
+
* frame.free();
|
|
265
359
|
* }
|
|
266
360
|
* ```
|
|
361
|
+
*
|
|
362
|
+
* @see {@link flush} For single frame flush
|
|
363
|
+
* @see {@link frames} For complete pipeline
|
|
267
364
|
*/
|
|
268
365
|
async *flushFrames() {
|
|
269
366
|
if (!this.isOpen) {
|
|
@@ -275,36 +372,55 @@ export class Decoder {
|
|
|
275
372
|
}
|
|
276
373
|
}
|
|
277
374
|
/**
|
|
278
|
-
*
|
|
375
|
+
* Decode packet stream to frame stream.
|
|
279
376
|
*
|
|
280
|
-
*
|
|
281
|
-
* Automatically
|
|
377
|
+
* High-level async generator for complete decoding pipeline.
|
|
378
|
+
* Automatically filters packets for this stream, manages memory,
|
|
379
|
+
* and flushes buffered frames at end.
|
|
380
|
+
* Primary interface for stream-based decoding.
|
|
282
381
|
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* Use 'using' statement or manually call frame.free() to avoid memory leaks.
|
|
288
|
-
*
|
|
289
|
-
* @param packets - Async iterable of packets (e.g., from MediaInput.packets())
|
|
290
|
-
*
|
|
291
|
-
* @yields Decoded frames (ownership transferred to caller)
|
|
382
|
+
* @param packets - Async iterable of packets
|
|
383
|
+
* @yields Decoded frames
|
|
384
|
+
* @throws {Error} If decoder is closed
|
|
385
|
+
* @throws {FFmpegError} If decoding fails
|
|
292
386
|
*
|
|
293
387
|
* @example
|
|
294
388
|
* ```typescript
|
|
295
|
-
*
|
|
296
|
-
*
|
|
389
|
+
* await using input = await MediaInput.open('video.mp4');
|
|
390
|
+
* using decoder = await Decoder.create(input.video());
|
|
391
|
+
*
|
|
392
|
+
* for await (const frame of decoder.frames(input.packets())) {
|
|
297
393
|
* console.log(`Frame: ${frame.width}x${frame.height}`);
|
|
298
|
-
*
|
|
394
|
+
* frame.free();
|
|
299
395
|
* }
|
|
396
|
+
* ```
|
|
300
397
|
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* // Process frame
|
|
305
|
-
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* for await (const frame of decoder.frames(input.packets())) {
|
|
401
|
+
* // Process frame
|
|
402
|
+
* await filter.filterFrame(frame);
|
|
403
|
+
*
|
|
404
|
+
* // Frame automatically freed
|
|
405
|
+
* frame.free();
|
|
306
406
|
* }
|
|
307
407
|
* ```
|
|
408
|
+
*
|
|
409
|
+
* @example
|
|
410
|
+
* ```typescript
|
|
411
|
+
* import { pipeline } from 'node-av/api';
|
|
412
|
+
*
|
|
413
|
+
* const control = pipeline(
|
|
414
|
+
* input,
|
|
415
|
+
* decoder,
|
|
416
|
+
* encoder,
|
|
417
|
+
* output
|
|
418
|
+
* );
|
|
419
|
+
* await control.completion;
|
|
420
|
+
* ```
|
|
421
|
+
*
|
|
422
|
+
* @see {@link decode} For single packet decoding
|
|
423
|
+
* @see {@link MediaInput.packets} For packet source
|
|
308
424
|
*/
|
|
309
425
|
async *frames(packets) {
|
|
310
426
|
if (!this.isOpen) {
|
|
@@ -335,42 +451,78 @@ export class Decoder {
|
|
|
335
451
|
/**
|
|
336
452
|
* Close decoder and free resources.
|
|
337
453
|
*
|
|
338
|
-
*
|
|
454
|
+
* Releases codec context and internal frame buffer.
|
|
455
|
+
* Safe to call multiple times.
|
|
456
|
+
* Automatically called by Symbol.dispose.
|
|
339
457
|
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* const decoder = await Decoder.create(stream);
|
|
461
|
+
* try {
|
|
462
|
+
* // Use decoder
|
|
463
|
+
* } finally {
|
|
464
|
+
* decoder.close();
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* @see {@link Symbol.dispose} For automatic cleanup
|
|
342
469
|
*/
|
|
343
470
|
close() {
|
|
344
|
-
if (!this.isOpen)
|
|
471
|
+
if (!this.isOpen) {
|
|
345
472
|
return;
|
|
473
|
+
}
|
|
346
474
|
this.frame.free();
|
|
347
475
|
this.codecContext.freeContext();
|
|
348
|
-
// NOTE: We do NOT dispose the hardware context here
|
|
349
|
-
// The caller who created the HardwareContext is responsible for disposing it
|
|
350
|
-
// This allows reusing the same HardwareContext for multiple decoders
|
|
351
476
|
this.isOpen = false;
|
|
352
477
|
}
|
|
353
478
|
/**
|
|
354
|
-
* Get
|
|
479
|
+
* Get stream index.
|
|
480
|
+
*
|
|
481
|
+
* Returns the index of the stream being decoded.
|
|
482
|
+
* Used for packet filtering in multi-stream files.
|
|
483
|
+
*
|
|
484
|
+
* @returns Stream index
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```typescript
|
|
488
|
+
* if (packet.streamIndex === decoder.getStreamIndex()) {
|
|
489
|
+
* const frame = await decoder.decode(packet);
|
|
490
|
+
* }
|
|
491
|
+
* ```
|
|
492
|
+
*
|
|
493
|
+
* @see {@link getStream} For full stream object
|
|
355
494
|
*/
|
|
356
495
|
getStreamIndex() {
|
|
357
496
|
return this.streamIndex;
|
|
358
497
|
}
|
|
359
498
|
/**
|
|
360
|
-
* Get
|
|
361
|
-
*
|
|
499
|
+
* Get stream object.
|
|
500
|
+
*
|
|
501
|
+
* Returns the underlying stream being decoded.
|
|
502
|
+
* Provides access to stream metadata and parameters.
|
|
503
|
+
*
|
|
504
|
+
* @returns Stream object
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```typescript
|
|
508
|
+
* const stream = decoder.getStream();
|
|
509
|
+
* console.log(`Duration: ${stream.duration}`);
|
|
510
|
+
* console.log(`Time base: ${stream.timeBase.num}/${stream.timeBase.den}`);
|
|
511
|
+
* ```
|
|
512
|
+
*
|
|
513
|
+
* @see {@link Stream} For stream properties
|
|
514
|
+
* @see {@link getStreamIndex} For index only
|
|
362
515
|
*/
|
|
363
516
|
getStream() {
|
|
364
517
|
return this.stream;
|
|
365
518
|
}
|
|
366
519
|
/**
|
|
367
|
-
* Get codec context
|
|
368
|
-
*
|
|
369
|
-
* Use with caution - direct manipulation may cause issues.
|
|
520
|
+
* Get underlying codec context.
|
|
370
521
|
*
|
|
371
|
-
*
|
|
522
|
+
* Returns the internal codec context for advanced operations.
|
|
523
|
+
* Returns null if decoder is closed.
|
|
372
524
|
*
|
|
373
|
-
* @returns
|
|
525
|
+
* @returns Codec context or null
|
|
374
526
|
*
|
|
375
527
|
* @internal
|
|
376
528
|
*/
|
|
@@ -378,26 +530,26 @@ export class Decoder {
|
|
|
378
530
|
return this.isOpen ? this.codecContext : null;
|
|
379
531
|
}
|
|
380
532
|
/**
|
|
381
|
-
* Receive
|
|
533
|
+
* Receive frame from decoder.
|
|
382
534
|
*
|
|
383
|
-
* Internal method to
|
|
535
|
+
* Internal method to get decoded frames from codec.
|
|
536
|
+
* Handles frame cloning and error checking.
|
|
537
|
+
* Hardware frames include hw_frames_ctx reference.
|
|
384
538
|
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
539
|
+
* Direct mapping to avcodec_receive_frame().
|
|
540
|
+
*
|
|
541
|
+
* @returns Cloned frame or null
|
|
542
|
+
*
|
|
543
|
+
* @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
|
|
387
544
|
*
|
|
388
|
-
* @returns Frame or null if no frame available
|
|
389
|
-
* @internal
|
|
390
545
|
*/
|
|
391
546
|
async receiveFrameInternal() {
|
|
392
547
|
// Clear previous frame data
|
|
393
548
|
this.frame.unref();
|
|
394
549
|
const ret = await this.codecContext.receiveFrame(this.frame);
|
|
395
550
|
if (ret === 0) {
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
if (this.hardware && this.frame.hwFramesCtx) {
|
|
399
|
-
this.hardware.framesContext = this.frame.hwFramesCtx;
|
|
400
|
-
}
|
|
551
|
+
// Note: hw_frames_ctx is now available in the frame
|
|
552
|
+
// Other components should get it directly from frames, not from HardwareContext
|
|
401
553
|
// Got a frame, clone it for the user
|
|
402
554
|
return this.frame.clone();
|
|
403
555
|
}
|
|
@@ -412,10 +564,20 @@ export class Decoder {
|
|
|
412
564
|
}
|
|
413
565
|
}
|
|
414
566
|
/**
|
|
415
|
-
*
|
|
567
|
+
* Dispose of decoder.
|
|
568
|
+
*
|
|
569
|
+
* Implements Disposable interface for automatic cleanup.
|
|
570
|
+
* Equivalent to calling close().
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* {
|
|
575
|
+
* using decoder = await Decoder.create(stream);
|
|
576
|
+
* // Decode frames...
|
|
577
|
+
* } // Automatically closed
|
|
578
|
+
* ```
|
|
416
579
|
*
|
|
417
|
-
*
|
|
418
|
-
* Calls close() to free all resources.
|
|
580
|
+
* @see {@link close} For manual cleanup
|
|
419
581
|
*/
|
|
420
582
|
[Symbol.dispose]() {
|
|
421
583
|
this.close();
|