node-av 6.2.0-beta.3 → 6.2.0-beta.30

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 (101) hide show
  1. package/README.md +13 -8
  2. package/dist/api/audio-frame-buffer.d.ts +438 -7
  3. package/dist/api/audio-frame-buffer.js +792 -52
  4. package/dist/api/audio-frame-buffer.js.map +1 -1
  5. package/dist/api/bitstream-filter.d.ts +82 -4
  6. package/dist/api/bitstream-filter.js +197 -100
  7. package/dist/api/bitstream-filter.js.map +1 -1
  8. package/dist/api/decoder.d.ts +5 -0
  9. package/dist/api/decoder.js +13 -6
  10. package/dist/api/decoder.js.map +1 -1
  11. package/dist/api/demuxer.d.ts +29 -4
  12. package/dist/api/demuxer.js +209 -60
  13. package/dist/api/demuxer.js.map +1 -1
  14. package/dist/api/encoder-pool.d.ts +43 -1
  15. package/dist/api/encoder-pool.js +121 -9
  16. package/dist/api/encoder-pool.js.map +1 -1
  17. package/dist/api/encoder.d.ts +58 -2
  18. package/dist/api/encoder.js +173 -57
  19. package/dist/api/encoder.js.map +1 -1
  20. package/dist/api/filter.js +15 -6
  21. package/dist/api/filter.js.map +1 -1
  22. package/dist/api/fmp4-stream.d.ts +327 -8
  23. package/dist/api/fmp4-stream.js +663 -123
  24. package/dist/api/fmp4-stream.js.map +1 -1
  25. package/dist/api/hardware.d.ts +2 -0
  26. package/dist/api/hardware.js +25 -4
  27. package/dist/api/hardware.js.map +1 -1
  28. package/dist/api/index.d.ts +2 -2
  29. package/dist/api/index.js.map +1 -1
  30. package/dist/api/io-stream.d.ts +3 -2
  31. package/dist/api/io-stream.js +76 -5
  32. package/dist/api/io-stream.js.map +1 -1
  33. package/dist/api/muxer.d.ts +289 -7
  34. package/dist/api/muxer.js +696 -109
  35. package/dist/api/muxer.js.map +1 -1
  36. package/dist/api/pipeline.d.ts +24 -0
  37. package/dist/api/pipeline.js +76 -5
  38. package/dist/api/pipeline.js.map +1 -1
  39. package/dist/api/rtp-stream.d.ts +77 -8
  40. package/dist/api/rtp-stream.js +316 -88
  41. package/dist/api/rtp-stream.js.map +1 -1
  42. package/dist/api/scaler.d.ts +124 -21
  43. package/dist/api/scaler.js +347 -114
  44. package/dist/api/scaler.js.map +1 -1
  45. package/dist/api/utilities/async-queue.d.ts +3 -0
  46. package/dist/api/utilities/async-queue.js +8 -0
  47. package/dist/api/utilities/async-queue.js.map +1 -1
  48. package/dist/api/utilities/packet-age-watchdog.d.ts +198 -0
  49. package/dist/api/utilities/packet-age-watchdog.js +594 -0
  50. package/dist/api/utilities/packet-age-watchdog.js.map +1 -0
  51. package/dist/api/webrtc-stream.d.ts +2 -0
  52. package/dist/api/webrtc-stream.js +46 -12
  53. package/dist/api/webrtc-stream.js.map +1 -1
  54. package/dist/api/whisper.d.ts +2 -0
  55. package/dist/api/whisper.js +63 -23
  56. package/dist/api/whisper.js.map +1 -1
  57. package/dist/constants/constants.d.ts +1 -0
  58. package/dist/constants/constants.js +1 -0
  59. package/dist/constants/constants.js.map +1 -1
  60. package/dist/constants/filter-options.d.ts +2 -0
  61. package/dist/ffmpeg/install.js +104 -26
  62. package/dist/ffmpeg/install.js.map +1 -1
  63. package/dist/ffmpeg/utils.js +7 -4
  64. package/dist/ffmpeg/utils.js.map +1 -1
  65. package/dist/lib/binding.d.ts +10 -1
  66. package/dist/lib/binding.js +25 -16
  67. package/dist/lib/binding.js.map +1 -1
  68. package/dist/lib/bitstream-filter-context.d.ts +2 -0
  69. package/dist/lib/bitstream-filter-context.js +2 -0
  70. package/dist/lib/bitstream-filter-context.js.map +1 -1
  71. package/dist/lib/codec-context.d.ts +0 -2
  72. package/dist/lib/codec-context.js +4 -22
  73. package/dist/lib/codec-context.js.map +1 -1
  74. package/dist/lib/filter-context.d.ts +0 -1
  75. package/dist/lib/filter-context.js +2 -11
  76. package/dist/lib/filter-context.js.map +1 -1
  77. package/dist/lib/format-context.d.ts +70 -7
  78. package/dist/lib/format-context.js +77 -21
  79. package/dist/lib/format-context.js.map +1 -1
  80. package/dist/lib/frame.d.ts +17 -13
  81. package/dist/lib/frame.js +24 -23
  82. package/dist/lib/frame.js.map +1 -1
  83. package/dist/lib/hardware-frames-context.d.ts +0 -1
  84. package/dist/lib/hardware-frames-context.js +2 -8
  85. package/dist/lib/hardware-frames-context.js.map +1 -1
  86. package/dist/lib/io-context.d.ts +4 -2
  87. package/dist/lib/io-context.js +4 -2
  88. package/dist/lib/io-context.js.map +1 -1
  89. package/dist/lib/native-types.d.ts +7 -5
  90. package/dist/lib/option.d.ts +26 -0
  91. package/dist/lib/option.js +42 -18
  92. package/dist/lib/option.js.map +1 -1
  93. package/dist/lib/packet.d.ts +5 -0
  94. package/dist/lib/packet.js +5 -0
  95. package/dist/lib/packet.js.map +1 -1
  96. package/dist/lib/stream.d.ts +8 -4
  97. package/dist/lib/stream.js +22 -24
  98. package/dist/lib/stream.js.map +1 -1
  99. package/install/check.js +40 -79
  100. package/package.json +45 -34
  101. package/build_mac_local.sh +0 -69
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
12
12
  [![FFmpeg](https://img.shields.io/badge/FFmpeg-8.1.1-green.svg)](https://ffmpeg.org)
13
- [![Platform](https://img.shields.io/badge/platform-Windows%20(MSVC%20%7C%20MinGW)%20%7C%20macOS%20%7C%20Linux-lightgrey.svg)](https://github.com/seydx/node-av)
13
+ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey.svg)](https://github.com/seydx/node-av)
14
14
 
15
15
  Native Node.js bindings for FFmpeg with full TypeScript support - including type-safe options (autocomplete + validation) for every codec, format, filter, and bitstream filter, generated from FFmpeg's own metadata. Provides direct access to FFmpeg's C APIs through N-API. Includes both raw FFmpeg bindings for full control and higher-level abstractions. Automatic resource management via Disposable pattern, hardware acceleration support and prebuilt binaries for Windows, Linux, and macOS.
16
16
 
@@ -527,6 +527,13 @@ The key difference: Async methods don't block the Node.js event loop, allowing o
527
527
 
528
528
  NodeAV provides direct bindings to FFmpeg's C APIs, which work with raw memory pointers. The high-level API adds safety abstractions and automatic resource management, but incorrect usage can still cause crashes. Common issues include mismatched video dimensions, incompatible pixel formats, or improper frame buffer handling. The library validates parameters where possible, but can't guarantee complete memory safety without limiting functionality. When using the low-level API, pay attention to parameter consistency, resource cleanup, and format compatibility. Following the documented patterns helps avoid memory-related issues.
529
529
 
530
+ ## Worker Threads and the libuv Thread Pool
531
+
532
+ NodeAV works inside `worker_threads`, with two caveats:
533
+
534
+ - **Prefer graceful shutdown over `worker.terminate()`.** Terminating a worker while native calls are in flight is mitigated (pending completions are dropped safely instead of aborting), but a termination that lands exactly inside a running synchronous native call can still abort the process — a limitation of Node-API error handling during isolate teardown. Signal the worker to close its NodeAV resources and exit on its own. For test runners, process isolation (e.g. vitest `pool: 'forks'`) is the most robust choice.
535
+ - **Async operations share the libuv thread pool** (default: 4 threads, process-wide across all workers). A custom IO read callback that never settles parks one pool thread until it resolves or its input is closed or interrupted (a closing `Demuxer` or an exiting process or worker fails it with `AVERROR_EXIT`); enough of them stall every async NodeAV operation in the entire process. Make custom IO callbacks always settle (return `null` for EOF, throw on error), and raise `UV_THREADPOOL_SIZE` when running many parallel live inputs.
536
+
530
537
  ## Electron
531
538
 
532
539
  NodeAV fully supports Electron applications. The prebuilt binaries are ABI-compatible with Electron, so no native rebuild is required during packaging. Both the native bindings and the bundled FFmpeg CLI binaries work seamlessly within Electron's main process.
@@ -555,7 +562,7 @@ Prebuilt binaries are available for multiple platforms:
555
562
 
556
563
  - **macOS**: x64, ARM64
557
564
  - **Linux**: x64, ARM64
558
- - **Windows**: x64, ARM64 (automatic MSVC/MinGW selection)
565
+ - **Windows**: x64, ARM64
559
566
 
560
567
  ### Cross-Platform Packaging (CI/CD)
561
568
 
@@ -569,9 +576,7 @@ npm install --os=win32 --cpu=x64
569
576
  ```
570
577
 
571
578
  npm filters the platform-specific `optionalDependencies` by these flags, and the
572
- postinstall hook honors them for the bundled FFmpeg binary as well. Cross-compiled
573
- Windows installs default to the MSVC build; pass `--libc=mingw` to select the MinGW
574
- (jellyfin) build instead.
579
+ postinstall hook honors them for the bundled FFmpeg binary as well.
575
580
 
576
581
  ## Troubleshooting
577
582
 
@@ -635,17 +640,17 @@ For issues and questions, please use the GitHub issue tracker.
635
640
  <source
636
641
  media="(prefers-color-scheme: dark)"
637
642
  srcset="
638
- https://api.star-history.com/svg?repos=seydx/node-av&type=Date&theme=dark
643
+ https://star-history.dera.page/svg?repos=seydx/node-av&type=Date&theme=dark
639
644
  "
640
645
  />
641
646
  <source
642
647
  media="(prefers-color-scheme: light)"
643
648
  srcset="
644
- https://api.star-history.com/svg?repos=seydx/node-av&type=Date
649
+ https://star-history.dera.page/svg?repos=seydx/node-av&type=Date
645
650
  "
646
651
  />
647
652
  <img
648
653
  alt="Star History Chart"
649
- src="https://api.star-history.com/svg?repos=seydx/node-av&type=Date"
654
+ src="https://star-history.dera.page/svg?repos=seydx/node-av&type=Date"
650
655
  />
651
656
  </picture>
@@ -1,6 +1,47 @@
1
1
  import { Frame } from '../lib/frame.js';
2
2
  import type { AVSampleFormat } from '../constants/index.js';
3
3
  import type { ChannelLayout } from '../lib/types.js';
4
+ /**
5
+ * Options for {@link AudioFrameBuffer.create}.
6
+ */
7
+ export interface AudioFrameBufferOptions {
8
+ /**
9
+ * Where the timestamps of the output frames come from.
10
+ *
11
+ * With `'input'` the output keeps the timing of the pushed frames, as described
12
+ * for {@link AudioFrameBuffer}. With `'samples'` input timestamps are ignored and
13
+ * the output is counted from 0 by its samples, in the time base of the pushed
14
+ * frames; use it for input whose timestamps are unusable.
15
+ *
16
+ * @default 'input'
17
+ */
18
+ timestamps?: 'input' | 'samples';
19
+ /**
20
+ * Largest gap in seconds that is filled with silence.
21
+ *
22
+ * A larger gap restarts the output timeline at the new timestamp, so it shows up
23
+ * as a timestamp jump instead. Real-time outputs such as RTP are better served by
24
+ * `0`: a filled gap is only written once the input resumes and then goes out as a
25
+ * burst of late packets, while receivers handle a timestamp gap like packet loss.
26
+ * Gaps of up to one output frame are filled either way, because a restart pads the
27
+ * current output frame with silence to its end. The silence is generated while
28
+ * pulling and takes no memory, but a long fill comes out in one go once the input
29
+ * resumes.
30
+ *
31
+ * @default 1
32
+ */
33
+ maxGapFill?: number;
34
+ }
35
+ /**
36
+ * Check the options of an audio frame buffer.
37
+ *
38
+ * @param options - Options to check
39
+ *
40
+ * @throws {RangeError} If an option is out of range
41
+ *
42
+ * @internal
43
+ */
44
+ export declare function assertAudioFrameBufferOptions(options: AudioFrameBufferOptions): void;
4
45
  /**
5
46
  * Audio frame buffering utility for encoders with fixed frame size requirements.
6
47
  *
@@ -8,6 +49,16 @@ import type { ChannelLayout } from '../lib/types.js';
8
49
  * of samples (frame_size). This class buffers incoming frames and outputs frames
9
50
  * with exactly the required size.
10
51
  *
52
+ * Output frames keep the input timing: each carries the timestamp of its first
53
+ * sample, in the time base of the pushed frames, and counts on by samples while the
54
+ * input is continuous. Deviations of up to 20 ms count as continuous, and larger ones
55
+ * only once they persist over 0.2 s of input, so jitter and single frames with a wrong
56
+ * timestamp leave the audio untouched. A persistent gap of up to a second is then
57
+ * filled with silence and an overlap of up to a second is dropped, which keeps the
58
+ * audio contiguous and in sync; a larger jump restarts the timeline at the new
59
+ * timestamp on the next frame boundary. Frames without a timestamp or time base
60
+ * continue the previous one.
61
+ *
11
62
  * Uses FFmpeg's AVAudioFifo internally for efficient sample buffering.
12
63
  *
13
64
  * @example
@@ -29,20 +80,49 @@ import type { ChannelLayout } from '../lib/types.js';
29
80
  * }
30
81
  * }
31
82
  *
32
- * // Flush remaining samples
83
+ * // Flush remaining samples (final partial frame is padded with silence)
33
84
  * let outputFrame;
34
85
  * while ((outputFrame = await buffer.pull()) !== null) {
35
86
  * await encoder.encode(outputFrame);
36
87
  * outputFrame.free();
37
88
  * }
89
+ * const tail = await buffer.pullPartial();
90
+ * if (tail) {
91
+ * await encoder.encode(tail);
92
+ * tail.free();
93
+ * }
94
+ * ```
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * // Real-time output: input gaps become timestamp jumps instead of silence
99
+ * using buffer = AudioFrameBuffer.create(960, AV_SAMPLE_FMT_FLT, 48000, 'mono', 1, { maxGapFill: 0 });
38
100
  * ```
39
101
  */
40
102
  export declare class AudioFrameBuffer implements Disposable {
41
103
  private fifo;
42
104
  private frame;
43
105
  private frameSize;
44
- private nextPts;
45
- private firstFramePts;
106
+ private channels;
107
+ private sampleStride;
108
+ private staging;
109
+ private stagingPlanes;
110
+ private silenceValue;
111
+ private sampleTimeBase;
112
+ private timeBase;
113
+ private countsInSamples;
114
+ private followInput;
115
+ private tolerance;
116
+ private confirmSamples;
117
+ private maxFill;
118
+ private maxTrim;
119
+ private headIndex;
120
+ private tailIndex;
121
+ private segments;
122
+ private silence;
123
+ private timed;
124
+ private trimPending;
125
+ private deviation;
46
126
  /**
47
127
  * @param fifo - Underlying AudioFifo instance
48
128
  *
@@ -54,6 +134,8 @@ export declare class AudioFrameBuffer implements Disposable {
54
134
  *
55
135
  * @param channelLayout - Channel layout
56
136
  *
137
+ * @param options - Timing options
138
+ *
57
139
  * @internal
58
140
  */
59
141
  private constructor();
@@ -70,18 +152,24 @@ export declare class AudioFrameBuffer implements Disposable {
70
152
  *
71
153
  * @param channels - Number of audio channels
72
154
  *
155
+ * @param options - Timing options
156
+ *
73
157
  * @returns Configured audio frame buffer
74
158
  *
159
+ * @throws {RangeError} If an option is out of range
160
+ *
75
161
  * @example
76
162
  * ```typescript
77
163
  * // For Opus encoder at 48kHz with 20ms frames
78
164
  * const buffer = AudioFrameBuffer.create(960, AV_SAMPLE_FMT_FLT, 48000, 'mono', 1);
79
165
  * ```
80
166
  */
81
- static create(frameSize: number, sampleFormat: AVSampleFormat, sampleRate: number, channelLayout: ChannelLayout, channels: number): AudioFrameBuffer;
167
+ static create(frameSize: number, sampleFormat: AVSampleFormat, sampleRate: number, channelLayout: ChannelLayout, channels: number, options?: AudioFrameBufferOptions): AudioFrameBuffer;
82
168
  /**
83
169
  * Get number of samples currently in buffer.
84
170
  *
171
+ * Includes silence inserted for gaps in the input.
172
+ *
85
173
  * @returns Number of buffered samples
86
174
  *
87
175
  * @example
@@ -93,7 +181,7 @@ export declare class AudioFrameBuffer implements Disposable {
93
181
  /**
94
182
  * Check if a complete frame is available.
95
183
  *
96
- * Returns true if the FIFO contains at least frameSize samples.
184
+ * Returns true if the buffer holds at least frameSize samples.
97
185
  *
98
186
  * @returns True if a full frame can be pulled
99
187
  *
@@ -112,8 +200,23 @@ export declare class AudioFrameBuffer implements Disposable {
112
200
  * The frame's samples are added to the internal FIFO.
113
201
  * Call hasFrame() and pull() to retrieve fixed-size output frames.
114
202
  *
203
+ * The first frame with a valid time base fixes the time base of the output frames.
204
+ * When the frame timestamps deviate from the buffered samples by more than 20 ms
205
+ * over at least 0.2 s of input (and 3 frames), a gap is filled with silence (up to
206
+ * `maxGapFill`) and an overlap of up to 1 s is dropped from the input; a larger jump
207
+ * restarts the timeline at the new timestamp after padding the current output frame
208
+ * with silence. The frames that arrive until then still continue the previous
209
+ * timeline. Each such correction is a short dropout or cut in the audio: input whose
210
+ * timestamps drift against its sample count, such as a capture device stamped from
211
+ * the system clock, gets one per 20 ms of drift, which keeps it within about 20 ms
212
+ * of its timestamps.
213
+ *
115
214
  * @param frame - Audio frame to buffer
116
215
  *
216
+ * @throws {Error} If the frame is not an audio frame
217
+ *
218
+ * @throws {FFmpegError} If the samples cannot be written to the FIFO
219
+ *
117
220
  * @example
118
221
  * ```typescript
119
222
  * await buffer.push(filterFrame);
@@ -128,9 +231,14 @@ export declare class AudioFrameBuffer implements Disposable {
128
231
  *
129
232
  * The frame's samples are added to the internal FIFO.
130
233
  * Call hasFrame() and pullSync() to retrieve fixed-size output frames.
234
+ * Timestamps are handled as in push().
131
235
  *
132
236
  * @param frame - Audio frame to buffer
133
237
  *
238
+ * @throws {Error} If the frame is not an audio frame
239
+ *
240
+ * @throws {FFmpegError} If the samples cannot be written to the FIFO
241
+ *
134
242
  * @example
135
243
  * ```typescript
136
244
  * buffer.pushSync(filterFrame);
@@ -142,12 +250,15 @@ export declare class AudioFrameBuffer implements Disposable {
142
250
  /**
143
251
  * Pull a fixed-size audio frame from the buffer asynchronously.
144
252
  *
145
- * Reads exactly frameSize samples from the FIFO and returns a cloned Frame.
253
+ * Reads exactly frameSize samples from the FIFO and returns a cloned Frame
254
+ * stamped with the timestamp of its first sample.
146
255
  * Returns null if not enough samples are available.
147
256
  * Reuses internal frame buffer for efficiency (like Decoder does).
148
257
  *
149
258
  * @returns Audio frame with exactly frameSize samples, or null if insufficient samples
150
259
  *
260
+ * @throws {FFmpegError} If the FIFO cannot be read or the frame cannot be filled
261
+ *
151
262
  * @throws {Error} If frame cloning fails (out of memory)
152
263
  *
153
264
  * @example
@@ -165,12 +276,15 @@ export declare class AudioFrameBuffer implements Disposable {
165
276
  * Pull a fixed-size audio frame from the buffer synchronously.
166
277
  * Synchronous version of pull.
167
278
  *
168
- * Reads exactly frameSize samples from the FIFO and returns a cloned Frame.
279
+ * Reads exactly frameSize samples from the FIFO and returns a cloned Frame
280
+ * stamped with the timestamp of its first sample.
169
281
  * Returns null if not enough samples are available.
170
282
  * Reuses internal frame buffer for efficiency (like Decoder does).
171
283
  *
172
284
  * @returns Audio frame with exactly frameSize samples, or null if insufficient samples
173
285
  *
286
+ * @throws {FFmpegError} If the FIFO cannot be read or the frame cannot be filled
287
+ *
174
288
  * @throws {Error} If frame cloning fails (out of memory)
175
289
  *
176
290
  * @example
@@ -184,15 +298,332 @@ export declare class AudioFrameBuffer implements Disposable {
184
298
  * @see {@link pull} For async version
185
299
  */
186
300
  pullSync(): Frame | null;
301
+ /**
302
+ * Pull the final partial frame from the buffer asynchronously.
303
+ *
304
+ * Reads the remaining samples (fewer than frameSize) and pads the tail with
305
+ * silence so the returned frame still carries exactly frameSize samples.
306
+ * Intended for end-of-stream flushing only - without it the tail (up to
307
+ * frameSize - 1 samples) would be dropped. Returns null if the buffer is
308
+ * empty, or while a complete frame is still available (drain those with
309
+ * pull() first). The padding counts as audio, so frames pushed afterwards are
310
+ * timed after it.
311
+ *
312
+ * @returns Silence-padded audio frame with frameSize samples, or null if nothing to drain
313
+ *
314
+ * @throws {FFmpegError} If the FIFO cannot be read or the frame cannot be filled
315
+ *
316
+ * @throws {Error} If frame cloning fails (out of memory)
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * // At end of stream, after pull() has returned null
321
+ * using tail = await buffer.pullPartial();
322
+ * if (tail) {
323
+ * await encoder.encode(tail);
324
+ * }
325
+ * ```
326
+ *
327
+ * @see {@link pullPartialSync} For synchronous version
328
+ * @see {@link pull} For pulling complete frames
329
+ */
330
+ pullPartial(): Promise<Frame | null>;
331
+ /**
332
+ * Pull the final partial frame from the buffer synchronously.
333
+ * Synchronous version of pullPartial.
334
+ *
335
+ * Reads the remaining samples (fewer than frameSize) and pads the tail with
336
+ * silence so the returned frame still carries exactly frameSize samples.
337
+ * Intended for end-of-stream flushing only. Returns null if the buffer is
338
+ * empty, or while a complete frame is still available.
339
+ *
340
+ * @returns Silence-padded audio frame with frameSize samples, or null if nothing to drain
341
+ *
342
+ * @throws {FFmpegError} If the FIFO cannot be read or the frame cannot be filled
343
+ *
344
+ * @throws {Error} If frame cloning fails (out of memory)
345
+ *
346
+ * @example
347
+ * ```typescript
348
+ * // At end of stream, after pullSync() has returned null
349
+ * using tail = buffer.pullPartialSync();
350
+ * if (tail) {
351
+ * encoder.encodeSync(tail);
352
+ * }
353
+ * ```
354
+ *
355
+ * @see {@link pullPartial} For async version
356
+ * @see {@link pullSync} For pulling complete frames
357
+ */
358
+ pullPartialSync(): Frame | null;
187
359
  /**
188
360
  * Reset the buffer, discarding all buffered samples.
189
361
  *
362
+ * The next pushed frame starts a new timeline, and its time base becomes
363
+ * the output time base.
364
+ *
190
365
  * @example
191
366
  * ```typescript
192
367
  * buffer.reset();
193
368
  * ```
194
369
  */
195
370
  reset(): void;
371
+ /**
372
+ * Decide how a pushed frame continues the buffered samples.
373
+ *
374
+ * The only place that moves the write position: it records the silence before the
375
+ * frame and accounts for the samples the caller then writes, so the position always
376
+ * runs ahead of the buffered samples by exactly the writes still in flight.
377
+ *
378
+ * @param frame - Audio frame about to be written
379
+ *
380
+ * @param nbSamples - Number of samples in the frame
381
+ *
382
+ * @returns Samples to drop from the head of the frame
383
+ *
384
+ * @internal
385
+ */
386
+ private planPush;
387
+ /**
388
+ * Append silence at the write position.
389
+ *
390
+ * Silence only exists as a run of sample indices and is generated while pulling, so
391
+ * a long gap costs neither a buffer of its size nor a FIFO grown to hold it (an
392
+ * AVAudioFifo never shrinks again).
393
+ *
394
+ * @param samples - Number of silent samples
395
+ *
396
+ * @internal
397
+ */
398
+ private addSilence;
399
+ /**
400
+ * Anchor the timeline at the first frame that carries a timestamp.
401
+ *
402
+ * @param pts - Timestamp of the frame in the output time base
403
+ *
404
+ * @returns Silence to write before the frame
405
+ *
406
+ * @internal
407
+ */
408
+ private anchorInput;
409
+ /**
410
+ * Compare a frame's timestamp with the buffered samples and compensate a persistent deviation.
411
+ *
412
+ * Within the tolerance the frame counts on by samples. A larger deviation is only
413
+ * acted on once it has lasted over enough input in the same direction: jitter and
414
+ * frames with a wrong timestamp come and go, while gaps, overlaps and clock steps
415
+ * persist. The correction is the median deviation of that run, so jitter on top of
416
+ * a real step does not leak into it.
417
+ *
418
+ * @param pts - Timestamp of the frame in the output time base
419
+ *
420
+ * @param nbSamples - Number of samples in the frame
421
+ *
422
+ * @returns Silence to write before the frame
423
+ *
424
+ * @internal
425
+ */
426
+ private trackInput;
427
+ /**
428
+ * Apply a confirmed deviation to the timeline.
429
+ *
430
+ * A gap is filled with silence and an overlap is dropped from the input, which keeps
431
+ * the output contiguous and on its frame grid. Beyond their limits the timeline
432
+ * restarts at the frame's timestamp instead.
433
+ *
434
+ * @param offset - Deviation of the frame in the output time base
435
+ *
436
+ * @param expected - Timestamp the frame would have without the deviation
437
+ *
438
+ * @returns Silence to write before the frame
439
+ *
440
+ * @internal
441
+ */
442
+ private compensate;
443
+ /**
444
+ * Start a new segment at the next output frame boundary.
445
+ *
446
+ * Every output frame then lies within one segment, so its timestamp stays well
447
+ * defined; the current partial frame is padded with silence to get there.
448
+ *
449
+ * @param pts - Timestamp of the frame about to be written
450
+ *
451
+ * @returns Silence to write before the frame
452
+ *
453
+ * @internal
454
+ */
455
+ private restart;
456
+ /**
457
+ * Fix the output time base from the first pushed frame.
458
+ *
459
+ * Frames without a valid time base leave the output counting samples (1/sample_rate).
460
+ *
461
+ * @param frame - First pushed frame
462
+ *
463
+ * @returns Output time base
464
+ *
465
+ * @internal
466
+ */
467
+ private lockTimeBase;
468
+ /**
469
+ * Read a frame's timestamp in the output time base.
470
+ *
471
+ * @param frame - Pushed frame
472
+ *
473
+ * @returns Timestamp, or AV_NOPTS_VALUE if the frame has no timestamp or time base
474
+ *
475
+ * @internal
476
+ */
477
+ private inputPts;
478
+ /**
479
+ * Timestamp of the next sample to pull.
480
+ *
481
+ * Drops segments the head has reached the end of first, so the head lies in the first one.
482
+ *
483
+ * @returns Timestamp in the output time base
484
+ *
485
+ * @internal
486
+ */
487
+ private headPts;
488
+ /**
489
+ * Timestamp right after the last buffered sample.
490
+ *
491
+ * @returns Timestamp in the output time base
492
+ *
493
+ * @internal
494
+ */
495
+ private tailPts;
496
+ /**
497
+ * Treat the silence padding of a partial frame as written audio.
498
+ *
499
+ * @internal
500
+ */
501
+ private advanceTailToHead;
502
+ /**
503
+ * Realign the write position with the FIFO after a failed write.
504
+ *
505
+ * @internal
506
+ */
507
+ private resyncTail;
508
+ /**
509
+ * Number of samples that can be pulled: buffered samples plus the silence between them.
510
+ *
511
+ * Stops at the first silence run that lies behind a write still in flight.
512
+ *
513
+ * @returns Number of samples available from the read position
514
+ *
515
+ * @internal
516
+ */
517
+ private available;
518
+ /**
519
+ * Split the next samples to pull into FIFO reads and silence.
520
+ *
521
+ * @param count - Number of samples to pull
522
+ *
523
+ * @returns Parts in pull order
524
+ *
525
+ * @internal
526
+ */
527
+ private readRuns;
528
+ /**
529
+ * Read the next samples into the head of the staging buffer.
530
+ *
531
+ * @param count - Number of samples to read
532
+ *
533
+ * @throws {FFmpegError} If the FIFO cannot be read
534
+ *
535
+ * @internal
536
+ */
537
+ private readStaging;
538
+ /**
539
+ * Read the next samples into the head of the staging buffer synchronously.
540
+ * Synchronous version of readStaging.
541
+ *
542
+ * @param count - Number of samples to read
543
+ *
544
+ * @throws {FFmpegError} If the FIFO cannot be read
545
+ *
546
+ * @internal
547
+ */
548
+ private readStagingSync;
549
+ /**
550
+ * Staging planes starting at a sample offset.
551
+ *
552
+ * @param offset - First sample to write to
553
+ *
554
+ * @returns Plane buffers for a FIFO read
555
+ *
556
+ * @internal
557
+ */
558
+ private stagingAt;
559
+ /**
560
+ * Convert a sample count to output time base ticks.
561
+ *
562
+ * @param samples - Number of samples
563
+ *
564
+ * @returns Duration in the output time base
565
+ *
566
+ * @internal
567
+ */
568
+ private ticks;
569
+ /**
570
+ * Convert output time base ticks to a sample count.
571
+ *
572
+ * @param ticks - Duration in the output time base
573
+ *
574
+ * @returns Number of samples (rounded)
575
+ *
576
+ * @internal
577
+ */
578
+ private samples;
579
+ /**
580
+ * Plane buffers of a pushed frame, starting at a sample offset.
581
+ *
582
+ * @param frame - Pushed frame
583
+ *
584
+ * @param skip - Samples to leave out at the head
585
+ *
586
+ * @returns Plane buffers to write to the FIFO
587
+ *
588
+ * @internal
589
+ */
590
+ private inputPlanes;
591
+ /**
592
+ * Stamp the reusable frame, advance the read position by one frame and hand out a clone.
593
+ *
594
+ * @param pts - Timestamp of the frame's first sample
595
+ *
596
+ * @returns Cloned output frame
597
+ *
598
+ * @throws {Error} If frame cloning fails (out of memory)
599
+ *
600
+ * @internal
601
+ */
602
+ private emitFrame;
603
+ /**
604
+ * Fill the staged sample range [fromSample, toSample) with silence.
605
+ *
606
+ * Planar frames carry one channel per plane; packed frames interleave all
607
+ * channels in plane 0.
608
+ *
609
+ * @param fromSample - First sample index to silence
610
+ *
611
+ * @param toSample - Sample index after the last one to silence
612
+ *
613
+ * @internal
614
+ */
615
+ private fillSilence;
616
+ /**
617
+ * Copy the staged samples into the reusable frame.
618
+ *
619
+ * A frame pulled earlier may still share the frame's buffer, so the buffer is
620
+ * made writable first instead of overwriting the samples under that frame.
621
+ *
622
+ * @throws {FFmpegError} If the frame buffer cannot be made writable or filled
623
+ *
624
+ * @internal
625
+ */
626
+ private commitStaging;
196
627
  /**
197
628
  * Free the buffer and all resources.
198
629
  *