node-av-lgpl 0.0.0 → 7.1.0-beta.1
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/LICENSE.md +22 -0
- package/README.md +711 -0
- package/dist/api/audio-frame-buffer.d.ts +636 -0
- package/dist/api/audio-frame-buffer.js +1027 -0
- package/dist/api/audio-frame-buffer.js.map +1 -0
- package/dist/api/bitstream-filter.d.ts +938 -0
- package/dist/api/bitstream-filter.js +1427 -0
- package/dist/api/bitstream-filter.js.map +1 -0
- package/dist/api/constants.d.ts +44 -0
- package/dist/api/constants.js +45 -0
- package/dist/api/constants.js.map +1 -0
- package/dist/api/data/test_av1.ivf +0 -0
- package/dist/api/data/test_h264.h264 +0 -0
- package/dist/api/data/test_hevc.h265 +0 -0
- package/dist/api/data/test_mjpeg.mjpeg +0 -0
- package/dist/api/data/test_vp8.ivf +0 -0
- package/dist/api/data/test_vp9.ivf +0 -0
- package/dist/api/decoder.d.ts +1413 -0
- package/dist/api/decoder.js +2367 -0
- package/dist/api/decoder.js.map +1 -0
- package/dist/api/demuxer.d.ts +1295 -0
- package/dist/api/demuxer.js +2344 -0
- package/dist/api/demuxer.js.map +1 -0
- package/dist/api/device.d.ts +589 -0
- package/dist/api/device.js +964 -0
- package/dist/api/device.js.map +1 -0
- package/dist/api/encoder-pool.d.ts +262 -0
- package/dist/api/encoder-pool.js +397 -0
- package/dist/api/encoder-pool.js.map +1 -0
- package/dist/api/encoder.d.ts +1509 -0
- package/dist/api/encoder.js +2619 -0
- package/dist/api/encoder.js.map +1 -0
- package/dist/api/filter-complex.d.ts +828 -0
- package/dist/api/filter-complex.js +1661 -0
- package/dist/api/filter-complex.js.map +1 -0
- package/dist/api/filter-presets.d.ts +800 -0
- package/dist/api/filter-presets.js +1554 -0
- package/dist/api/filter-presets.js.map +1 -0
- package/dist/api/filter.d.ts +1339 -0
- package/dist/api/filter.js +2075 -0
- package/dist/api/filter.js.map +1 -0
- package/dist/api/fmp4-stream.d.ts +797 -0
- package/dist/api/fmp4-stream.js +1428 -0
- package/dist/api/fmp4-stream.js.map +1 -0
- package/dist/api/hardware.d.ts +677 -0
- package/dist/api/hardware.js +1300 -0
- package/dist/api/hardware.js.map +1 -0
- package/dist/api/index.d.ts +21 -0
- package/dist/api/index.js +38 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/io-stream.d.ts +308 -0
- package/dist/api/io-stream.js +358 -0
- package/dist/api/io-stream.js.map +1 -0
- package/dist/api/muxer.d.ts +1311 -0
- package/dist/api/muxer.js +2672 -0
- package/dist/api/muxer.js.map +1 -0
- package/dist/api/pipeline.d.ts +801 -0
- package/dist/api/pipeline.js +1344 -0
- package/dist/api/pipeline.js.map +1 -0
- package/dist/api/probe.d.ts +128 -0
- package/dist/api/probe.js +227 -0
- package/dist/api/probe.js.map +1 -0
- package/dist/api/rtp-stream.d.ts +497 -0
- package/dist/api/rtp-stream.js +1131 -0
- package/dist/api/rtp-stream.js.map +1 -0
- package/dist/api/scaler.d.ts +561 -0
- package/dist/api/scaler.js +918 -0
- package/dist/api/scaler.js.map +1 -0
- package/dist/api/types.d.ts +17 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/utilities/async-queue.d.ts +149 -0
- package/dist/api/utilities/async-queue.js +254 -0
- package/dist/api/utilities/async-queue.js.map +1 -0
- package/dist/api/utilities/audio-sample.d.ts +117 -0
- package/dist/api/utilities/audio-sample.js +112 -0
- package/dist/api/utilities/audio-sample.js.map +1 -0
- package/dist/api/utilities/audio-transcode.d.ts +20 -0
- package/dist/api/utilities/audio-transcode.js +27 -0
- package/dist/api/utilities/audio-transcode.js.map +1 -0
- package/dist/api/utilities/channel-layout.d.ts +76 -0
- package/dist/api/utilities/channel-layout.js +80 -0
- package/dist/api/utilities/channel-layout.js.map +1 -0
- package/dist/api/utilities/codec-format.d.ts +87 -0
- package/dist/api/utilities/codec-format.js +117 -0
- package/dist/api/utilities/codec-format.js.map +1 -0
- package/dist/api/utilities/context-options.d.ts +25 -0
- package/dist/api/utilities/context-options.js +23 -0
- package/dist/api/utilities/context-options.js.map +1 -0
- package/dist/api/utilities/electron-shared-texture.d.ts +368 -0
- package/dist/api/utilities/electron-shared-texture.js +540 -0
- package/dist/api/utilities/electron-shared-texture.js.map +1 -0
- package/dist/api/utilities/image.d.ts +207 -0
- package/dist/api/utilities/image.js +213 -0
- package/dist/api/utilities/image.js.map +1 -0
- package/dist/api/utilities/index.d.ts +14 -0
- package/dist/api/utilities/index.js +27 -0
- package/dist/api/utilities/index.js.map +1 -0
- package/dist/api/utilities/live-decoding.d.ts +15 -0
- package/dist/api/utilities/live-decoding.js +32 -0
- package/dist/api/utilities/live-decoding.js.map +1 -0
- package/dist/api/utilities/media-type.d.ts +49 -0
- package/dist/api/utilities/media-type.js +53 -0
- package/dist/api/utilities/media-type.js.map +1 -0
- package/dist/api/utilities/packet-age-watchdog.d.ts +198 -0
- package/dist/api/utilities/packet-age-watchdog.js +594 -0
- package/dist/api/utilities/packet-age-watchdog.js.map +1 -0
- package/dist/api/utilities/pixel-format.d.ts +89 -0
- package/dist/api/utilities/pixel-format.js +97 -0
- package/dist/api/utilities/pixel-format.js.map +1 -0
- package/dist/api/utilities/sample-format.d.ts +129 -0
- package/dist/api/utilities/sample-format.js +141 -0
- package/dist/api/utilities/sample-format.js.map +1 -0
- package/dist/api/utilities/scale-size.d.ts +51 -0
- package/dist/api/utilities/scale-size.js +55 -0
- package/dist/api/utilities/scale-size.js.map +1 -0
- package/dist/api/utilities/scheduler.d.ts +138 -0
- package/dist/api/utilities/scheduler.js +98 -0
- package/dist/api/utilities/scheduler.js.map +1 -0
- package/dist/api/utilities/streaming.d.ts +186 -0
- package/dist/api/utilities/streaming.js +309 -0
- package/dist/api/utilities/streaming.js.map +1 -0
- package/dist/api/utilities/timestamp.d.ts +193 -0
- package/dist/api/utilities/timestamp.js +206 -0
- package/dist/api/utilities/timestamp.js.map +1 -0
- package/dist/api/utilities/video-transcode.d.ts +218 -0
- package/dist/api/utilities/video-transcode.js +624 -0
- package/dist/api/utilities/video-transcode.js.map +1 -0
- package/dist/api/utilities/whisper-model.d.ts +330 -0
- package/dist/api/utilities/whisper-model.js +587 -0
- package/dist/api/utilities/whisper-model.js.map +1 -0
- package/dist/api/utils.d.ts +19 -0
- package/dist/api/utils.js +39 -0
- package/dist/api/utils.js.map +1 -0
- package/dist/api/webrtc-stream.d.ts +294 -0
- package/dist/api/webrtc-stream.js +546 -0
- package/dist/api/webrtc-stream.js.map +1 -0
- package/dist/api/whisper.d.ts +326 -0
- package/dist/api/whisper.js +402 -0
- package/dist/api/whisper.js.map +1 -0
- package/dist/constants/bsf-options.d.ts +364 -0
- package/dist/constants/bsf-options.js +7 -0
- package/dist/constants/bsf-options.js.map +1 -0
- package/dist/constants/channel-layouts.d.ts +53 -0
- package/dist/constants/channel-layouts.js +57 -0
- package/dist/constants/channel-layouts.js.map +1 -0
- package/dist/constants/constants.d.ts +2435 -0
- package/dist/constants/constants.js +1989 -0
- package/dist/constants/constants.js.map +1 -0
- package/dist/constants/decoders.d.ts +651 -0
- package/dist/constants/decoders.js +641 -0
- package/dist/constants/decoders.js.map +1 -0
- package/dist/constants/encoders.d.ts +314 -0
- package/dist/constants/encoders.js +309 -0
- package/dist/constants/encoders.js.map +1 -0
- package/dist/constants/filter-options.d.ts +12099 -0
- package/dist/constants/filter-options.js +7 -0
- package/dist/constants/filter-options.js.map +1 -0
- package/dist/constants/format-options.d.ts +4319 -0
- package/dist/constants/format-options.js +7 -0
- package/dist/constants/format-options.js.map +1 -0
- package/dist/constants/formats.d.ts +18 -0
- package/dist/constants/formats.js +7 -0
- package/dist/constants/formats.js.map +1 -0
- package/dist/constants/hardware.d.ts +26 -0
- package/dist/constants/hardware.js +27 -0
- package/dist/constants/hardware.js.map +1 -0
- package/dist/constants/index.d.ts +10 -0
- package/dist/constants/index.js +6 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/options.d.ts +6217 -0
- package/dist/constants/options.js +7 -0
- package/dist/constants/options.js.map +1 -0
- package/dist/ffmpeg/index.d.ts +101 -0
- package/dist/ffmpeg/index.js +161 -0
- package/dist/ffmpeg/index.js.map +1 -0
- package/dist/ffmpeg/version.d.ts +6 -0
- package/dist/ffmpeg/version.js +7 -0
- package/dist/ffmpeg/version.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audio-fifo.d.ts +399 -0
- package/dist/lib/audio-fifo.js +431 -0
- package/dist/lib/audio-fifo.js.map +1 -0
- package/dist/lib/binding.d.ts +242 -0
- package/dist/lib/binding.js +102 -0
- package/dist/lib/binding.js.map +1 -0
- package/dist/lib/bitstream-filter-context.d.ts +381 -0
- package/dist/lib/bitstream-filter-context.js +443 -0
- package/dist/lib/bitstream-filter-context.js.map +1 -0
- package/dist/lib/bitstream-filter.d.ts +140 -0
- package/dist/lib/bitstream-filter.js +154 -0
- package/dist/lib/bitstream-filter.js.map +1 -0
- package/dist/lib/codec-context.d.ts +1069 -0
- package/dist/lib/codec-context.js +1336 -0
- package/dist/lib/codec-context.js.map +1 -0
- package/dist/lib/codec-parameters.d.ts +664 -0
- package/dist/lib/codec-parameters.js +813 -0
- package/dist/lib/codec-parameters.js.map +1 -0
- package/dist/lib/codec-parser.d.ts +201 -0
- package/dist/lib/codec-parser.js +213 -0
- package/dist/lib/codec-parser.js.map +1 -0
- package/dist/lib/codec.d.ts +617 -0
- package/dist/lib/codec.js +746 -0
- package/dist/lib/codec.js.map +1 -0
- package/dist/lib/device.d.ts +294 -0
- package/dist/lib/device.js +326 -0
- package/dist/lib/device.js.map +1 -0
- package/dist/lib/dictionary.d.ts +333 -0
- package/dist/lib/dictionary.js +372 -0
- package/dist/lib/dictionary.js.map +1 -0
- package/dist/lib/error.d.ts +311 -0
- package/dist/lib/error.js +395 -0
- package/dist/lib/error.js.map +1 -0
- package/dist/lib/fifo.d.ts +416 -0
- package/dist/lib/fifo.js +453 -0
- package/dist/lib/fifo.js.map +1 -0
- package/dist/lib/filter-context.d.ts +714 -0
- package/dist/lib/filter-context.js +783 -0
- package/dist/lib/filter-context.js.map +1 -0
- package/dist/lib/filter-graph-segment.d.ts +161 -0
- package/dist/lib/filter-graph-segment.js +172 -0
- package/dist/lib/filter-graph-segment.js.map +1 -0
- package/dist/lib/filter-graph.d.ts +641 -0
- package/dist/lib/filter-graph.js +704 -0
- package/dist/lib/filter-graph.js.map +1 -0
- package/dist/lib/filter-inout.d.ts +202 -0
- package/dist/lib/filter-inout.js +261 -0
- package/dist/lib/filter-inout.js.map +1 -0
- package/dist/lib/filter.d.ts +243 -0
- package/dist/lib/filter.js +272 -0
- package/dist/lib/filter.js.map +1 -0
- package/dist/lib/format-context.d.ts +1335 -0
- package/dist/lib/format-context.js +1455 -0
- package/dist/lib/format-context.js.map +1 -0
- package/dist/lib/frame-utils.d.ts +116 -0
- package/dist/lib/frame-utils.js +98 -0
- package/dist/lib/frame-utils.js.map +1 -0
- package/dist/lib/frame.d.ts +1315 -0
- package/dist/lib/frame.js +1535 -0
- package/dist/lib/frame.js.map +1 -0
- package/dist/lib/hardware-device-context.d.ts +362 -0
- package/dist/lib/hardware-device-context.js +383 -0
- package/dist/lib/hardware-device-context.js.map +1 -0
- package/dist/lib/hardware-frames-context.d.ts +418 -0
- package/dist/lib/hardware-frames-context.js +471 -0
- package/dist/lib/hardware-frames-context.js.map +1 -0
- package/dist/lib/index.d.ts +35 -0
- package/dist/lib/index.js +60 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/input-format.d.ts +249 -0
- package/dist/lib/input-format.js +306 -0
- package/dist/lib/input-format.js.map +1 -0
- package/dist/lib/io-context.d.ts +698 -0
- package/dist/lib/io-context.js +771 -0
- package/dist/lib/io-context.js.map +1 -0
- package/dist/lib/log.d.ts +174 -0
- package/dist/lib/log.js +184 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/native-types.d.ts +1024 -0
- package/dist/lib/native-types.js +2 -0
- package/dist/lib/native-types.js.map +1 -0
- package/dist/lib/option.d.ts +953 -0
- package/dist/lib/option.js +1607 -0
- package/dist/lib/option.js.map +1 -0
- package/dist/lib/output-format.d.ts +222 -0
- package/dist/lib/output-format.js +259 -0
- package/dist/lib/output-format.js.map +1 -0
- package/dist/lib/packet.d.ts +521 -0
- package/dist/lib/packet.js +613 -0
- package/dist/lib/packet.js.map +1 -0
- package/dist/lib/rational.d.ts +251 -0
- package/dist/lib/rational.js +278 -0
- package/dist/lib/rational.js.map +1 -0
- package/dist/lib/software-resample-context.d.ts +552 -0
- package/dist/lib/software-resample-context.js +592 -0
- package/dist/lib/software-resample-context.js.map +1 -0
- package/dist/lib/software-scale-context.d.ts +344 -0
- package/dist/lib/software-scale-context.js +366 -0
- package/dist/lib/software-scale-context.js.map +1 -0
- package/dist/lib/stream.d.ts +383 -0
- package/dist/lib/stream.js +524 -0
- package/dist/lib/stream.js.map +1 -0
- package/dist/lib/sync-queue.d.ts +179 -0
- package/dist/lib/sync-queue.js +197 -0
- package/dist/lib/sync-queue.js.map +1 -0
- package/dist/lib/types.d.ts +34 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utilities.d.ts +1198 -0
- package/dist/lib/utilities.js +1302 -0
- package/dist/lib/utilities.js.map +1 -0
- package/dist/utils/electron.d.ts +49 -0
- package/dist/utils/electron.js +63 -0
- package/dist/utils/electron.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/libc.d.ts +11 -0
- package/dist/utils/libc.js +35 -0
- package/dist/utils/libc.js.map +1 -0
- package/dist/utils/variant.d.ts +26 -0
- package/dist/utils/variant.js +31 -0
- package/dist/utils/variant.js.map +1 -0
- package/dist/webrtc/index.d.ts +4 -0
- package/dist/webrtc/index.js +7 -0
- package/dist/webrtc/index.js.map +1 -0
- package/install/check.js +124 -0
- package/install/ffmpeg.js +66 -0
- package/package.json +165 -3
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
import { Demuxer } from './demuxer.js';
|
|
2
|
+
import type { AVHWDeviceType, FFHWDeviceType } from '../constants/index.js';
|
|
3
|
+
import type { DemuxerOptions } from './demuxer.js';
|
|
4
|
+
import type { EncoderOptions } from './encoder.js';
|
|
5
|
+
import type { MediaFrameSource } from './types.js';
|
|
6
|
+
export type MP4BoxType = 'ftyp' | 'styp' | 'moov' | 'moof' | 'mdat' | 'free' | 'skip' | 'sidx' | 'emsg' | 'mvhd' | 'trak' | 'mvex' | 'trex' | 'mehd' | 'mfhd' | 'traf' | 'tfhd' | 'tfdt' | 'trun' | 'sdtp' | 'tkhd' | 'mdia' | 'minf' | 'stbl' | 'meta' | 'udta' | (string & {});
|
|
7
|
+
/**
|
|
8
|
+
* MP4 box information.
|
|
9
|
+
*/
|
|
10
|
+
export interface MP4Box {
|
|
11
|
+
/** Four-character code identifying the box type (e.g., 'ftyp', 'moov', 'moof') */
|
|
12
|
+
type: MP4BoxType;
|
|
13
|
+
/** Total size of the box in bytes (including the 8-byte header, or 16-byte for 64-bit boxes) */
|
|
14
|
+
size: number;
|
|
15
|
+
/** Box payload data (excluding the 8-byte header, or 16-byte for 64-bit boxes) */
|
|
16
|
+
data: Buffer;
|
|
17
|
+
/** Offset of this box in the original buffer */
|
|
18
|
+
offset: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A fragment of fMP4 data yielded by {@link FMP4Stream.fragments}.
|
|
22
|
+
*/
|
|
23
|
+
export interface FMP4Fragment {
|
|
24
|
+
/** fMP4 data buffer */
|
|
25
|
+
data: Buffer;
|
|
26
|
+
/** Box information (complete boxes, parsed box details) */
|
|
27
|
+
info: FMP4Data;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* fMP4 data information provided in onData callback.
|
|
31
|
+
*/
|
|
32
|
+
export interface FMP4Data {
|
|
33
|
+
/**
|
|
34
|
+
* True if data contains complete MP4 boxes, false if raw chunks.
|
|
35
|
+
* When boxMode is enabled, this is always true.
|
|
36
|
+
*/
|
|
37
|
+
isComplete: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Parsed MP4 boxes (only available when isComplete is true).
|
|
40
|
+
* Array is empty when isComplete is false.
|
|
41
|
+
*/
|
|
42
|
+
boxes: MP4Box[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Options for configuring fMP4 streaming.
|
|
46
|
+
*/
|
|
47
|
+
export interface FMP4StreamOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Callback invoked for fMP4 data (chunks or complete boxes).
|
|
50
|
+
*
|
|
51
|
+
* @param data - fMP4 data information with buffer and box details
|
|
52
|
+
*/
|
|
53
|
+
onData?: (data: Buffer, info: FMP4Data) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Callback invoked when the stream is closed or encounters an error.
|
|
56
|
+
*
|
|
57
|
+
* Called once per started session. On an error the stream has already been
|
|
58
|
+
* stopped and its resources (including the input connection) released.
|
|
59
|
+
* Output the muxer still held at the error is discarded, so no `onData` call
|
|
60
|
+
* follows the failure. Exceptions thrown by the callback are logged and do
|
|
61
|
+
* not propagate.
|
|
62
|
+
*
|
|
63
|
+
* @param error - Optional error if stream closed due to an error
|
|
64
|
+
*/
|
|
65
|
+
onClose?: (error?: Error) => void;
|
|
66
|
+
/**
|
|
67
|
+
* Supported codec strings from client.
|
|
68
|
+
* Comma-separated list (e.g., "avc1.640029,hvc1.1.6.L153.B0,mp4a.40.2,flac,opus").
|
|
69
|
+
*
|
|
70
|
+
* @example "avc1.640029,mp4a.40.2"
|
|
71
|
+
*/
|
|
72
|
+
supportedCodecs?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Fragment duration in microseconds.
|
|
75
|
+
*
|
|
76
|
+
* Passed to the mp4 muxer as `frag_duration`, which creates a new fragment
|
|
77
|
+
* whenever the current one exceeds this duration (in addition to any cuts
|
|
78
|
+
* requested via movFlags such as `frag_keyframe`).
|
|
79
|
+
* Smaller values reduce latency but increase overhead.
|
|
80
|
+
* Set to 1 to send data as soon as possible.
|
|
81
|
+
* Transcoded video gets a GOP of the fragment duration from 0.5 s on, so
|
|
82
|
+
* every fragment starts with a keyframe; below that, and with 0, it gets a
|
|
83
|
+
* 2 s GOP.
|
|
84
|
+
*
|
|
85
|
+
* @default 1
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const stream = FMP4Stream.create('rtsp://camera.local/stream', {
|
|
90
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2',
|
|
91
|
+
* fragDuration: 2_000_000, // 2 seconds in microseconds
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
fragDuration?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Maximum number of media fragments buffered for a {@link FMP4Stream.fragments}
|
|
98
|
+
* consumer before the oldest are dropped.
|
|
99
|
+
*
|
|
100
|
+
* Live sources (RTSP cameras) produce fragments at a fixed rate regardless of
|
|
101
|
+
* consumer speed. Without a bound, a slow or stalled consumer would grow the
|
|
102
|
+
* queue until the process runs out of memory. When the backlog exceeds this
|
|
103
|
+
* limit, the oldest media fragments are dropped (never the init segment,
|
|
104
|
+
* which is kept separately via {@link FMP4Stream.initSegment}) and
|
|
105
|
+
* {@link FMP4Stream.droppedFragments} is incremented.
|
|
106
|
+
*
|
|
107
|
+
* @default 16
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const stream = FMP4Stream.create('rtsp://camera.local/stream', {
|
|
112
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2',
|
|
113
|
+
* boxMode: true,
|
|
114
|
+
* maxQueuedFragments: 8,
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* await stream.start();
|
|
118
|
+
* for await (const fragment of stream.fragments()) {
|
|
119
|
+
* await sendToClient(fragment.data); // Slow consumer - oldest fragments dropped
|
|
120
|
+
* }
|
|
121
|
+
* console.log(`Dropped ${stream.droppedFragments} fragments`);
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
maxQueuedFragments?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Hardware acceleration configuration for video transcoding.
|
|
127
|
+
*
|
|
128
|
+
* - `'auto'` - Automatically detect and use available hardware acceleration
|
|
129
|
+
* - Object with deviceType - Manually specify hardware acceleration type
|
|
130
|
+
*
|
|
131
|
+
* @default { deviceType: AV_HWDEVICE_TYPE_NONE }
|
|
132
|
+
*/
|
|
133
|
+
hardware?: 'auto' | {
|
|
134
|
+
deviceType: AVHWDeviceType | FFHWDeviceType;
|
|
135
|
+
device?: string;
|
|
136
|
+
options?: Record<string, string>;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Input media options passed to Demuxer.
|
|
140
|
+
*/
|
|
141
|
+
inputOptions?: DemuxerOptions;
|
|
142
|
+
/**
|
|
143
|
+
* Video stream configuration.
|
|
144
|
+
*/
|
|
145
|
+
video?: {
|
|
146
|
+
fps?: number;
|
|
147
|
+
width?: number;
|
|
148
|
+
height?: number;
|
|
149
|
+
bitrate?: number;
|
|
150
|
+
/**
|
|
151
|
+
* Transcode to H.264 even when the source codec is one of `supportedCodecs`, so `width`, `height`, `fps` and `bitrate` apply.
|
|
152
|
+
*/
|
|
153
|
+
transcode?: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* FourCC sample entry tag for the HEVC video stream, e.g. `hvc1`. Only applied to HEVC output.
|
|
156
|
+
*/
|
|
157
|
+
tag?: string;
|
|
158
|
+
encoderOptions?: EncoderOptions['options'];
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Audio stream configuration.
|
|
162
|
+
*/
|
|
163
|
+
audio?: {
|
|
164
|
+
encoderOptions?: EncoderOptions['options'];
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Buffer size for I/O operations in bytes (output).
|
|
168
|
+
*
|
|
169
|
+
* @default 2 MB
|
|
170
|
+
*/
|
|
171
|
+
bufferSize?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Enable box mode - buffers data until complete MP4 boxes are available.
|
|
174
|
+
* When true, onData receives complete boxes with parsed box information,
|
|
175
|
+
* and media fragments are emitted fragment-aligned: a callback that contains
|
|
176
|
+
* a `moof` always contains its `mdat` too, regardless of how the muxer's
|
|
177
|
+
* I/O buffer split the write (large fragments arrive in several chunks).
|
|
178
|
+
* When false, onData receives raw chunks as they arrive from FFmpeg.
|
|
179
|
+
*
|
|
180
|
+
* @default false
|
|
181
|
+
*/
|
|
182
|
+
boxMode?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* MOV flags for fragmented MP4 output.
|
|
185
|
+
*
|
|
186
|
+
* @default '+frag_keyframe+separate_moof+default_base_moof+empty_moov'
|
|
187
|
+
*/
|
|
188
|
+
movFlags?: string;
|
|
189
|
+
/**
|
|
190
|
+
* Backward DTS jump in seconds above which the stream ends with an error.
|
|
191
|
+
*
|
|
192
|
+
* A live source that restarts its clock (a camera reboot behind a restreamer)
|
|
193
|
+
* would otherwise leave every following packet clamped to one tick past the
|
|
194
|
+
* previous one, so fragments carry no media for as long as the jump. The stream
|
|
195
|
+
* instead stops and reports the error through `onClose(error)`, and the owner
|
|
196
|
+
* can restart it on a fresh timeline. Smaller jumps are clamped. `0` disables
|
|
197
|
+
* the check. Same semantics as the `Muxer` option of the same name.
|
|
198
|
+
*
|
|
199
|
+
* @default 10
|
|
200
|
+
*/
|
|
201
|
+
dtsBackwardThreshold?: number;
|
|
202
|
+
/**
|
|
203
|
+
* Forward DTS jump in seconds, beyond the real time that passed, above which
|
|
204
|
+
* the stream ends with an error.
|
|
205
|
+
*
|
|
206
|
+
* A live source that restarts its clock (a camera reboot behind a
|
|
207
|
+
* restreamer) can continue with timestamps far ahead of the time that
|
|
208
|
+
* passed. Muxing that would leave a sample spanning the jump, or, when the
|
|
209
|
+
* timeline later returns, a stream clamped behind it. The stream instead
|
|
210
|
+
* stops and reports the error through `onClose(error)`, and the owner can
|
|
211
|
+
* restart it on a fresh timeline. Gaps in the source come with a matching
|
|
212
|
+
* wait and pass. Only meaningful for inputs read in real time, so it is off
|
|
213
|
+
* for files, a pre-opened Demuxer and frame sources unless set; pass it for
|
|
214
|
+
* live inputs read over HTTP (HLS, HTTP-FLV) as well. Same semantics as the
|
|
215
|
+
* `Muxer` option of the same name; `0` disables the check.
|
|
216
|
+
*
|
|
217
|
+
* @default 10 for rtsp, rtsps, rtmp (and rtmps, rtmpt, rtmpe, rtmpte, rtmpts), rtp, srtp, udp, srt, tcp, tls and sctp URLs, 0 otherwise
|
|
218
|
+
*/
|
|
219
|
+
dtsForwardThreshold?: number;
|
|
220
|
+
/**
|
|
221
|
+
* Longest time in seconds a packet may wait inside the muxer before it is
|
|
222
|
+
* emitted in a fragment.
|
|
223
|
+
*
|
|
224
|
+
* The mp4 muxer holds packets back to interleave the streams by timestamp.
|
|
225
|
+
* When a stream's timestamps stop advancing or run far ahead of a silent
|
|
226
|
+
* stream (a camera restarting its clock behind a restreamer, missing
|
|
227
|
+
* timestamps), packets can pile up without ever being emitted while memory
|
|
228
|
+
* grows. The age is measured per stream in real time while the input
|
|
229
|
+
* delivers packets and is independent of the timestamps; once the oldest
|
|
230
|
+
* pending packet of any stream is older than this, the stream stops and
|
|
231
|
+
* reports the error through `onClose(error)`. A pause of the whole input
|
|
232
|
+
* counts at most 2 s, so a source that goes quiet and resumes does not end
|
|
233
|
+
* the session. Healthy sessions stay below the default: up to the fragment
|
|
234
|
+
* duration (the GOP length with `fragDuration: 0`) plus about 10 s of
|
|
235
|
+
* interleaving delay while a stream is silent. Inputs read faster than real
|
|
236
|
+
* time can pile up correspondingly more media before it fires, inputs that
|
|
237
|
+
* deliver packets more than 2 s apart take correspondingly longer. `0`
|
|
238
|
+
* disables the check.
|
|
239
|
+
*
|
|
240
|
+
* @default 60
|
|
241
|
+
*/
|
|
242
|
+
maxPacketAge?: number;
|
|
243
|
+
/**
|
|
244
|
+
* AbortSignal for cancellation.
|
|
245
|
+
*
|
|
246
|
+
* When aborted, the stream stops gracefully, equivalent to calling stop().
|
|
247
|
+
* If already aborted when start() is called, throws AbortError.
|
|
248
|
+
*/
|
|
249
|
+
signal?: AbortSignal;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Video description of the muxed fMP4 output.
|
|
253
|
+
*/
|
|
254
|
+
export interface FMP4VideoInfo {
|
|
255
|
+
/** Output codec family. */
|
|
256
|
+
codec: 'h264' | 'hevc' | 'av1' | 'unknown';
|
|
257
|
+
/** RFC 6381 codec string of the output (e.g. `hvc1.1.6.L120.B0`). */
|
|
258
|
+
codecString: string;
|
|
259
|
+
width: number;
|
|
260
|
+
height: number;
|
|
261
|
+
/** Frames per second, 0 when unknown. */
|
|
262
|
+
fps: number;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Target codec strings for fMP4 streaming.
|
|
266
|
+
*/
|
|
267
|
+
export declare const FMP4_CODECS: {
|
|
268
|
+
readonly H264: "avc1.640029";
|
|
269
|
+
readonly H265: "hvc1.1.6.L153.B0";
|
|
270
|
+
readonly AV1: "av01.0.00M.08";
|
|
271
|
+
readonly AAC: "mp4a.40.2";
|
|
272
|
+
readonly FLAC: "flac";
|
|
273
|
+
readonly OPUS: "opus";
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* High-level fMP4 streaming with automatic codec detection and transcoding.
|
|
277
|
+
*
|
|
278
|
+
* Provides fragmented MP4 streaming for clients.
|
|
279
|
+
* Automatically transcodes video to H.264 and audio to AAC (Opus when the client
|
|
280
|
+
* lists Opus but not AAC) if not supported by client.
|
|
281
|
+
* Client sends supported codecs, server transcodes accordingly.
|
|
282
|
+
* Essential component for building adaptive streaming servers.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* import { FMP4Stream } from 'node-av/api';
|
|
287
|
+
*
|
|
288
|
+
* // Client sends supported codecs
|
|
289
|
+
* const supportedCodecs = 'avc1.640029,hvc1.1.6.L153.B0,mp4a.40.2,flac';
|
|
290
|
+
*
|
|
291
|
+
* // Create stream with codec negotiation
|
|
292
|
+
* const stream = FMP4Stream.create('rtsp://camera.local/stream', {
|
|
293
|
+
* supportedCodecs,
|
|
294
|
+
* onData: (data) => ws.send(data.data)
|
|
295
|
+
* });
|
|
296
|
+
*
|
|
297
|
+
* // Start streaming (auto-transcodes if needed)
|
|
298
|
+
* await stream.start();
|
|
299
|
+
*
|
|
300
|
+
* // Stop when done
|
|
301
|
+
* await stream.stop();
|
|
302
|
+
* ```
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* // Stream with hardware acceleration
|
|
307
|
+
* const stream = FMP4Stream.create('input.mp4', {
|
|
308
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2',
|
|
309
|
+
* hardware: 'auto',
|
|
310
|
+
* fragDuration: 1,
|
|
311
|
+
* onData: (data) => sendToClient(data.data)
|
|
312
|
+
* });
|
|
313
|
+
*
|
|
314
|
+
* await stream.start();
|
|
315
|
+
* await stream.stop();
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
export declare class FMP4Stream {
|
|
319
|
+
private options;
|
|
320
|
+
private inputUrl;
|
|
321
|
+
private inputOptions;
|
|
322
|
+
private liveInput;
|
|
323
|
+
private input?;
|
|
324
|
+
private source?;
|
|
325
|
+
private output?;
|
|
326
|
+
private hardwareContext?;
|
|
327
|
+
private videoDecoder?;
|
|
328
|
+
private videoFilter?;
|
|
329
|
+
private videoEncoder?;
|
|
330
|
+
private audioDecoder?;
|
|
331
|
+
private audioFilter?;
|
|
332
|
+
private audioEncoder?;
|
|
333
|
+
private pipeline?;
|
|
334
|
+
private startPromise?;
|
|
335
|
+
private signal?;
|
|
336
|
+
private supportedCodecs;
|
|
337
|
+
private incompleteBoxBuffer;
|
|
338
|
+
private pendingFragment;
|
|
339
|
+
private abortHandler?;
|
|
340
|
+
private stopRequested;
|
|
341
|
+
private discardOutput;
|
|
342
|
+
private stopPromise?;
|
|
343
|
+
private startAbort?;
|
|
344
|
+
private stopSignal;
|
|
345
|
+
private fragmentQueue;
|
|
346
|
+
private packetAgeWatchdog?;
|
|
347
|
+
private _droppedFragments;
|
|
348
|
+
private _initSegment;
|
|
349
|
+
private _initSegmentResolve;
|
|
350
|
+
private _initSegmentReject;
|
|
351
|
+
private _initSegmentPromise;
|
|
352
|
+
private _ftypData;
|
|
353
|
+
private _moovData;
|
|
354
|
+
/**
|
|
355
|
+
* @param input - Media input URL or pre-opened Demuxer
|
|
356
|
+
*
|
|
357
|
+
* @param options - Stream configuration options
|
|
358
|
+
*
|
|
359
|
+
* Use {@link create} factory method
|
|
360
|
+
*
|
|
361
|
+
* @throws {RangeError} If dtsBackwardThreshold, dtsForwardThreshold or maxPacketAge is not a finite number >= 0
|
|
362
|
+
*
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
private constructor();
|
|
366
|
+
/**
|
|
367
|
+
* Create a fMP4 stream from a media source.
|
|
368
|
+
*
|
|
369
|
+
* Configures the stream with input URL and options. The input is not opened
|
|
370
|
+
* until start() is called, allowing the stream to be reused after stop().
|
|
371
|
+
*
|
|
372
|
+
* @param input - Media source URL (RTSP, file path, HTTP, etc.) or a pre-opened {@link Demuxer}
|
|
373
|
+
*
|
|
374
|
+
* @param options - Stream configuration options with supported codecs
|
|
375
|
+
*
|
|
376
|
+
* @returns Configured fMP4 stream instance
|
|
377
|
+
*
|
|
378
|
+
* @throws {RangeError} If dtsBackwardThreshold, dtsForwardThreshold or maxPacketAge is not a finite number >= 0
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```typescript
|
|
382
|
+
* // Stream from file with codec negotiation
|
|
383
|
+
* const stream = FMP4Stream.create('video.mp4', {
|
|
384
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2',
|
|
385
|
+
* onData: (data) => ws.send(data.data)
|
|
386
|
+
* });
|
|
387
|
+
* ```
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```typescript
|
|
391
|
+
* // Stream from RTSP with auto hardware acceleration
|
|
392
|
+
* const stream = FMP4Stream.create('rtsp://camera.local/stream', {
|
|
393
|
+
* supportedCodecs: 'avc1.640029,hvc1.1.6.L153.B0,mp4a.40.2',
|
|
394
|
+
* hardware: 'auto',
|
|
395
|
+
* fragDuration: 0.5
|
|
396
|
+
* });
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
static create(input: string | Demuxer | MediaFrameSource, options?: FMP4StreamOptions): FMP4Stream;
|
|
400
|
+
/**
|
|
401
|
+
* Promise that resolves with the init segment (ftyp+moov).
|
|
402
|
+
*
|
|
403
|
+
* Only available when boxMode is enabled.
|
|
404
|
+
* Resolves once the first ftyp and moov boxes have been received.
|
|
405
|
+
* Rejects if the stream fails or is stopped before the init segment
|
|
406
|
+
* was produced, so awaiting it never hangs.
|
|
407
|
+
*
|
|
408
|
+
* @throws {Error} If boxMode is not enabled
|
|
409
|
+
*/
|
|
410
|
+
get initSegment(): Promise<Buffer>;
|
|
411
|
+
/**
|
|
412
|
+
* Number of media fragments dropped due to consumer backpressure.
|
|
413
|
+
*
|
|
414
|
+
* Incremented whenever the {@link fragments} backlog exceeds
|
|
415
|
+
* `maxQueuedFragments` and the oldest fragment is discarded.
|
|
416
|
+
* Reset to 0 when the stream is (re)started.
|
|
417
|
+
*
|
|
418
|
+
* @returns Total dropped fragments for the current run
|
|
419
|
+
*
|
|
420
|
+
* @example
|
|
421
|
+
* ```typescript
|
|
422
|
+
* for await (const fragment of stream.fragments()) {
|
|
423
|
+
* await sendToClient(fragment.data);
|
|
424
|
+
* if (stream.droppedFragments > 0) {
|
|
425
|
+
* console.warn(`Consumer too slow: ${stream.droppedFragments} fragments dropped`);
|
|
426
|
+
* }
|
|
427
|
+
* }
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
get droppedFragments(): number;
|
|
431
|
+
/**
|
|
432
|
+
* Get the demuxer instance.
|
|
433
|
+
*
|
|
434
|
+
* Used for accessing the underlying demuxer.
|
|
435
|
+
* Only available after start() is called.
|
|
436
|
+
*
|
|
437
|
+
* @returns Demuxer instance or undefined if not started
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* ```typescript
|
|
441
|
+
* const stream = FMP4Stream.create('input.mp4', {
|
|
442
|
+
* const input = stream.getInput();
|
|
443
|
+
* console.log('Bitrate:', input?.bitRate);
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
getInput(): Demuxer | undefined;
|
|
447
|
+
/**
|
|
448
|
+
* Get the codec string that will be used by client.
|
|
449
|
+
*
|
|
450
|
+
* Returns the MIME type codec string based on input codecs and transcoding decisions.
|
|
451
|
+
* Call this after start() is called to know what codec string to use for addSourceBuffer().
|
|
452
|
+
*
|
|
453
|
+
* @returns MIME type codec string (e.g., "avc1.640029,mp4a.40.2")
|
|
454
|
+
*
|
|
455
|
+
* @throws {Error} If called before start() is called
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* const stream = await FMP4Stream.create('input.mp4', {
|
|
460
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2'
|
|
461
|
+
* });
|
|
462
|
+
*
|
|
463
|
+
* await stream.start(); // Must start first
|
|
464
|
+
* const codecString = stream.getCodecString();
|
|
465
|
+
* console.log(codecString); // "avc1.640029,mp4a.40.2"
|
|
466
|
+
* // Use this for: sourceBuffer = mediaSource.addSourceBuffer(`video/mp4; codecs="${codecString}"`);
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
getCodecString(): string;
|
|
470
|
+
/**
|
|
471
|
+
* Video description of the muxed fMP4 output (output codec, RFC 6381 codec string, resolution, fps).
|
|
472
|
+
*
|
|
473
|
+
* Available after {@link start} has opened the input.
|
|
474
|
+
*
|
|
475
|
+
* @returns The output video info, or `undefined` when there is no video track.
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* ```typescript
|
|
479
|
+
* const videoInfo = stream.getVideoInfo();
|
|
480
|
+
* if (videoInfo) {
|
|
481
|
+
* console.log(`Codec: ${videoInfo.codec}, Resolution: ${videoInfo.width}x${videoInfo.height}, FPS: ${videoInfo.fps}`);
|
|
482
|
+
* }
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
getVideoInfo(): FMP4VideoInfo | undefined;
|
|
486
|
+
/**
|
|
487
|
+
* Start streaming media to fMP4 chunks.
|
|
488
|
+
*
|
|
489
|
+
* Begins the media processing pipeline, reading packets from input,
|
|
490
|
+
* transcoding based on supported codecs, and generating fMP4 chunks.
|
|
491
|
+
* Video transcodes to H.264 if H.264/H.265 not supported.
|
|
492
|
+
* Audio transcodes to AAC (Opus when only Opus is listed) if the input codec is not supported.
|
|
493
|
+
* This method returns immediately after starting the pipeline.
|
|
494
|
+
*
|
|
495
|
+
* @returns Promise that resolves when pipeline is started
|
|
496
|
+
*
|
|
497
|
+
* @throws {FFmpegError} If setup fails
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* const stream = await FMP4Stream.create('input.mp4', {
|
|
502
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2',
|
|
503
|
+
* onData: (data) => sendToClient(data.data)
|
|
504
|
+
* });
|
|
505
|
+
*
|
|
506
|
+
* // Start streaming (returns immediately)
|
|
507
|
+
* await stream.start();
|
|
508
|
+
*
|
|
509
|
+
* // Later: stop streaming
|
|
510
|
+
* await stream.stop();
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
start(): Promise<void>;
|
|
514
|
+
/**
|
|
515
|
+
* Perform the actual startup (see {@link start}).
|
|
516
|
+
*
|
|
517
|
+
* @internal
|
|
518
|
+
*/
|
|
519
|
+
private doStart;
|
|
520
|
+
/**
|
|
521
|
+
* Abort an in-flight startup once stop() was requested.
|
|
522
|
+
*
|
|
523
|
+
* Checked after every await in the startup path: the resources created so
|
|
524
|
+
* far are all assigned to instance fields, so the stop() that is waiting on
|
|
525
|
+
* this startup tears them down the moment it unwinds.
|
|
526
|
+
*
|
|
527
|
+
* @throws {Error} If stop() was called while starting
|
|
528
|
+
*
|
|
529
|
+
* @internal
|
|
530
|
+
*/
|
|
531
|
+
private throwIfStopRequested;
|
|
532
|
+
/**
|
|
533
|
+
* libx264, the encoder for H.264 output when no hardware encoder is usable.
|
|
534
|
+
*
|
|
535
|
+
* @returns The libx264 encoder
|
|
536
|
+
*
|
|
537
|
+
* @throws {Error} If the FFmpeg build has no libx264 (node-av-lgpl)
|
|
538
|
+
*
|
|
539
|
+
* @internal
|
|
540
|
+
*/
|
|
541
|
+
private softwareH264Encoder;
|
|
542
|
+
/**
|
|
543
|
+
* GOP size for the video encoder that starts every fragment with a keyframe.
|
|
544
|
+
*
|
|
545
|
+
* frag_duration flushes a fragment once it is that long, mid-GOP on a
|
|
546
|
+
* P-frame unless the GOP has the same length, and strict consumers reject
|
|
547
|
+
* such fragments. Shorter fragments (the 1 µs default flushes after every
|
|
548
|
+
* frame) are not matched: a GOP of a few frames multiplies the bitrate, so
|
|
549
|
+
* below MIN_FRAGMENT_GOP the GOP is 2 s, as with a fragDuration of 0.
|
|
550
|
+
*
|
|
551
|
+
* @param fps - Output frame rate
|
|
552
|
+
*
|
|
553
|
+
* @returns GOP size in frames
|
|
554
|
+
*
|
|
555
|
+
* @internal
|
|
556
|
+
*/
|
|
557
|
+
private gopSize;
|
|
558
|
+
/**
|
|
559
|
+
* Create the fMP4 output muxer with the fragment-emitting write callback.
|
|
560
|
+
*
|
|
561
|
+
* Uses `this.input` when present (demuxer path) and no input for the
|
|
562
|
+
* frame-source path. Attaches a fresh packet age watchdog unless
|
|
563
|
+
* `maxPacketAge` is 0.
|
|
564
|
+
*
|
|
565
|
+
* @returns The configured muxer
|
|
566
|
+
*
|
|
567
|
+
* @internal
|
|
568
|
+
*/
|
|
569
|
+
private createOutput;
|
|
570
|
+
/**
|
|
571
|
+
* Wire the pipeline completion to the fragment/close callbacks.
|
|
572
|
+
*
|
|
573
|
+
* On success ends the fragment iterator and invokes `onClose()`; on error
|
|
574
|
+
* stops the stream, discarding the output the muxer still holds, and invokes
|
|
575
|
+
* `onClose(error)`. Either way `onClose` runs exactly once and the detached
|
|
576
|
+
* chain never rejects. Runs in the background.
|
|
577
|
+
*
|
|
578
|
+
* @param completion - The pipeline completion promise
|
|
579
|
+
*
|
|
580
|
+
* @internal
|
|
581
|
+
*/
|
|
582
|
+
private attachCompletion;
|
|
583
|
+
/**
|
|
584
|
+
* Invoke the owner's onClose callback.
|
|
585
|
+
*
|
|
586
|
+
* The completion chain runs detached, so an exception from the callback
|
|
587
|
+
* could only surface as an unhandled rejection - it is logged instead.
|
|
588
|
+
*
|
|
589
|
+
* @param error - Pipeline error, or undefined for a clean end
|
|
590
|
+
*
|
|
591
|
+
* @internal
|
|
592
|
+
*/
|
|
593
|
+
private emitClose;
|
|
594
|
+
/**
|
|
595
|
+
* Whether a pipeline rejection was caused by an intentional shutdown.
|
|
596
|
+
*
|
|
597
|
+
* @param error - The pipeline rejection reason
|
|
598
|
+
*
|
|
599
|
+
* @returns True when stop() is in flight or the owner's signal aborted
|
|
600
|
+
*
|
|
601
|
+
* @internal
|
|
602
|
+
*/
|
|
603
|
+
private isDeliberateStop;
|
|
604
|
+
/**
|
|
605
|
+
* Set up encoders and the fMP4 muxer for a pre-composited frame source and
|
|
606
|
+
* start encoding. No demuxer or decoder is involved - the provided frames are
|
|
607
|
+
* encoded straight into the fragmented container. Both streams share the ONE
|
|
608
|
+
* muxer, so neither may close it on its own: each source is encoded to packets
|
|
609
|
+
* and written concurrently (the muxer interleaves), and the muxer is closed
|
|
610
|
+
* once all streams have finished.
|
|
611
|
+
*
|
|
612
|
+
* @throws {Error} If the source has neither video nor audio frames
|
|
613
|
+
*
|
|
614
|
+
* @internal
|
|
615
|
+
*/
|
|
616
|
+
private startFromFrames;
|
|
617
|
+
/**
|
|
618
|
+
* Stop streaming gracefully and clean up all resources.
|
|
619
|
+
*
|
|
620
|
+
* Stops the pipeline, closes output, and releases all FFmpeg resources.
|
|
621
|
+
* Safe to call multiple times. After stopping, you can call start() again
|
|
622
|
+
* to restart the stream. Resolves even if the pipeline had failed; that
|
|
623
|
+
* error is reported through `onClose(error)`.
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```typescript
|
|
627
|
+
* const stream = await FMP4Stream.create('input.mp4', {
|
|
628
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2'
|
|
629
|
+
* });
|
|
630
|
+
* await stream.start();
|
|
631
|
+
*
|
|
632
|
+
* // Stop after 10 seconds
|
|
633
|
+
* setTimeout(async () => await stream.stop(), 10000);
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
stop(): Promise<void>;
|
|
637
|
+
/**
|
|
638
|
+
* Perform the actual teardown (see {@link stop}).
|
|
639
|
+
*
|
|
640
|
+
* @internal
|
|
641
|
+
*/
|
|
642
|
+
private doStop;
|
|
643
|
+
/**
|
|
644
|
+
* Async generator that yields media fragments (moof+mdat chunks).
|
|
645
|
+
*
|
|
646
|
+
* In box mode, yields only media fragments (chunks containing moof boxes),
|
|
647
|
+
* NOT the init segment. Use {@link initSegment} to get the ftyp+moov data.
|
|
648
|
+
*
|
|
649
|
+
* In chunk mode (boxMode disabled), yields every chunk as-is since
|
|
650
|
+
* box-level filtering is not possible without box parsing.
|
|
651
|
+
*
|
|
652
|
+
* The generator completes when the stream stops or the pipeline ends.
|
|
653
|
+
*
|
|
654
|
+
* Only a single consumer is supported: a second concurrent fragments() call
|
|
655
|
+
* would silently strand the first consumer, so it throws instead. The
|
|
656
|
+
* backlog is bounded by `maxQueuedFragments` - if the consumer falls behind
|
|
657
|
+
* a live source, the oldest fragments are dropped (see
|
|
658
|
+
* {@link droppedFragments}).
|
|
659
|
+
*
|
|
660
|
+
* @yields {FMP4Fragment} Media fragment with data buffer and box info
|
|
661
|
+
*
|
|
662
|
+
* @throws {Error} If another fragments() iterator is already active
|
|
663
|
+
*
|
|
664
|
+
* @example
|
|
665
|
+
* ```typescript
|
|
666
|
+
* const stream = FMP4Stream.create('rtsp://camera/stream', {
|
|
667
|
+
* supportedCodecs: 'avc1.640029,mp4a.40.2',
|
|
668
|
+
* boxMode: true,
|
|
669
|
+
* });
|
|
670
|
+
*
|
|
671
|
+
* await stream.start();
|
|
672
|
+
* const init = await stream.initSegment;
|
|
673
|
+
*
|
|
674
|
+
* for await (const fragment of stream.fragments()) {
|
|
675
|
+
* sendToClient(fragment.data);
|
|
676
|
+
* }
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
679
|
+
fragments(): AsyncGenerator<FMP4Fragment, void>;
|
|
680
|
+
/**
|
|
681
|
+
* Run the streaming pipeline until completion or stopped.
|
|
682
|
+
*
|
|
683
|
+
* @internal
|
|
684
|
+
*/
|
|
685
|
+
private runPipeline;
|
|
686
|
+
/**
|
|
687
|
+
* Check if the video is encoded again instead of passed through.
|
|
688
|
+
*
|
|
689
|
+
* @param codecId - Codec ID of the source video
|
|
690
|
+
*
|
|
691
|
+
* @returns True if `video.transcode` is set or the codec is not in supported codecs
|
|
692
|
+
*
|
|
693
|
+
* @internal
|
|
694
|
+
*/
|
|
695
|
+
private transcodesVideo;
|
|
696
|
+
/**
|
|
697
|
+
* Check if video codec is supported.
|
|
698
|
+
*
|
|
699
|
+
* @param codecId - Codec ID
|
|
700
|
+
*
|
|
701
|
+
* @returns True if H.264, H.265, or AV1 is in supported codecs
|
|
702
|
+
*
|
|
703
|
+
* @internal
|
|
704
|
+
*/
|
|
705
|
+
private isVideoCodecSupported;
|
|
706
|
+
/**
|
|
707
|
+
* Codec that unsupported audio is transcoded to.
|
|
708
|
+
*
|
|
709
|
+
* @returns Opus when the client lists Opus but not AAC, otherwise AAC
|
|
710
|
+
*
|
|
711
|
+
* @internal
|
|
712
|
+
*/
|
|
713
|
+
private audioTranscodeTarget;
|
|
714
|
+
/**
|
|
715
|
+
* Create the audio filter and encoder for the transcode target.
|
|
716
|
+
*
|
|
717
|
+
* AAC is resampled to 44.1 kHz stereo. Opus is resampled to 48 kHz, the only
|
|
718
|
+
* rate it transmits, keeps mono input mono and uses 20 ms frames.
|
|
719
|
+
*
|
|
720
|
+
* @param decoder - Input audio decoder, absent for a frame source
|
|
721
|
+
*
|
|
722
|
+
* @param channels - Input channel count, 0 when unknown
|
|
723
|
+
*
|
|
724
|
+
* @internal
|
|
725
|
+
*/
|
|
726
|
+
private createAudioEncoder;
|
|
727
|
+
/**
|
|
728
|
+
* Check if audio codec is supported.
|
|
729
|
+
*
|
|
730
|
+
* @param codecId - Codec ID
|
|
731
|
+
*
|
|
732
|
+
* @returns True if AAC, FLAC, or Opus is in supported codecs
|
|
733
|
+
*
|
|
734
|
+
* @internal
|
|
735
|
+
*/
|
|
736
|
+
private isAudioCodecSupported;
|
|
737
|
+
/**
|
|
738
|
+
* Process buffer in box mode - buffers until complete boxes are available.
|
|
739
|
+
*
|
|
740
|
+
* The muxer splits writes at arbitrary positions, so a chunk may end
|
|
741
|
+
* anywhere - including inside a box header. Any trailing partial box
|
|
742
|
+
* (header or body) is buffered for the next chunk; the parser never
|
|
743
|
+
* loses bytes regardless of where the split falls.
|
|
744
|
+
*
|
|
745
|
+
* Emission is fragment-aligned: a `moof` is held back until its `mdat` has
|
|
746
|
+
* arrived, then both are emitted as ONE onData call. The avio buffer flushes
|
|
747
|
+
* at arbitrary points (a fragment larger than the buffer always splits), so
|
|
748
|
+
* without this a consumer would see a bare `moof` followed by a detached
|
|
749
|
+
* `mdat` - unplayable framing for fMP4 consumers.
|
|
750
|
+
* Non-fragment boxes (ftyp, moov, mfra, ...) are emitted as they complete.
|
|
751
|
+
*
|
|
752
|
+
* @param chunk - Incoming data chunk from FFmpeg
|
|
753
|
+
*
|
|
754
|
+
* @internal
|
|
755
|
+
*/
|
|
756
|
+
private processBoxMode;
|
|
757
|
+
/**
|
|
758
|
+
* Emit a group of complete boxes as one onData call.
|
|
759
|
+
*
|
|
760
|
+
* Concatenates the raw box bytes, rebuilds the box list with offsets
|
|
761
|
+
* relative to the emitted buffer, tracks the init segment, and feeds the
|
|
762
|
+
* fragments() queue when the group is a media fragment.
|
|
763
|
+
*
|
|
764
|
+
* @param entries - Complete boxes in stream order
|
|
765
|
+
*
|
|
766
|
+
* @internal
|
|
767
|
+
*/
|
|
768
|
+
private emitBoxGroup;
|
|
769
|
+
/**
|
|
770
|
+
* Push a fragment to the fragment queue for the async generator.
|
|
771
|
+
*
|
|
772
|
+
* @param data - fMP4 data buffer
|
|
773
|
+
*
|
|
774
|
+
* @param info - Parsed box information
|
|
775
|
+
*
|
|
776
|
+
* @internal
|
|
777
|
+
*/
|
|
778
|
+
private pushFragment;
|
|
779
|
+
/**
|
|
780
|
+
* Reject a pending initSegment promise so consumers never hang.
|
|
781
|
+
*
|
|
782
|
+
* No-op when the promise was never requested or has already settled. The
|
|
783
|
+
* promise is marked as handled to avoid an unhandled rejection when no
|
|
784
|
+
* consumer is currently awaiting it.
|
|
785
|
+
*
|
|
786
|
+
* @param error - Rejection reason (pipeline error or stop-before-init)
|
|
787
|
+
*
|
|
788
|
+
* @internal
|
|
789
|
+
*/
|
|
790
|
+
private rejectInitSegment;
|
|
791
|
+
/**
|
|
792
|
+
* Signal the fragment queue that no more fragments will arrive.
|
|
793
|
+
*
|
|
794
|
+
* @internal
|
|
795
|
+
*/
|
|
796
|
+
private endFragments;
|
|
797
|
+
}
|