node-av 6.2.0-beta.2 → 6.2.0-beta.20

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 (89) hide show
  1. package/README.md +13 -8
  2. package/dist/api/audio-frame-buffer.d.ts +73 -1
  3. package/dist/api/audio-frame-buffer.js +127 -1
  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 +15 -1
  12. package/dist/api/demuxer.js +165 -54
  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 +19 -0
  18. package/dist/api/encoder.js +112 -45
  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 +167 -3
  23. package/dist/api/fmp4-stream.js +423 -90
  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/io-stream.d.ts +3 -2
  29. package/dist/api/io-stream.js +61 -5
  30. package/dist/api/io-stream.js.map +1 -1
  31. package/dist/api/muxer.d.ts +14 -0
  32. package/dist/api/muxer.js +131 -51
  33. package/dist/api/muxer.js.map +1 -1
  34. package/dist/api/pipeline.d.ts +24 -0
  35. package/dist/api/pipeline.js +76 -5
  36. package/dist/api/pipeline.js.map +1 -1
  37. package/dist/api/rtp-stream.d.ts +56 -6
  38. package/dist/api/rtp-stream.js +252 -80
  39. package/dist/api/rtp-stream.js.map +1 -1
  40. package/dist/api/scaler.d.ts +105 -17
  41. package/dist/api/scaler.js +326 -108
  42. package/dist/api/scaler.js.map +1 -1
  43. package/dist/api/utilities/async-queue.d.ts +3 -0
  44. package/dist/api/utilities/async-queue.js +8 -0
  45. package/dist/api/utilities/async-queue.js.map +1 -1
  46. package/dist/api/webrtc-stream.d.ts +2 -0
  47. package/dist/api/webrtc-stream.js +46 -12
  48. package/dist/api/webrtc-stream.js.map +1 -1
  49. package/dist/api/whisper.d.ts +2 -0
  50. package/dist/api/whisper.js +63 -23
  51. package/dist/api/whisper.js.map +1 -1
  52. package/dist/ffmpeg/install.js +115 -29
  53. package/dist/ffmpeg/install.js.map +1 -1
  54. package/dist/ffmpeg/utils.js +7 -4
  55. package/dist/ffmpeg/utils.js.map +1 -1
  56. package/dist/lib/binding.d.ts +1 -0
  57. package/dist/lib/binding.js +12 -16
  58. package/dist/lib/binding.js.map +1 -1
  59. package/dist/lib/codec-context.d.ts +0 -2
  60. package/dist/lib/codec-context.js +4 -22
  61. package/dist/lib/codec-context.js.map +1 -1
  62. package/dist/lib/filter-context.d.ts +0 -1
  63. package/dist/lib/filter-context.js +2 -11
  64. package/dist/lib/filter-context.js.map +1 -1
  65. package/dist/lib/format-context.d.ts +63 -7
  66. package/dist/lib/format-context.js +70 -21
  67. package/dist/lib/format-context.js.map +1 -1
  68. package/dist/lib/frame.d.ts +11 -11
  69. package/dist/lib/frame.js +13 -20
  70. package/dist/lib/frame.js.map +1 -1
  71. package/dist/lib/hardware-frames-context.d.ts +0 -1
  72. package/dist/lib/hardware-frames-context.js +2 -8
  73. package/dist/lib/hardware-frames-context.js.map +1 -1
  74. package/dist/lib/io-context.d.ts +4 -2
  75. package/dist/lib/io-context.js +4 -2
  76. package/dist/lib/io-context.js.map +1 -1
  77. package/dist/lib/native-types.d.ts +4 -4
  78. package/dist/lib/option.d.ts +26 -0
  79. package/dist/lib/option.js +42 -18
  80. package/dist/lib/option.js.map +1 -1
  81. package/dist/lib/packet.d.ts +5 -0
  82. package/dist/lib/packet.js +5 -0
  83. package/dist/lib/packet.js.map +1 -1
  84. package/dist/lib/stream.d.ts +3 -4
  85. package/dist/lib/stream.js +17 -24
  86. package/dist/lib/stream.js.map +1 -1
  87. package/install/check.js +40 -79
  88. package/package.json +43 -32
  89. package/build_mac_local.sh +0 -69
@@ -10,9 +10,9 @@ export type MP4BoxType = 'ftyp' | 'styp' | 'moov' | 'moof' | 'mdat' | 'free' | '
10
10
  export interface MP4Box {
11
11
  /** Four-character code identifying the box type (e.g., 'ftyp', 'moov', 'moof') */
12
12
  type: MP4BoxType;
13
- /** Total size of the box in bytes (including 8-byte header) */
13
+ /** Total size of the box in bytes (including the 8-byte header, or 16-byte for 64-bit boxes) */
14
14
  size: number;
15
- /** Box payload data (excluding the 8-byte header) */
15
+ /** Box payload data (excluding the 8-byte header, or 16-byte for 64-bit boxes) */
16
16
  data: Buffer;
17
17
  /** Offset of this box in the original buffer */
18
18
  offset: number;
@@ -66,12 +66,53 @@ export interface FMP4StreamOptions {
66
66
  supportedCodecs?: string;
67
67
  /**
68
68
  * Fragment duration in microseconds.
69
+ *
70
+ * Passed to the mp4 muxer as `frag_duration`, which creates a new fragment
71
+ * whenever the current one exceeds this duration (in addition to any cuts
72
+ * requested via movFlags such as `frag_keyframe`).
69
73
  * Smaller values reduce latency but increase overhead.
70
74
  * Set to 1 to send data as soon as possible.
71
75
  *
72
76
  * @default 1
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const stream = FMP4Stream.create('rtsp://camera.local/stream', {
81
+ * supportedCodecs: 'avc1.640029,mp4a.40.2',
82
+ * fragDuration: 2_000_000, // 2 seconds in microseconds
83
+ * });
84
+ * ```
73
85
  */
74
86
  fragDuration?: number;
87
+ /**
88
+ * Maximum number of media fragments buffered for a {@link FMP4Stream.fragments}
89
+ * consumer before the oldest are dropped.
90
+ *
91
+ * Live sources (RTSP cameras) produce fragments at a fixed rate regardless of
92
+ * consumer speed. Without a bound, a slow or stalled consumer would grow the
93
+ * queue until the process runs out of memory. When the backlog exceeds this
94
+ * limit, the oldest media fragments are dropped (never the init segment,
95
+ * which is kept separately via {@link FMP4Stream.initSegment}) and
96
+ * {@link FMP4Stream.droppedFragments} is incremented.
97
+ *
98
+ * @default 16
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const stream = FMP4Stream.create('rtsp://camera.local/stream', {
103
+ * supportedCodecs: 'avc1.640029,mp4a.40.2',
104
+ * boxMode: true,
105
+ * maxQueuedFragments: 8,
106
+ * });
107
+ *
108
+ * await stream.start();
109
+ * for await (const fragment of stream.fragments()) {
110
+ * await sendToClient(fragment.data); // Slow consumer - oldest fragments dropped
111
+ * }
112
+ * console.log(`Dropped ${stream.droppedFragments} fragments`);
113
+ * ```
114
+ */
115
+ maxQueuedFragments?: number;
75
116
  /**
76
117
  * Hardware acceleration configuration for video transcoding.
77
118
  *
@@ -96,6 +137,7 @@ export interface FMP4StreamOptions {
96
137
  fps?: number;
97
138
  width?: number;
98
139
  height?: number;
140
+ bitrate?: number;
99
141
  encoderOptions?: EncoderOptions['options'];
100
142
  };
101
143
  /**
@@ -112,7 +154,10 @@ export interface FMP4StreamOptions {
112
154
  bufferSize?: number;
113
155
  /**
114
156
  * Enable box mode - buffers data until complete MP4 boxes are available.
115
- * When true, onData receives complete boxes with parsed box information.
157
+ * When true, onData receives complete boxes with parsed box information,
158
+ * and media fragments are emitted fragment-aligned: a callback that contains
159
+ * a `moof` always contains its `mdat` too, regardless of how the muxer's
160
+ * I/O buffer split the write (large fragments arrive in several chunks).
116
161
  * When false, onData receives raw chunks as they arrive from FFmpeg.
117
162
  *
118
163
  * @default false
@@ -204,9 +249,17 @@ export declare class FMP4Stream {
204
249
  private signal?;
205
250
  private supportedCodecs;
206
251
  private incompleteBoxBuffer;
252
+ private pendingFragment;
253
+ private abortHandler?;
254
+ private stopRequested;
255
+ private stopPromise?;
256
+ private startAbort?;
257
+ private stopSignal;
207
258
  private fragmentQueue;
259
+ private _droppedFragments;
208
260
  private _initSegment;
209
261
  private _initSegmentResolve;
262
+ private _initSegmentReject;
210
263
  private _initSegmentPromise;
211
264
  private _ftypData;
212
265
  private _moovData;
@@ -257,10 +310,32 @@ export declare class FMP4Stream {
257
310
  *
258
311
  * Only available when boxMode is enabled.
259
312
  * Resolves once the first ftyp and moov boxes have been received.
313
+ * Rejects if the stream fails or is stopped before the init segment
314
+ * was produced, so awaiting it never hangs.
260
315
  *
261
316
  * @throws {Error} If boxMode is not enabled
262
317
  */
263
318
  get initSegment(): Promise<Buffer>;
319
+ /**
320
+ * Number of media fragments dropped due to consumer backpressure.
321
+ *
322
+ * Incremented whenever the {@link fragments} backlog exceeds
323
+ * `maxQueuedFragments` and the oldest fragment is discarded.
324
+ * Reset to 0 when the stream is (re)started.
325
+ *
326
+ * @returns Total dropped fragments for the current run
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * for await (const fragment of stream.fragments()) {
331
+ * await sendToClient(fragment.data);
332
+ * if (stream.droppedFragments > 0) {
333
+ * console.warn(`Consumer too slow: ${stream.droppedFragments} fragments dropped`);
334
+ * }
335
+ * }
336
+ * ```
337
+ */
338
+ get droppedFragments(): number;
264
339
  /**
265
340
  * Get the demuxer instance.
266
341
  *
@@ -334,6 +409,35 @@ export declare class FMP4Stream {
334
409
  * @internal
335
410
  */
336
411
  private doStart;
412
+ /**
413
+ * Abort an in-flight startup once stop() was requested.
414
+ *
415
+ * Checked after every await in the startup path: the resources created so
416
+ * far are all assigned to instance fields, so the stop() that is waiting on
417
+ * this startup tears them down the moment it unwinds.
418
+ *
419
+ * @throws {Error} If stop() was called while starting
420
+ *
421
+ * @internal
422
+ */
423
+ private throwIfStopRequested;
424
+ /**
425
+ * Cap the video bitrate on the encoder context.
426
+ *
427
+ * Applies a VBV rate cap (maxrate + a 1s buffer) WITHOUT setting a target
428
+ * bitrate, so the encoder keeps its default constant-quality mode but can
429
+ * never exceed the negotiated ceiling. Setting the bitrate as an ABR target
430
+ * instead would push a normally-lighter stream UP to fill it, overshooting on
431
+ * bursts - the opposite of what strict rate-capped consumers want.
432
+ * No-op when no bitrate was requested.
433
+ *
434
+ * @param ctx - Encoder codec context
435
+ *
436
+ * @param bitrate - Maximum bitrate in bits per second, or undefined
437
+ *
438
+ * @internal
439
+ */
440
+ private applyBitrate;
337
441
  /**
338
442
  * Create the fMP4 output muxer with the fragment-emitting write callback.
339
443
  *
@@ -356,6 +460,16 @@ export declare class FMP4Stream {
356
460
  * @internal
357
461
  */
358
462
  private attachCompletion;
463
+ /**
464
+ * Whether a pipeline rejection was caused by an intentional shutdown.
465
+ *
466
+ * @param error - The pipeline rejection reason
467
+ *
468
+ * @returns True when stop() is in flight or the owner's signal aborted
469
+ *
470
+ * @internal
471
+ */
472
+ private isDeliberateStop;
359
473
  /**
360
474
  * Set up encoders and the fMP4 muxer for a pre-composited frame source and
361
475
  * start encoding. No demuxer or decoder is involved - the provided frames are
@@ -388,6 +502,12 @@ export declare class FMP4Stream {
388
502
  * ```
389
503
  */
390
504
  stop(): Promise<void>;
505
+ /**
506
+ * Perform the actual teardown (see {@link stop}).
507
+ *
508
+ * @internal
509
+ */
510
+ private doStop;
391
511
  /**
392
512
  * Async generator that yields media fragments (moof+mdat chunks).
393
513
  *
@@ -399,8 +519,16 @@ export declare class FMP4Stream {
399
519
  *
400
520
  * The generator completes when the stream stops or the pipeline ends.
401
521
  *
522
+ * Only a single consumer is supported: a second concurrent fragments() call
523
+ * would silently strand the first consumer, so it throws instead. The
524
+ * backlog is bounded by `maxQueuedFragments` - if the consumer falls behind
525
+ * a live source, the oldest fragments are dropped (see
526
+ * {@link droppedFragments}).
527
+ *
402
528
  * @yields {FMP4Fragment} Media fragment with data buffer and box info
403
529
  *
530
+ * @throws {Error} If another fragments() iterator is already active
531
+ *
404
532
  * @example
405
533
  * ```typescript
406
534
  * const stream = FMP4Stream.create('rtsp://camera/stream', {
@@ -446,11 +574,35 @@ export declare class FMP4Stream {
446
574
  /**
447
575
  * Process buffer in box mode - buffers until complete boxes are available.
448
576
  *
577
+ * The muxer splits writes at arbitrary positions, so a chunk may end
578
+ * anywhere - including inside a box header. Any trailing partial box
579
+ * (header or body) is buffered for the next chunk; the parser never
580
+ * loses bytes regardless of where the split falls.
581
+ *
582
+ * Emission is fragment-aligned: a `moof` is held back until its `mdat` has
583
+ * arrived, then both are emitted as ONE onData call. The avio buffer flushes
584
+ * at arbitrary points (a fragment larger than the buffer always splits), so
585
+ * without this a consumer would see a bare `moof` followed by a detached
586
+ * `mdat` - unplayable framing for fMP4 consumers.
587
+ * Non-fragment boxes (ftyp, moov, mfra, ...) are emitted as they complete.
588
+ *
449
589
  * @param chunk - Incoming data chunk from FFmpeg
450
590
  *
451
591
  * @internal
452
592
  */
453
593
  private processBoxMode;
594
+ /**
595
+ * Emit a group of complete boxes as one onData call.
596
+ *
597
+ * Concatenates the raw box bytes, rebuilds the box list with offsets
598
+ * relative to the emitted buffer, tracks the init segment, and feeds the
599
+ * fragments() queue when the group is a media fragment.
600
+ *
601
+ * @param entries - Complete boxes in stream order
602
+ *
603
+ * @internal
604
+ */
605
+ private emitBoxGroup;
454
606
  /**
455
607
  * Push a fragment to the fragment queue for the async generator.
456
608
  *
@@ -461,6 +613,18 @@ export declare class FMP4Stream {
461
613
  * @internal
462
614
  */
463
615
  private pushFragment;
616
+ /**
617
+ * Reject a pending initSegment promise so consumers never hang.
618
+ *
619
+ * No-op when the promise was never requested or has already settled. The
620
+ * promise is marked as handled to avoid an unhandled rejection when no
621
+ * consumer is currently awaiting it.
622
+ *
623
+ * @param error - Rejection reason (pipeline error or stop-before-init)
624
+ *
625
+ * @internal
626
+ */
627
+ private rejectInitSegment;
464
628
  /**
465
629
  * Signal the fragment queue that no more fragments will arrive.
466
630
  *