node-av 3.1.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +65 -52
  2. package/binding.gyp +4 -0
  3. package/dist/api/audio-frame-buffer.d.ts +201 -0
  4. package/dist/api/audio-frame-buffer.js +275 -0
  5. package/dist/api/audio-frame-buffer.js.map +1 -0
  6. package/dist/api/bitstream-filter.d.ts +319 -78
  7. package/dist/api/bitstream-filter.js +680 -151
  8. package/dist/api/bitstream-filter.js.map +1 -1
  9. package/dist/api/constants.d.ts +44 -0
  10. package/dist/api/constants.js +45 -0
  11. package/dist/api/constants.js.map +1 -0
  12. package/dist/api/data/test_av1.ivf +0 -0
  13. package/dist/api/data/test_mjpeg.mjpeg +0 -0
  14. package/dist/api/data/test_vp8.ivf +0 -0
  15. package/dist/api/data/test_vp9.ivf +0 -0
  16. package/dist/api/decoder.d.ts +279 -17
  17. package/dist/api/decoder.js +998 -209
  18. package/dist/api/decoder.js.map +1 -1
  19. package/dist/api/{media-input.d.ts → demuxer.d.ts} +294 -44
  20. package/dist/api/demuxer.js +1968 -0
  21. package/dist/api/demuxer.js.map +1 -0
  22. package/dist/api/encoder.d.ts +308 -50
  23. package/dist/api/encoder.js +1133 -111
  24. package/dist/api/encoder.js.map +1 -1
  25. package/dist/api/filter-presets.d.ts +12 -5
  26. package/dist/api/filter-presets.js +21 -7
  27. package/dist/api/filter-presets.js.map +1 -1
  28. package/dist/api/filter.d.ts +406 -40
  29. package/dist/api/filter.js +966 -139
  30. package/dist/api/filter.js.map +1 -1
  31. package/dist/api/{fmp4.d.ts → fmp4-stream.d.ts} +141 -140
  32. package/dist/api/fmp4-stream.js +539 -0
  33. package/dist/api/fmp4-stream.js.map +1 -0
  34. package/dist/api/hardware.d.ts +58 -6
  35. package/dist/api/hardware.js +127 -11
  36. package/dist/api/hardware.js.map +1 -1
  37. package/dist/api/index.d.ts +6 -4
  38. package/dist/api/index.js +14 -8
  39. package/dist/api/index.js.map +1 -1
  40. package/dist/api/io-stream.d.ts +3 -3
  41. package/dist/api/io-stream.js +5 -4
  42. package/dist/api/io-stream.js.map +1 -1
  43. package/dist/api/{media-output.d.ts → muxer.d.ts} +274 -60
  44. package/dist/api/muxer.js +1934 -0
  45. package/dist/api/muxer.js.map +1 -0
  46. package/dist/api/pipeline.d.ts +77 -29
  47. package/dist/api/pipeline.js +435 -425
  48. package/dist/api/pipeline.js.map +1 -1
  49. package/dist/api/rtp-stream.d.ts +312 -0
  50. package/dist/api/rtp-stream.js +630 -0
  51. package/dist/api/rtp-stream.js.map +1 -0
  52. package/dist/api/types.d.ts +476 -55
  53. package/dist/api/utilities/async-queue.d.ts +91 -0
  54. package/dist/api/utilities/async-queue.js +162 -0
  55. package/dist/api/utilities/async-queue.js.map +1 -0
  56. package/dist/api/utilities/audio-sample.d.ts +1 -1
  57. package/dist/api/utilities/image.d.ts +1 -1
  58. package/dist/api/utilities/index.d.ts +2 -0
  59. package/dist/api/utilities/index.js +4 -0
  60. package/dist/api/utilities/index.js.map +1 -1
  61. package/dist/api/utilities/media-type.d.ts +1 -1
  62. package/dist/api/utilities/pixel-format.d.ts +1 -1
  63. package/dist/api/utilities/sample-format.d.ts +1 -1
  64. package/dist/api/utilities/scheduler.d.ts +169 -0
  65. package/dist/api/utilities/scheduler.js +136 -0
  66. package/dist/api/utilities/scheduler.js.map +1 -0
  67. package/dist/api/utilities/streaming.d.ts +74 -15
  68. package/dist/api/utilities/streaming.js +170 -12
  69. package/dist/api/utilities/streaming.js.map +1 -1
  70. package/dist/api/utilities/timestamp.d.ts +1 -1
  71. package/dist/api/webrtc-stream.d.ts +288 -0
  72. package/dist/api/webrtc-stream.js +440 -0
  73. package/dist/api/webrtc-stream.js.map +1 -0
  74. package/dist/constants/constants.d.ts +51 -1
  75. package/dist/constants/constants.js +47 -1
  76. package/dist/constants/constants.js.map +1 -1
  77. package/dist/constants/encoders.d.ts +2 -1
  78. package/dist/constants/encoders.js +4 -3
  79. package/dist/constants/encoders.js.map +1 -1
  80. package/dist/constants/hardware.d.ts +26 -0
  81. package/dist/constants/hardware.js +27 -0
  82. package/dist/constants/hardware.js.map +1 -0
  83. package/dist/constants/index.d.ts +1 -0
  84. package/dist/constants/index.js +1 -0
  85. package/dist/constants/index.js.map +1 -1
  86. package/dist/lib/binding.d.ts +19 -8
  87. package/dist/lib/binding.js.map +1 -1
  88. package/dist/lib/codec-context.d.ts +87 -0
  89. package/dist/lib/codec-context.js +125 -4
  90. package/dist/lib/codec-context.js.map +1 -1
  91. package/dist/lib/codec-parameters.d.ts +183 -1
  92. package/dist/lib/codec-parameters.js +209 -0
  93. package/dist/lib/codec-parameters.js.map +1 -1
  94. package/dist/lib/codec-parser.d.ts +23 -0
  95. package/dist/lib/codec-parser.js +25 -0
  96. package/dist/lib/codec-parser.js.map +1 -1
  97. package/dist/lib/codec.d.ts +26 -4
  98. package/dist/lib/codec.js +35 -0
  99. package/dist/lib/codec.js.map +1 -1
  100. package/dist/lib/dictionary.js +1 -0
  101. package/dist/lib/dictionary.js.map +1 -1
  102. package/dist/lib/error.js +1 -1
  103. package/dist/lib/error.js.map +1 -1
  104. package/dist/lib/filter-context.d.ts +52 -11
  105. package/dist/lib/filter-context.js +56 -12
  106. package/dist/lib/filter-context.js.map +1 -1
  107. package/dist/lib/filter-graph.d.ts +9 -0
  108. package/dist/lib/filter-graph.js +13 -0
  109. package/dist/lib/filter-graph.js.map +1 -1
  110. package/dist/lib/filter.d.ts +21 -0
  111. package/dist/lib/filter.js +28 -0
  112. package/dist/lib/filter.js.map +1 -1
  113. package/dist/lib/format-context.d.ts +48 -14
  114. package/dist/lib/format-context.js +76 -7
  115. package/dist/lib/format-context.js.map +1 -1
  116. package/dist/lib/frame.d.ts +168 -0
  117. package/dist/lib/frame.js +212 -0
  118. package/dist/lib/frame.js.map +1 -1
  119. package/dist/lib/hardware-device-context.d.ts +3 -2
  120. package/dist/lib/hardware-device-context.js.map +1 -1
  121. package/dist/lib/index.d.ts +1 -0
  122. package/dist/lib/index.js +2 -0
  123. package/dist/lib/index.js.map +1 -1
  124. package/dist/lib/input-format.d.ts +21 -0
  125. package/dist/lib/input-format.js +42 -2
  126. package/dist/lib/input-format.js.map +1 -1
  127. package/dist/lib/native-types.d.ts +48 -26
  128. package/dist/lib/option.d.ts +25 -13
  129. package/dist/lib/option.js +28 -0
  130. package/dist/lib/option.js.map +1 -1
  131. package/dist/lib/output-format.d.ts +22 -1
  132. package/dist/lib/output-format.js +28 -0
  133. package/dist/lib/output-format.js.map +1 -1
  134. package/dist/lib/packet.d.ts +35 -0
  135. package/dist/lib/packet.js +52 -2
  136. package/dist/lib/packet.js.map +1 -1
  137. package/dist/lib/stream.d.ts +126 -0
  138. package/dist/lib/stream.js +188 -5
  139. package/dist/lib/stream.js.map +1 -1
  140. package/dist/lib/sync-queue.d.ts +179 -0
  141. package/dist/lib/sync-queue.js +197 -0
  142. package/dist/lib/sync-queue.js.map +1 -0
  143. package/dist/lib/types.d.ts +27 -1
  144. package/dist/lib/utilities.d.ts +281 -53
  145. package/dist/lib/utilities.js +298 -55
  146. package/dist/lib/utilities.js.map +1 -1
  147. package/package.json +20 -19
  148. package/dist/api/fmp4.js +0 -710
  149. package/dist/api/fmp4.js.map +0 -1
  150. package/dist/api/media-input.js +0 -1075
  151. package/dist/api/media-input.js.map +0 -1
  152. package/dist/api/media-output.js +0 -1040
  153. package/dist/api/media-output.js.map +0 -1
  154. package/dist/api/webrtc.d.ts +0 -664
  155. package/dist/api/webrtc.js +0 -1132
  156. package/dist/api/webrtc.js.map +0 -1
@@ -0,0 +1,539 @@
1
+ import { AV_CODEC_ID_AAC, AV_CODEC_ID_AV1, AV_CODEC_ID_FLAC, AV_CODEC_ID_H264, AV_CODEC_ID_HEVC, AV_CODEC_ID_OPUS, AV_HWDEVICE_TYPE_NONE, AV_SAMPLE_FMT_FLTP, } from '../constants/constants.js';
2
+ import { FF_ENCODER_AAC, FF_ENCODER_LIBX264 } from '../constants/encoders.js';
3
+ import { Codec } from '../lib/codec.js';
4
+ import { avGetCodecString } from '../lib/utilities.js';
5
+ import { Decoder } from './decoder.js';
6
+ import { Demuxer } from './demuxer.js';
7
+ import { Encoder } from './encoder.js';
8
+ import { FilterPreset } from './filter-presets.js';
9
+ import { FilterAPI } from './filter.js';
10
+ import { HardwareContext } from './hardware.js';
11
+ import { Muxer } from './muxer.js';
12
+ import { pipeline } from './pipeline.js';
13
+ /**
14
+ * Target codec strings for fMP4 streaming.
15
+ */
16
+ export const FMP4_CODECS = {
17
+ H264: 'avc1.640029',
18
+ H265: 'hvc1.1.6.L153.B0',
19
+ AV1: 'av01.0.00M.08',
20
+ AAC: 'mp4a.40.2',
21
+ FLAC: 'flac',
22
+ OPUS: 'opus',
23
+ };
24
+ /**
25
+ * High-level fMP4 streaming with automatic codec detection and transcoding.
26
+ *
27
+ * Provides fragmented MP4 streaming for clients.
28
+ * Automatically transcodes video to H.264 and audio to AAC if not supported by client.
29
+ * Client sends supported codecs, server transcodes accordingly.
30
+ * Essential component for building adaptive streaming servers.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { FMP4Stream } from 'node-av/api';
35
+ *
36
+ * // Client sends supported codecs
37
+ * const supportedCodecs = 'avc1.640029,hvc1.1.6.L153.B0,mp4a.40.2,flac';
38
+ *
39
+ * // Create stream with codec negotiation
40
+ * const stream = FMP4Stream.create('rtsp://camera.local/stream', {
41
+ * supportedCodecs,
42
+ * onData: (data) => ws.send(data.data)
43
+ * });
44
+ *
45
+ * // Start streaming (auto-transcodes if needed)
46
+ * await stream.start();
47
+ *
48
+ * // Stop when done
49
+ * await stream.stop();
50
+ * ```
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Stream with hardware acceleration
55
+ * const stream = FMP4Stream.create('input.mp4', {
56
+ * supportedCodecs: 'avc1.640029,mp4a.40.2',
57
+ * hardware: 'auto',
58
+ * fragDuration: 1,
59
+ * onData: (data) => sendToClient(data.data)
60
+ * });
61
+ *
62
+ * await stream.start();
63
+ * await stream.stop();
64
+ * ```
65
+ */
66
+ export class FMP4Stream {
67
+ options;
68
+ inputUrl;
69
+ inputOptions;
70
+ input;
71
+ output;
72
+ hardwareContext;
73
+ videoDecoder;
74
+ videoEncoder;
75
+ audioDecoder;
76
+ audioFilter;
77
+ audioEncoder;
78
+ pipeline;
79
+ supportedCodecs;
80
+ incompleteBoxBuffer = null;
81
+ /**
82
+ * @param inputUrl - Media input URL
83
+ *
84
+ * @param options - Stream configuration options
85
+ *
86
+ * Use {@link create} factory method
87
+ *
88
+ * @internal
89
+ */
90
+ constructor(inputUrl, options) {
91
+ this.inputUrl = inputUrl;
92
+ this.inputOptions = {
93
+ ...options.inputOptions,
94
+ options: {
95
+ flags: 'low_delay',
96
+ fflags: 'nobuffer',
97
+ // analyzeduration: 0,
98
+ // probesize: 32,
99
+ timeout: 5000000,
100
+ rtsp_transport: inputUrl.toLowerCase().startsWith('rtsp') ? 'tcp' : undefined,
101
+ ...options.inputOptions?.options,
102
+ },
103
+ };
104
+ this.options = {
105
+ onData: options.onData ?? (() => { }),
106
+ onClose: options.onClose ?? (() => { }),
107
+ supportedCodecs: options.supportedCodecs ?? '',
108
+ fragDuration: options.fragDuration ?? 1,
109
+ hardware: options.hardware ?? { deviceType: AV_HWDEVICE_TYPE_NONE },
110
+ inputOptions: options.inputOptions,
111
+ bufferSize: options.bufferSize ?? 2 * 1024 * 1024,
112
+ boxMode: options.boxMode ?? false,
113
+ movFlags: options.movFlags ?? '+frag_keyframe+separate_moof+default_base_moof+empty_moov',
114
+ };
115
+ // Parse supported codecs
116
+ this.supportedCodecs = new Set(this.options.supportedCodecs
117
+ .split(',')
118
+ .map((c) => c.trim())
119
+ .filter(Boolean));
120
+ }
121
+ /**
122
+ * Create a fMP4 stream from a media source.
123
+ *
124
+ * Configures the stream with input URL and options. The input is not opened
125
+ * until start() is called, allowing the stream to be reused after stop().
126
+ *
127
+ * @param inputUrl - Media source URL (RTSP, file path, HTTP, etc.)
128
+ *
129
+ * @param options - Stream configuration options with supported codecs
130
+ *
131
+ * @returns Configured fMP4 stream instance
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // Stream from file with codec negotiation
136
+ * const stream = FMP4Stream.create('video.mp4', {
137
+ * supportedCodecs: 'avc1.640029,mp4a.40.2',
138
+ * onData: (data) => ws.send(data.data)
139
+ * });
140
+ * ```
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // Stream from RTSP with auto hardware acceleration
145
+ * const stream = FMP4Stream.create('rtsp://camera.local/stream', {
146
+ * supportedCodecs: 'avc1.640029,hvc1.1.6.L153.B0,mp4a.40.2',
147
+ * hardware: 'auto',
148
+ * fragDuration: 0.5
149
+ * });
150
+ * ```
151
+ */
152
+ static create(inputUrl, options = {}) {
153
+ return new FMP4Stream(inputUrl, options);
154
+ }
155
+ /**
156
+ * Get the demuxer instance.
157
+ *
158
+ * Used for accessing the underlying demuxer.
159
+ * Only available after start() is called.
160
+ *
161
+ * @returns Demuxer instance or undefined if not started
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const stream = FMP4Stream.create('input.mp4', {
166
+ * const input = stream.getInput();
167
+ * console.log('Bitrate:', input?.bitRate);
168
+ * ```
169
+ */
170
+ getInput() {
171
+ return this.input;
172
+ }
173
+ /**
174
+ * Get the codec string that will be used by client.
175
+ *
176
+ * Returns the MIME type codec string based on input codecs and transcoding decisions.
177
+ * Call this after start() is called to know what codec string to use for addSourceBuffer().
178
+ *
179
+ * @returns MIME type codec string (e.g., "avc1.640029,mp4a.40.2")
180
+ *
181
+ * @throws {Error} If called before start() is called
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const stream = await FMP4Stream.create('input.mp4', {
186
+ * supportedCodecs: 'avc1.640029,mp4a.40.2'
187
+ * });
188
+ *
189
+ * await stream.start(); // Must start first
190
+ * const codecString = stream.getCodecString();
191
+ * console.log(codecString); // "avc1.640029,mp4a.40.2"
192
+ * // Use this for: sourceBuffer = mediaSource.addSourceBuffer(`video/mp4; codecs="${codecString}"`);
193
+ * ```
194
+ */
195
+ getCodecString() {
196
+ if (!this.input) {
197
+ throw new Error('Input not opened. Call start() first to open the input.');
198
+ }
199
+ const videoStream = this.input.video();
200
+ const audioStream = this.input.audio();
201
+ const videoCodecId = videoStream?.codecpar.codecId;
202
+ const audioCodecId = audioStream?.codecpar.codecId;
203
+ // Determine video codec string
204
+ let videoCodec = null;
205
+ if (videoCodecId) {
206
+ const needsVideoTranscode = !this.isVideoCodecSupported(videoCodecId);
207
+ if (needsVideoTranscode) {
208
+ // Transcoding to H.264
209
+ videoCodec = FMP4_CODECS.H264;
210
+ }
211
+ else if (videoCodecId === AV_CODEC_ID_H264) {
212
+ // H.264 - use RFC 6381 codec string from input
213
+ const codecString = avGetCodecString(videoStream.codecpar);
214
+ videoCodec = codecString ?? FMP4_CODECS.H264;
215
+ }
216
+ else if (videoCodecId === AV_CODEC_ID_HEVC) {
217
+ // H.265 - use RFC 6381 codec string from input
218
+ const codecString = avGetCodecString(videoStream.codecpar);
219
+ videoCodec = codecString ?? FMP4_CODECS.H265;
220
+ }
221
+ else if (videoCodecId === AV_CODEC_ID_AV1) {
222
+ // AV1 - use RFC 6381 codec string from input
223
+ const codecString = avGetCodecString(videoStream.codecpar);
224
+ videoCodec = codecString ?? FMP4_CODECS.AV1;
225
+ }
226
+ else {
227
+ // Fallback to H.264 (should not happen as we transcode unsupported codecs)
228
+ videoCodec = FMP4_CODECS.H264;
229
+ }
230
+ }
231
+ // Determine audio codec string
232
+ let audioCodec = null;
233
+ if (audioCodecId) {
234
+ const needsAudioTranscode = !this.isAudioCodecSupported(audioCodecId);
235
+ if (needsAudioTranscode) {
236
+ // Transcoding to AAC
237
+ audioCodec = FMP4_CODECS.AAC;
238
+ }
239
+ else if (audioCodecId === AV_CODEC_ID_AAC) {
240
+ // AAC - use fixed codec string
241
+ audioCodec = FMP4_CODECS.AAC;
242
+ }
243
+ else if (audioCodecId === AV_CODEC_ID_FLAC) {
244
+ // FLAC
245
+ audioCodec = FMP4_CODECS.FLAC;
246
+ }
247
+ else if (audioCodecId === AV_CODEC_ID_OPUS) {
248
+ // Opus
249
+ audioCodec = FMP4_CODECS.OPUS;
250
+ }
251
+ else {
252
+ // Fallback to AAC (should not happen as we transcode unsupported codecs)
253
+ audioCodec = FMP4_CODECS.AAC;
254
+ }
255
+ }
256
+ // Combine video and audio codec strings
257
+ return [videoCodec, audioCodec].filter(Boolean).join(',');
258
+ }
259
+ /**
260
+ * Start streaming media to fMP4 chunks.
261
+ *
262
+ * Begins the media processing pipeline, reading packets from input,
263
+ * transcoding based on supported codecs, and generating fMP4 chunks.
264
+ * Video transcodes to H.264 if H.264/H.265 not supported.
265
+ * Audio transcodes to AAC if AAC/FLAC/Opus not supported.
266
+ * This method returns immediately after starting the pipeline.
267
+ *
268
+ * @returns Promise that resolves when pipeline is started
269
+ *
270
+ * @throws {FFmpegError} If setup fails
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * const stream = await FMP4Stream.create('input.mp4', {
275
+ * supportedCodecs: 'avc1.640029,mp4a.40.2',
276
+ * onData: (data) => sendToClient(data.data)
277
+ * });
278
+ *
279
+ * // Start streaming (returns immediately)
280
+ * await stream.start();
281
+ *
282
+ * // Later: stop streaming
283
+ * await stream.stop();
284
+ * ```
285
+ */
286
+ async start() {
287
+ if (this.pipeline) {
288
+ return;
289
+ }
290
+ // Open input if not already open
291
+ this.input ??= await Demuxer.open(this.inputUrl, this.inputOptions);
292
+ const videoStream = this.input.video();
293
+ const audioStream = this.input.audio();
294
+ // Check if video needs transcoding
295
+ const needsVideoTranscode = videoStream && !this.isVideoCodecSupported(videoStream.codecpar.codecId);
296
+ if (needsVideoTranscode) {
297
+ // Check if we need hardware acceleration
298
+ if (this.options.hardware === 'auto') {
299
+ this.hardwareContext = HardwareContext.auto();
300
+ }
301
+ else if (this.options.hardware.deviceType !== AV_HWDEVICE_TYPE_NONE) {
302
+ this.hardwareContext = HardwareContext.create(this.options.hardware.deviceType, this.options.hardware.device, this.options.hardware.options);
303
+ }
304
+ // Transcode to H.264
305
+ this.videoDecoder = await Decoder.create(videoStream, {
306
+ hardware: this.hardwareContext,
307
+ exitOnError: false,
308
+ });
309
+ const encoderCodec = this.hardwareContext?.getEncoderCodec('h264') ?? Codec.findEncoderByName(FF_ENCODER_LIBX264);
310
+ const encoderOptions = {};
311
+ if (encoderCodec.name === FF_ENCODER_LIBX264 || encoderCodec.name === FF_ENCODER_LIBX264) {
312
+ encoderOptions.preset = 'ultrafast';
313
+ encoderOptions.tune = 'zerolatency';
314
+ }
315
+ this.videoEncoder = await Encoder.create(encoderCodec, {
316
+ decoder: this.videoDecoder,
317
+ options: encoderOptions,
318
+ });
319
+ }
320
+ // Check if audio needs transcoding
321
+ const needsAudioTranscode = audioStream && !this.isAudioCodecSupported(audioStream.codecpar.codecId);
322
+ if (needsAudioTranscode) {
323
+ // Transcode to AAC
324
+ this.audioDecoder = await Decoder.create(audioStream, {
325
+ exitOnError: false,
326
+ });
327
+ const targetSampleRate = 44100;
328
+ const filterChain = FilterPreset.chain().aformat(AV_SAMPLE_FMT_FLTP, targetSampleRate, 'stereo').build();
329
+ this.audioFilter = FilterAPI.create(filterChain);
330
+ this.audioEncoder = await Encoder.create(FF_ENCODER_AAC, {
331
+ decoder: this.audioDecoder,
332
+ filter: this.audioFilter,
333
+ });
334
+ }
335
+ // Setup output with callback
336
+ const cb = {
337
+ write: (buffer) => {
338
+ if (this.options.boxMode) {
339
+ // Box mode: buffer until we have complete boxes
340
+ this.processBoxMode(buffer);
341
+ }
342
+ else {
343
+ // Chunk mode: send raw data immediately
344
+ this.options.onData(buffer, {
345
+ isComplete: false,
346
+ boxes: [],
347
+ });
348
+ }
349
+ return buffer.length;
350
+ },
351
+ };
352
+ this.output = await Muxer.open(cb, {
353
+ input: this.input,
354
+ format: 'mp4',
355
+ bufferSize: this.options.bufferSize,
356
+ exitOnError: false,
357
+ options: {
358
+ movflags: this.options.movFlags,
359
+ frag_duration: this.options.fragDuration,
360
+ },
361
+ });
362
+ this.runPipeline()
363
+ .then(() => {
364
+ // Pipeline completed successfully
365
+ this.options.onClose?.();
366
+ })
367
+ .catch(async (error) => {
368
+ await this.stop();
369
+ this.options.onClose?.(error);
370
+ });
371
+ }
372
+ /**
373
+ * Stop streaming gracefully and clean up all resources.
374
+ *
375
+ * Stops the pipeline, closes output, and releases all FFmpeg resources.
376
+ * Safe to call multiple times. After stopping, you can call start() again
377
+ * to restart the stream.
378
+ *
379
+ * @example
380
+ * ```typescript
381
+ * const stream = await FMP4Stream.create('input.mp4', {
382
+ * supportedCodecs: 'avc1.640029,mp4a.40.2'
383
+ * });
384
+ * await stream.start();
385
+ *
386
+ * // Stop after 10 seconds
387
+ * setTimeout(async () => await stream.stop(), 10000);
388
+ * ```
389
+ */
390
+ async stop() {
391
+ // Stop pipeline if running and wait for completion
392
+ if (this.pipeline && !this.pipeline.isStopped()) {
393
+ this.pipeline.stop();
394
+ await this.pipeline.completion;
395
+ this.pipeline = undefined;
396
+ }
397
+ // Close all resources
398
+ await this.input?.close();
399
+ this.input = undefined;
400
+ this.videoDecoder?.close();
401
+ this.videoDecoder = undefined;
402
+ this.videoEncoder?.close();
403
+ this.videoEncoder = undefined;
404
+ this.audioDecoder?.close();
405
+ this.audioDecoder = undefined;
406
+ this.audioFilter?.close();
407
+ this.audioFilter = undefined;
408
+ this.audioEncoder?.close();
409
+ this.audioEncoder = undefined;
410
+ this.hardwareContext?.dispose();
411
+ this.hardwareContext = undefined;
412
+ await this.output?.close();
413
+ this.output = undefined;
414
+ }
415
+ /**
416
+ * Run the streaming pipeline until completion or stopped.
417
+ *
418
+ * @internal
419
+ */
420
+ async runPipeline() {
421
+ if (!this.input || !this.output) {
422
+ return;
423
+ }
424
+ const hasVideo = this.input?.video() !== undefined;
425
+ const hasAudio = this.input?.audio() !== undefined;
426
+ if (hasAudio && hasVideo) {
427
+ this.pipeline = pipeline(this.input, {
428
+ video: [this.videoDecoder, this.videoEncoder],
429
+ audio: [this.audioDecoder, this.audioFilter, this.audioEncoder],
430
+ }, this.output);
431
+ }
432
+ else if (hasVideo) {
433
+ this.pipeline = pipeline(this.input, {
434
+ video: [this.videoDecoder, this.videoEncoder],
435
+ }, this.output);
436
+ }
437
+ else if (hasAudio) {
438
+ this.pipeline = pipeline(this.input, {
439
+ audio: [this.audioDecoder, this.audioFilter, this.audioEncoder],
440
+ }, this.output);
441
+ }
442
+ else {
443
+ throw new Error('No audio or video streams found in input');
444
+ }
445
+ await this.pipeline.completion;
446
+ this.pipeline = undefined;
447
+ }
448
+ /**
449
+ * Check if video codec is supported.
450
+ *
451
+ * @param codecId - Codec ID
452
+ *
453
+ * @returns True if H.264, H.265, or AV1 is in supported codecs
454
+ *
455
+ * @internal
456
+ */
457
+ isVideoCodecSupported(codecId) {
458
+ if (codecId === AV_CODEC_ID_H264 && (this.supportedCodecs.has(FMP4_CODECS.H264) || this.supportedCodecs.has('avc1'))) {
459
+ return true;
460
+ }
461
+ if (codecId === AV_CODEC_ID_HEVC && (this.supportedCodecs.has(FMP4_CODECS.H265) || this.supportedCodecs.has('hvc1') || this.supportedCodecs.has('hev1'))) {
462
+ return true;
463
+ }
464
+ if (codecId === AV_CODEC_ID_AV1 && (this.supportedCodecs.has(FMP4_CODECS.AV1) || this.supportedCodecs.has('av01'))) {
465
+ return true;
466
+ }
467
+ return false;
468
+ }
469
+ /**
470
+ * Check if audio codec is supported.
471
+ *
472
+ * @param codecId - Codec ID
473
+ *
474
+ * @returns True if AAC, FLAC, or Opus is in supported codecs
475
+ *
476
+ * @internal
477
+ */
478
+ isAudioCodecSupported(codecId) {
479
+ if (codecId === AV_CODEC_ID_AAC && this.supportedCodecs.has(FMP4_CODECS.AAC)) {
480
+ return true;
481
+ }
482
+ if (codecId === AV_CODEC_ID_FLAC && this.supportedCodecs.has(FMP4_CODECS.FLAC)) {
483
+ return true;
484
+ }
485
+ if (codecId === AV_CODEC_ID_OPUS && this.supportedCodecs.has(FMP4_CODECS.OPUS)) {
486
+ return true;
487
+ }
488
+ return false;
489
+ }
490
+ /**
491
+ * Process buffer in box mode - buffers until complete boxes are available.
492
+ *
493
+ * @param chunk - Incoming data chunk from FFmpeg
494
+ *
495
+ * @internal
496
+ */
497
+ processBoxMode(chunk) {
498
+ // If we have an incomplete box from previous chunk, append to it
499
+ if (this.incompleteBoxBuffer) {
500
+ chunk = Buffer.concat([this.incompleteBoxBuffer, chunk]);
501
+ this.incompleteBoxBuffer = null;
502
+ }
503
+ let offset = 0;
504
+ const boxes = [];
505
+ while (offset + 8 <= chunk.length) {
506
+ // Read box header
507
+ const boxSize = chunk.readUInt32BE(offset);
508
+ const boxType = chunk.toString('ascii', offset + 4, offset + 8);
509
+ // Check if we have the complete box
510
+ if (offset + boxSize > chunk.length) {
511
+ // Box is incomplete - save for next chunk
512
+ this.incompleteBoxBuffer = chunk.subarray(offset);
513
+ break;
514
+ }
515
+ // We have the complete box - parse it
516
+ const box = {
517
+ type: boxType,
518
+ size: boxSize,
519
+ data: chunk.subarray(offset + 8, offset + boxSize),
520
+ offset: offset,
521
+ };
522
+ boxes.push(box);
523
+ // Move to next box
524
+ offset += boxSize;
525
+ // Safety check: invalid box size
526
+ if (boxSize < 8) {
527
+ break;
528
+ }
529
+ }
530
+ // If we have complete boxes, send them to the callback
531
+ if (boxes.length > 0) {
532
+ this.options.onData(chunk.subarray(0, offset), {
533
+ isComplete: true,
534
+ boxes,
535
+ });
536
+ }
537
+ }
538
+ }
539
+ //# sourceMappingURL=fmp4-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fmp4-stream.js","sourceRoot":"","sources":["../../src/api/fmp4-stream.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
- import { Codec, HardwareDeviceContext } from '../lib/index.js';
2
- import type { AVCodecID, AVHWDeviceType, AVPixelFormat, FFDecoderCodec, FFEncoderCodec } from '../constants/index.js';
1
+ import { Codec } from '../lib/codec.js';
2
+ import { HardwareDeviceContext } from '../lib/hardware-device-context.js';
3
+ import { Stream } from '../lib/stream.js';
4
+ import type { AVCodecID, AVHWDeviceType, AVPixelFormat, FFDecoderCodec, FFEncoderCodec, FFHWDeviceType } from '../constants/index.js';
3
5
  import type { BaseCodecName, HardwareOptions } from './types.js';
4
6
  /**
5
7
  * High-level hardware acceleration management.
@@ -120,7 +122,57 @@ export declare class HardwareContext implements Disposable {
120
122
  * @see {@link auto} For automatic detection
121
123
  * @see {@link HardwareDeviceContext} For low-level API
122
124
  */
123
- static create(deviceType: AVHWDeviceType, device?: string, options?: Record<string, string>): HardwareContext | null;
125
+ static create(deviceType: AVHWDeviceType | FFHWDeviceType, device?: string, options?: Record<string, string>): HardwareContext | null;
126
+ /**
127
+ * Create a derived hardware device context.
128
+ *
129
+ * Creates a new hardware context derived from an existing one.
130
+ * Allows creating contexts on different device types that can share
131
+ * memory or resources with the source device. Useful for cross-device
132
+ * pipelines (e.g., VAAPI → OpenCL, CUDA → Vulkan).
133
+ *
134
+ * Direct mapping to av_hwdevice_ctx_create_derived().
135
+ *
136
+ * @param source - Source hardware context to derive from
137
+ *
138
+ * @param targetType - Target device type to create
139
+ *
140
+ * @returns New hardware context or null if derivation fails
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * import { AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_OPENCL } from 'node-av/constants';
145
+ *
146
+ * // Create VAAPI device for decoding
147
+ * const vaapi = HardwareContext.create(AV_HWDEVICE_TYPE_VAAPI);
148
+ *
149
+ * // Derive OpenCL device for filtering (shares memory with VAAPI)
150
+ * const opencl = HardwareContext.derive(vaapi, AV_HWDEVICE_TYPE_OPENCL);
151
+ *
152
+ * if (opencl) {
153
+ * // Decode with VAAPI
154
+ * const decoder = await Decoder.create(stream, { hardware: vaapi });
155
+ *
156
+ * // Filter with OpenCL (overrides frame's VAAPI context)
157
+ * const filter = FilterAPI.create('program_opencl=...', { hardware: opencl });
158
+ *
159
+ * for await (const frame of decoder.frames()) {
160
+ * // frame has VAAPI context, but filter uses OpenCL
161
+ * const filtered = await filter.apply(frame);
162
+ * }
163
+ * }
164
+ * ```
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * // CUDA → Vulkan derivation
169
+ * const cuda = HardwareContext.create(AV_HWDEVICE_TYPE_CUDA);
170
+ * const vulkan = HardwareContext.derive(cuda, AV_HWDEVICE_TYPE_VULKAN);
171
+ * ```
172
+ *
173
+ * @see {@link create} For creating independent device
174
+ */
175
+ static derive(source: HardwareContext, targetType: AVHWDeviceType | FFHWDeviceType): HardwareContext | null;
124
176
  /**
125
177
  * List all available hardware device types.
126
178
  *
@@ -175,7 +227,7 @@ export declare class HardwareContext implements Disposable {
175
227
  * // Output: "cuda" or "videotoolbox" etc.
176
228
  * ```
177
229
  */
178
- get deviceTypeName(): string;
230
+ get deviceTypeName(): FFHWDeviceType;
179
231
  /**
180
232
  * Get the device pixel format.
181
233
  *
@@ -257,7 +309,7 @@ export declare class HardwareContext implements Disposable {
257
309
  * Returns null if no hardware encoder is available for the codec.
258
310
  * Automatically tests encoder viability before returning.
259
311
  *
260
- * @param codec - Generic codec name (e.g., 'h264', 'hevc', 'av1') or AVCodecID
312
+ * @param codecOrStream - Generic codec name (e.g., 'h264', 'hevc', 'av1'), AVCodecID or Stream
261
313
  *
262
314
  * @param validate - Whether to validate encoder by testing (default: false)
263
315
  *
@@ -283,7 +335,7 @@ export declare class HardwareContext implements Disposable {
283
335
  *
284
336
  * @see {@link Encoder.create} For using the codec
285
337
  */
286
- getEncoderCodec(codec: BaseCodecName | AVCodecID, validate?: boolean): Codec | null;
338
+ getEncoderCodec(codecOrStream: BaseCodecName | AVCodecID | Stream, validate?: boolean): Codec | null;
287
339
  /**
288
340
  * Get the appropriate decoder codec for a given base codec name.
289
341
  *