node-av 3.1.3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/README.md +88 -52
  2. package/binding.gyp +23 -11
  3. package/dist/api/audio-frame-buffer.d.ts +201 -0
  4. package/dist/api/audio-frame-buffer.js +275 -0
  5. package/dist/api/audio-frame-buffer.js.map +1 -0
  6. package/dist/api/bitstream-filter.d.ts +320 -78
  7. package/dist/api/bitstream-filter.js +684 -151
  8. package/dist/api/bitstream-filter.js.map +1 -1
  9. package/dist/api/constants.d.ts +44 -0
  10. package/dist/api/constants.js +45 -0
  11. package/dist/api/constants.js.map +1 -0
  12. package/dist/api/data/test_av1.ivf +0 -0
  13. package/dist/api/data/test_mjpeg.mjpeg +0 -0
  14. package/dist/api/data/test_vp8.ivf +0 -0
  15. package/dist/api/data/test_vp9.ivf +0 -0
  16. package/dist/api/decoder.d.ts +454 -77
  17. package/dist/api/decoder.js +1081 -271
  18. package/dist/api/decoder.js.map +1 -1
  19. package/dist/api/{media-input.d.ts → demuxer.d.ts} +295 -45
  20. package/dist/api/demuxer.js +1965 -0
  21. package/dist/api/demuxer.js.map +1 -0
  22. package/dist/api/encoder.d.ts +423 -132
  23. package/dist/api/encoder.js +1089 -240
  24. package/dist/api/encoder.js.map +1 -1
  25. package/dist/api/filter-complex.d.ts +769 -0
  26. package/dist/api/filter-complex.js +1596 -0
  27. package/dist/api/filter-complex.js.map +1 -0
  28. package/dist/api/filter-presets.d.ts +80 -5
  29. package/dist/api/filter-presets.js +117 -7
  30. package/dist/api/filter-presets.js.map +1 -1
  31. package/dist/api/filter.d.ts +561 -125
  32. package/dist/api/filter.js +1083 -274
  33. package/dist/api/filter.js.map +1 -1
  34. package/dist/api/{fmp4.d.ts → fmp4-stream.d.ts} +141 -140
  35. package/dist/api/fmp4-stream.js +539 -0
  36. package/dist/api/fmp4-stream.js.map +1 -0
  37. package/dist/api/hardware.d.ts +58 -6
  38. package/dist/api/hardware.js +127 -11
  39. package/dist/api/hardware.js.map +1 -1
  40. package/dist/api/index.d.ts +8 -4
  41. package/dist/api/index.js +17 -8
  42. package/dist/api/index.js.map +1 -1
  43. package/dist/api/io-stream.d.ts +6 -6
  44. package/dist/api/io-stream.js +5 -4
  45. package/dist/api/io-stream.js.map +1 -1
  46. package/dist/api/{media-output.d.ts → muxer.d.ts} +280 -66
  47. package/dist/api/muxer.js +1934 -0
  48. package/dist/api/muxer.js.map +1 -0
  49. package/dist/api/pipeline.d.ts +77 -29
  50. package/dist/api/pipeline.js +449 -439
  51. package/dist/api/pipeline.js.map +1 -1
  52. package/dist/api/rtp-stream.d.ts +312 -0
  53. package/dist/api/rtp-stream.js +630 -0
  54. package/dist/api/rtp-stream.js.map +1 -0
  55. package/dist/api/types.d.ts +533 -56
  56. package/dist/api/utilities/async-queue.d.ts +91 -0
  57. package/dist/api/utilities/async-queue.js +162 -0
  58. package/dist/api/utilities/async-queue.js.map +1 -0
  59. package/dist/api/utilities/audio-sample.d.ts +11 -1
  60. package/dist/api/utilities/audio-sample.js +10 -0
  61. package/dist/api/utilities/audio-sample.js.map +1 -1
  62. package/dist/api/utilities/channel-layout.d.ts +1 -0
  63. package/dist/api/utilities/channel-layout.js +1 -0
  64. package/dist/api/utilities/channel-layout.js.map +1 -1
  65. package/dist/api/utilities/image.d.ts +39 -1
  66. package/dist/api/utilities/image.js +38 -0
  67. package/dist/api/utilities/image.js.map +1 -1
  68. package/dist/api/utilities/index.d.ts +3 -0
  69. package/dist/api/utilities/index.js +6 -0
  70. package/dist/api/utilities/index.js.map +1 -1
  71. package/dist/api/utilities/media-type.d.ts +2 -1
  72. package/dist/api/utilities/media-type.js +1 -0
  73. package/dist/api/utilities/media-type.js.map +1 -1
  74. package/dist/api/utilities/pixel-format.d.ts +4 -1
  75. package/dist/api/utilities/pixel-format.js +3 -0
  76. package/dist/api/utilities/pixel-format.js.map +1 -1
  77. package/dist/api/utilities/sample-format.d.ts +6 -1
  78. package/dist/api/utilities/sample-format.js +5 -0
  79. package/dist/api/utilities/sample-format.js.map +1 -1
  80. package/dist/api/utilities/scheduler.d.ts +138 -0
  81. package/dist/api/utilities/scheduler.js +98 -0
  82. package/dist/api/utilities/scheduler.js.map +1 -0
  83. package/dist/api/utilities/streaming.d.ts +105 -15
  84. package/dist/api/utilities/streaming.js +201 -12
  85. package/dist/api/utilities/streaming.js.map +1 -1
  86. package/dist/api/utilities/timestamp.d.ts +15 -1
  87. package/dist/api/utilities/timestamp.js +14 -0
  88. package/dist/api/utilities/timestamp.js.map +1 -1
  89. package/dist/api/utilities/whisper-model.d.ts +310 -0
  90. package/dist/api/utilities/whisper-model.js +528 -0
  91. package/dist/api/utilities/whisper-model.js.map +1 -0
  92. package/dist/api/webrtc-stream.d.ts +288 -0
  93. package/dist/api/webrtc-stream.js +440 -0
  94. package/dist/api/webrtc-stream.js.map +1 -0
  95. package/dist/api/whisper.d.ts +324 -0
  96. package/dist/api/whisper.js +362 -0
  97. package/dist/api/whisper.js.map +1 -0
  98. package/dist/constants/constants.d.ts +54 -2
  99. package/dist/constants/constants.js +48 -1
  100. package/dist/constants/constants.js.map +1 -1
  101. package/dist/constants/encoders.d.ts +2 -1
  102. package/dist/constants/encoders.js +4 -3
  103. package/dist/constants/encoders.js.map +1 -1
  104. package/dist/constants/hardware.d.ts +26 -0
  105. package/dist/constants/hardware.js +27 -0
  106. package/dist/constants/hardware.js.map +1 -0
  107. package/dist/constants/index.d.ts +1 -0
  108. package/dist/constants/index.js +1 -0
  109. package/dist/constants/index.js.map +1 -1
  110. package/dist/ffmpeg/index.d.ts +3 -3
  111. package/dist/ffmpeg/index.js +3 -3
  112. package/dist/ffmpeg/utils.d.ts +27 -0
  113. package/dist/ffmpeg/utils.js +28 -16
  114. package/dist/ffmpeg/utils.js.map +1 -1
  115. package/dist/lib/binding.d.ts +22 -11
  116. package/dist/lib/binding.js.map +1 -1
  117. package/dist/lib/codec-context.d.ts +87 -0
  118. package/dist/lib/codec-context.js +125 -4
  119. package/dist/lib/codec-context.js.map +1 -1
  120. package/dist/lib/codec-parameters.d.ts +229 -1
  121. package/dist/lib/codec-parameters.js +264 -0
  122. package/dist/lib/codec-parameters.js.map +1 -1
  123. package/dist/lib/codec-parser.d.ts +23 -0
  124. package/dist/lib/codec-parser.js +25 -0
  125. package/dist/lib/codec-parser.js.map +1 -1
  126. package/dist/lib/codec.d.ts +26 -4
  127. package/dist/lib/codec.js +35 -0
  128. package/dist/lib/codec.js.map +1 -1
  129. package/dist/lib/dictionary.js +1 -0
  130. package/dist/lib/dictionary.js.map +1 -1
  131. package/dist/lib/error.js +1 -1
  132. package/dist/lib/error.js.map +1 -1
  133. package/dist/lib/fifo.d.ts +416 -0
  134. package/dist/lib/fifo.js +453 -0
  135. package/dist/lib/fifo.js.map +1 -0
  136. package/dist/lib/filter-context.d.ts +52 -11
  137. package/dist/lib/filter-context.js +56 -12
  138. package/dist/lib/filter-context.js.map +1 -1
  139. package/dist/lib/filter-graph.d.ts +9 -0
  140. package/dist/lib/filter-graph.js +13 -0
  141. package/dist/lib/filter-graph.js.map +1 -1
  142. package/dist/lib/filter.d.ts +21 -0
  143. package/dist/lib/filter.js +28 -0
  144. package/dist/lib/filter.js.map +1 -1
  145. package/dist/lib/format-context.d.ts +48 -14
  146. package/dist/lib/format-context.js +76 -7
  147. package/dist/lib/format-context.js.map +1 -1
  148. package/dist/lib/frame.d.ts +264 -1
  149. package/dist/lib/frame.js +351 -1
  150. package/dist/lib/frame.js.map +1 -1
  151. package/dist/lib/hardware-device-context.d.ts +3 -2
  152. package/dist/lib/hardware-device-context.js.map +1 -1
  153. package/dist/lib/index.d.ts +2 -0
  154. package/dist/lib/index.js +4 -0
  155. package/dist/lib/index.js.map +1 -1
  156. package/dist/lib/input-format.d.ts +21 -0
  157. package/dist/lib/input-format.js +42 -2
  158. package/dist/lib/input-format.js.map +1 -1
  159. package/dist/lib/native-types.d.ts +76 -27
  160. package/dist/lib/option.d.ts +25 -13
  161. package/dist/lib/option.js +28 -0
  162. package/dist/lib/option.js.map +1 -1
  163. package/dist/lib/output-format.d.ts +22 -1
  164. package/dist/lib/output-format.js +28 -0
  165. package/dist/lib/output-format.js.map +1 -1
  166. package/dist/lib/packet.d.ts +35 -0
  167. package/dist/lib/packet.js +52 -2
  168. package/dist/lib/packet.js.map +1 -1
  169. package/dist/lib/rational.d.ts +18 -0
  170. package/dist/lib/rational.js +19 -0
  171. package/dist/lib/rational.js.map +1 -1
  172. package/dist/lib/stream.d.ts +126 -0
  173. package/dist/lib/stream.js +188 -5
  174. package/dist/lib/stream.js.map +1 -1
  175. package/dist/lib/sync-queue.d.ts +179 -0
  176. package/dist/lib/sync-queue.js +197 -0
  177. package/dist/lib/sync-queue.js.map +1 -0
  178. package/dist/lib/types.d.ts +49 -1
  179. package/dist/lib/utilities.d.ts +281 -53
  180. package/dist/lib/utilities.js +298 -55
  181. package/dist/lib/utilities.js.map +1 -1
  182. package/install/check.js +2 -2
  183. package/package.json +37 -26
  184. package/dist/api/fmp4.js +0 -710
  185. package/dist/api/fmp4.js.map +0 -1
  186. package/dist/api/media-input.js +0 -1075
  187. package/dist/api/media-input.js.map +0 -1
  188. package/dist/api/media-output.js +0 -1040
  189. package/dist/api/media-output.js.map +0 -1
  190. package/dist/api/webrtc.d.ts +0 -664
  191. package/dist/api/webrtc.js +0 -1132
  192. package/dist/api/webrtc.js.map +0 -1
@@ -0,0 +1,630 @@
1
+ import { isRtcp, RtpPacket } from 'werift';
2
+ import { AV_HWDEVICE_TYPE_NONE } from '../constants/constants.js';
3
+ import { FF_ENCODER_LIBOPUS, FF_ENCODER_LIBX264, FF_ENCODER_LIBX265 } from '../constants/encoders.js';
4
+ import { Codec } from '../lib/codec.js';
5
+ import { MAX_PACKET_SIZE } from './constants.js';
6
+ import { Decoder } from './decoder.js';
7
+ import { Demuxer } from './demuxer.js';
8
+ import { Encoder } from './encoder.js';
9
+ import { FilterPreset } from './filter-presets.js';
10
+ import { FilterAPI } from './filter.js';
11
+ import { HardwareContext } from './hardware.js';
12
+ import { Muxer } from './muxer.js';
13
+ import { pipeline } from './pipeline.js';
14
+ /**
15
+ * Generic RTP streaming with automatic codec detection and transcoding.
16
+ *
17
+ * Provides library-agnostic RTP streaming for various applications.
18
+ * Automatically detects input codecs and transcodes non-compatible formats.
19
+ * Supports hardware acceleration for video transcoding.
20
+ * Essential component for building RTP streaming servers.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { RTPStream } from 'node-av/api';
25
+ *
26
+ * // Create stream with RTP packet callbacks
27
+ * const stream = RTPStream.create('rtsp://camera.local/stream', {
28
+ * mtu: 1200,
29
+ * hardware: 'auto',
30
+ * onVideoPacket: (rtp) => {
31
+ * // Send RTP packet
32
+ * sendRtpPacket(rtp);
33
+ * },
34
+ * onAudioPacket: (rtp) => {
35
+ * sendRtpPacket(rtp);
36
+ * }
37
+ * });
38
+ *
39
+ * // Start streaming
40
+ * await stream.start();
41
+ * ```
42
+ *
43
+ * @see {@link Demuxer} For input media handling
44
+ * @see {@link HardwareContext} For GPU acceleration
45
+ */
46
+ export class RTPStream {
47
+ options;
48
+ inputUrl;
49
+ inputOptions;
50
+ input;
51
+ videoOutput;
52
+ audioOutput;
53
+ hardwareContext;
54
+ videoDecoder;
55
+ videoEncoder;
56
+ audioDecoder;
57
+ audioFilter;
58
+ audioEncoder;
59
+ pipeline;
60
+ supportedVideoCodecs;
61
+ supportedAudioCodecs;
62
+ /**
63
+ * @param inputUrl - Media input URL
64
+ *
65
+ * @param options - Stream configuration options
66
+ *
67
+ * Use {@link create} factory method
68
+ *
69
+ * @internal
70
+ */
71
+ constructor(inputUrl, options) {
72
+ this.inputUrl = inputUrl;
73
+ this.inputOptions = {
74
+ ...options.inputOptions,
75
+ options: {
76
+ flags: 'low_delay',
77
+ fflags: 'nobuffer',
78
+ analyzeduration: 0,
79
+ probesize: 32,
80
+ timeout: 5000000,
81
+ rtsp_transport: inputUrl.toLowerCase().startsWith('rtsp') ? 'tcp' : undefined,
82
+ ...options.inputOptions?.options,
83
+ },
84
+ };
85
+ options.supportedVideoCodecs = options.supportedVideoCodecs?.filter(Boolean);
86
+ options.supportedAudioCodecs = options.supportedAudioCodecs?.filter(Boolean);
87
+ // If no supported codecs specified, empty set means ALL codecs are supported (passthrough only)
88
+ this.supportedVideoCodecs = new Set(options.supportedVideoCodecs && options.supportedVideoCodecs.length > 0 ? options.supportedVideoCodecs : []);
89
+ this.supportedAudioCodecs = new Set(options.supportedAudioCodecs && options.supportedAudioCodecs.length > 0 ? options.supportedAudioCodecs : []);
90
+ this.options = {
91
+ onVideoPacket: options.onVideoPacket ?? (() => { }),
92
+ onAudioPacket: options.onAudioPacket ?? (() => { }),
93
+ onClose: options.onClose ?? (() => { }),
94
+ supportedVideoCodecs: Array.from(this.supportedVideoCodecs),
95
+ supportedAudioCodecs: Array.from(this.supportedAudioCodecs),
96
+ hardware: options.hardware ?? { deviceType: AV_HWDEVICE_TYPE_NONE },
97
+ inputOptions: options.inputOptions,
98
+ video: {
99
+ ssrc: options.video?.ssrc,
100
+ payloadType: options.video?.payloadType,
101
+ mtu: options.video?.mtu ?? MAX_PACKET_SIZE,
102
+ fps: options.video?.fps ?? 20,
103
+ encoderOptions: options.video?.encoderOptions ?? {},
104
+ },
105
+ audio: {
106
+ ssrc: options.audio?.ssrc,
107
+ payloadType: options.audio?.payloadType,
108
+ mtu: options.audio?.mtu ?? MAX_PACKET_SIZE,
109
+ sampleRate: options.audio?.sampleRate,
110
+ channels: options.audio?.channels,
111
+ encoderOptions: options.audio?.encoderOptions,
112
+ sampleFormat: options.audio?.sampleFormat,
113
+ },
114
+ };
115
+ }
116
+ /**
117
+ * Create an RTP stream from a media source.
118
+ *
119
+ * Configures the stream with input URL and options. The input is not opened
120
+ * until start() is called, allowing the stream to be reused after stop().
121
+ *
122
+ * @param inputUrl - Media source URL (RTSP, file path, HTTP, etc.)
123
+ *
124
+ * @param options - Stream configuration options
125
+ *
126
+ * @returns Configured RTP stream instance
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * // Stream from RTSP camera
131
+ * const stream = RTPStream.create('rtsp://camera.local/stream', {
132
+ * mtu: 1200,
133
+ * onVideoPacket: (rtp) => sendPacket(rtp),
134
+ * onAudioPacket: (rtp) => sendPacket(rtp)
135
+ * });
136
+ * ```
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * // Stream file with auto hardware acceleration
141
+ * const stream = RTPStream.create('video.mp4', {
142
+ * hardware: 'auto'
143
+ * });
144
+ * ```
145
+ */
146
+ static create(inputUrl, options = {}) {
147
+ return new RTPStream(inputUrl, options);
148
+ }
149
+ /**
150
+ * Check if the stream is active.
151
+ *
152
+ * @returns True if the stream is active, false otherwise
153
+ */
154
+ get isStreamActive() {
155
+ return this.pipeline !== undefined && !this.pipeline.isStopped();
156
+ }
157
+ /**
158
+ * Get the demuxer instance.
159
+ *
160
+ * Used for accessing the underlying demuxer.
161
+ * Only available after start() is called.
162
+ *
163
+ * @returns Demuxer instance or undefined if not started
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * const stream = RTPStream.create('input.mp4', {
168
+ * onVideoPacket: (rtp) => sendRtp(rtp)
169
+ * });
170
+ * await stream.start();
171
+ * const input = stream.getInput();
172
+ * console.log('Bitrate:', input?.bitRate);
173
+ * ```
174
+ */
175
+ getInput() {
176
+ return this.input;
177
+ }
178
+ /**
179
+ * Start streaming media to RTP packets.
180
+ *
181
+ * Begins the media processing pipeline, reading packets from input,
182
+ * transcoding if necessary, and invoking RTP packet callbacks.
183
+ * Automatically handles video and audio streams in parallel.
184
+ * This method returns immediately after starting the pipeline.
185
+ *
186
+ * @returns Promise that resolves when pipeline is started
187
+ *
188
+ * @throws {Error} If no video stream found in input
189
+ *
190
+ * @throws {FFmpegError} If setup fails
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * const stream = RTPStream.create('rtsp://camera.local/stream', {
195
+ * onVideoPacket: (rtp) => sendRtp(rtp)
196
+ * });
197
+ *
198
+ * // Start streaming (returns immediately)
199
+ * await stream.start();
200
+ *
201
+ * // Later: stop streaming
202
+ * await stream.stop();
203
+ * ```
204
+ */
205
+ async start() {
206
+ if (this.pipeline) {
207
+ return;
208
+ }
209
+ this.input ??= await Demuxer.open(this.inputUrl, this.inputOptions);
210
+ const videoStream = this.input.video();
211
+ const audioStream = this.input.audio();
212
+ // Setup video transcoding if needed
213
+ if (videoStream && !this.isVideoCodecSupported(videoStream.codecpar.codecId)) {
214
+ // Check if we need hardware acceleration
215
+ if (this.options.hardware === 'auto') {
216
+ this.hardwareContext = HardwareContext.auto();
217
+ }
218
+ else if (this.options.hardware.deviceType !== AV_HWDEVICE_TYPE_NONE) {
219
+ this.hardwareContext = HardwareContext.create(this.options.hardware.deviceType, this.options.hardware.device, this.options.hardware.options);
220
+ }
221
+ this.videoDecoder = await Decoder.create(videoStream, {
222
+ exitOnError: false,
223
+ hardware: this.hardwareContext,
224
+ });
225
+ // Get first supported codec
226
+ const targetCodecId = this.options.supportedVideoCodecs[0];
227
+ if (!targetCodecId) {
228
+ throw new Error('No supported video codec specified for transcoding');
229
+ }
230
+ let encoderCodec = null;
231
+ if (typeof targetCodecId === 'string') {
232
+ encoderCodec = Codec.findEncoderByName(targetCodecId);
233
+ }
234
+ else {
235
+ encoderCodec = this.hardwareContext?.getEncoderCodec(targetCodecId) ?? Codec.findEncoder(targetCodecId);
236
+ }
237
+ if (!encoderCodec) {
238
+ throw new Error(`No encoder found for codec ID ${targetCodecId}`);
239
+ }
240
+ let encoderOptions = {};
241
+ if (encoderCodec.name === FF_ENCODER_LIBX264 || encoderCodec.name === FF_ENCODER_LIBX265) {
242
+ encoderOptions.preset = 'ultrafast';
243
+ encoderOptions.tune = 'zerolatency';
244
+ }
245
+ encoderOptions = {
246
+ ...encoderOptions,
247
+ ...this.options.video.encoderOptions,
248
+ };
249
+ this.videoEncoder = await Encoder.create(encoderCodec, {
250
+ decoder: this.videoDecoder,
251
+ maxBFrames: 0,
252
+ options: encoderOptions,
253
+ });
254
+ }
255
+ // Initialize RTP sequence numbers and timestamps
256
+ let videoSequenceNumber = Math.floor(Math.random() * 0xffff);
257
+ let videoTimestamp = Math.floor(Math.random() * 0xffffffff) >>> 0; // unsigned 32-bit
258
+ let audioSequenceNumber = Math.floor(Math.random() * 0xffff);
259
+ // Calculate video timestamp increment
260
+ const videoStreamFps = videoStream ? videoStream.avgFrameRate.num / videoStream.avgFrameRate.den : 20;
261
+ let fps = this.options.video.fps ?? videoStreamFps;
262
+ if (!isFinite(fps) || fps <= 0 || isNaN(fps)) {
263
+ fps = 20; // Default to 20 FPS if invalid
264
+ }
265
+ const videoTimestampIncrement = 90000 / fps;
266
+ // Setup video output
267
+ this.videoOutput = await Muxer.open({
268
+ write: (buffer) => {
269
+ if (isRtcp(buffer)) {
270
+ // Ignore RTCP packets
271
+ return buffer.length;
272
+ }
273
+ const rtpPacket = RtpPacket.deSerialize(buffer);
274
+ // Set SSRC (synchronization source identifier)
275
+ if (this.options.video.ssrc !== undefined) {
276
+ rtpPacket.header.ssrc = this.options.video.ssrc;
277
+ }
278
+ // Set payload type
279
+ if (this.options.video.payloadType !== undefined) {
280
+ rtpPacket.header.payloadType = this.options.video.payloadType;
281
+ }
282
+ // Fix sequence number - ensure continuous sequence
283
+ rtpPacket.header.sequenceNumber = videoSequenceNumber;
284
+ videoSequenceNumber = (videoSequenceNumber + 1) & 0xffff; // Wrap at 16-bit
285
+ // Fix timestamp - calculate based on FPS
286
+ // All packets in same frame have same timestamp (marker=false)
287
+ // Only increment timestamp when frame ends (marker=true)
288
+ rtpPacket.header.timestamp = videoTimestamp;
289
+ // Increment timestamp for next frame when current frame ends
290
+ if (rtpPacket.header.marker) {
291
+ videoTimestamp = (videoTimestamp + videoTimestampIncrement) >>> 0; // Unsigned 32-bit wrap
292
+ }
293
+ this.options.onVideoPacket(rtpPacket);
294
+ return buffer.length;
295
+ },
296
+ }, {
297
+ input: this.input,
298
+ copyInitialNonkeyframes: true,
299
+ format: 'rtp',
300
+ maxPacketSize: this.options.video.mtu,
301
+ options: {
302
+ pkt_size: this.options.video.mtu,
303
+ },
304
+ });
305
+ // Setup audio if available and needs transcoding
306
+ if (audioStream && !this.isAudioCodecSupported(audioStream.codecpar.codecId)) {
307
+ this.audioDecoder = await Decoder.create(audioStream, {
308
+ exitOnError: false,
309
+ });
310
+ // Get first supported audio codec
311
+ const targetCodecId = this.options.supportedAudioCodecs[0];
312
+ if (!targetCodecId) {
313
+ throw new Error('No supported audio codec specified for transcoding');
314
+ }
315
+ let encoderCodec = null;
316
+ if (typeof targetCodecId === 'string') {
317
+ encoderCodec = Codec.findEncoderByName(targetCodecId);
318
+ }
319
+ else {
320
+ encoderCodec = Codec.findEncoder(targetCodecId);
321
+ }
322
+ if (!encoderCodec) {
323
+ throw new Error(`No encoder found for codec ID ${targetCodecId}`);
324
+ }
325
+ // Determine target audio parameters from options
326
+ const desiredSampleFormat = this.options.audio?.sampleFormat ?? audioStream.codecpar.format;
327
+ const desiredSampleRate = this.options.audio?.sampleRate ?? (audioStream.codecpar.sampleRate > 0 ? audioStream.codecpar.sampleRate : 48000);
328
+ const desiredChannels = this.options.audio?.channels ?? (audioStream.codecpar.channels > 0 ? audioStream.codecpar.channels : 2);
329
+ // Select best supported parameters from codec
330
+ const targetSampleFormat = this.selectSampleFormat(encoderCodec, desiredSampleFormat);
331
+ const targetSampleRate = this.selectSampleRate(encoderCodec, desiredSampleRate);
332
+ const channelLayoutStr = this.selectChannelLayout(encoderCodec, desiredChannels);
333
+ // Create audio filter for resampling
334
+ const filterChain = FilterPreset.chain().aformat(targetSampleFormat, targetSampleRate, channelLayoutStr).build();
335
+ // FilterAPI now auto-calculates timeBase from first frame (FFmpeg behavior)
336
+ this.audioFilter = FilterAPI.create(filterChain);
337
+ let encoderOptions = {};
338
+ if (encoderCodec.name === FF_ENCODER_LIBOPUS) {
339
+ encoderOptions.application = 'lowdelay';
340
+ encoderOptions.frame_duration = 20;
341
+ }
342
+ encoderOptions = {
343
+ ...encoderOptions,
344
+ ...this.options.audio.encoderOptions,
345
+ };
346
+ this.audioEncoder = await Encoder.create(encoderCodec, {
347
+ decoder: this.audioDecoder,
348
+ filter: this.audioFilter,
349
+ options: encoderOptions,
350
+ });
351
+ }
352
+ // Setup audio output if available
353
+ if (audioStream) {
354
+ this.audioOutput = await Muxer.open({
355
+ write: (buffer) => {
356
+ if (isRtcp(buffer)) {
357
+ // Ignore RTCP packets
358
+ return buffer.length;
359
+ }
360
+ const rtpPacket = RtpPacket.deSerialize(buffer);
361
+ // Set SSRC (synchronization source identifier)
362
+ if (this.options.audio.ssrc !== undefined) {
363
+ rtpPacket.header.ssrc = this.options.audio.ssrc;
364
+ }
365
+ // Set payload type
366
+ if (this.options.audio.payloadType !== undefined) {
367
+ rtpPacket.header.payloadType = this.options.audio.payloadType;
368
+ }
369
+ // Fix sequence number - ensure continuous sequence
370
+ rtpPacket.header.sequenceNumber = audioSequenceNumber;
371
+ audioSequenceNumber = (audioSequenceNumber + 1) & 0xffff; // Wrap at 16-bit
372
+ this.options.onAudioPacket(rtpPacket);
373
+ return buffer.length;
374
+ },
375
+ }, {
376
+ input: this.input,
377
+ copyInitialNonkeyframes: true,
378
+ format: 'rtp',
379
+ maxPacketSize: this.options.audio.mtu,
380
+ options: {
381
+ pkt_size: this.options.audio.mtu,
382
+ },
383
+ });
384
+ }
385
+ // Start pipeline in background (don't await)
386
+ this.runPipeline()
387
+ .then(() => {
388
+ // Pipeline completed successfully
389
+ this.options.onClose?.();
390
+ })
391
+ .catch(async (error) => {
392
+ console.error('[RTPStream] Pipeline error:', error);
393
+ await this.stop();
394
+ this.options.onClose?.(error);
395
+ });
396
+ }
397
+ /**
398
+ * Run the streaming pipeline until completion or stopped.
399
+ *
400
+ * @internal
401
+ */
402
+ async runPipeline() {
403
+ if (!this.input) {
404
+ return;
405
+ }
406
+ const hasVideo = this.input.video() !== undefined && this.videoOutput !== undefined;
407
+ const hasAudio = this.input.audio() !== undefined && this.audioOutput !== undefined;
408
+ if (hasAudio && hasVideo) {
409
+ this.pipeline = pipeline(this.input, {
410
+ video: [this.videoDecoder, this.videoEncoder],
411
+ audio: [this.audioDecoder, this.audioFilter, this.audioEncoder],
412
+ }, {
413
+ video: this.videoOutput,
414
+ audio: this.audioOutput,
415
+ });
416
+ }
417
+ else if (hasVideo) {
418
+ this.pipeline = pipeline(this.input, {
419
+ video: [this.videoDecoder, this.videoEncoder],
420
+ }, this.videoOutput);
421
+ }
422
+ else if (hasAudio) {
423
+ this.pipeline = pipeline(this.input, {
424
+ audio: [this.audioDecoder, this.audioFilter, this.audioEncoder],
425
+ }, this.audioOutput);
426
+ }
427
+ else {
428
+ throw new Error('No audio or video streams found in input');
429
+ }
430
+ await this.pipeline.completion;
431
+ this.pipeline = undefined;
432
+ }
433
+ /**
434
+ * Stop streaming gracefully and clean up all resources.
435
+ *
436
+ * Stops the pipeline, closes output, and releases all FFmpeg resources.
437
+ * Safe to call multiple times. After stopping, you can call start() again
438
+ * to restart the stream.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * const stream = RTPStream.create('input.mp4', {
443
+ * onVideoPacket: (rtp) => sendRtp(rtp)
444
+ * });
445
+ * await stream.start();
446
+ *
447
+ * // Stop after 10 seconds
448
+ * setTimeout(async () => await stream.stop(), 10000);
449
+ * ```
450
+ */
451
+ async stop() {
452
+ // Stop pipeline if running and wait for completion
453
+ if (this.pipeline && !this.pipeline.isStopped()) {
454
+ this.pipeline.stop();
455
+ await this.pipeline.completion;
456
+ this.pipeline = undefined;
457
+ }
458
+ // Close all resources
459
+ await this.videoOutput?.close();
460
+ this.videoOutput = undefined;
461
+ await this.audioOutput?.close();
462
+ this.audioOutput = undefined;
463
+ this.videoEncoder?.close();
464
+ this.videoEncoder = undefined;
465
+ this.videoDecoder?.close();
466
+ this.videoDecoder = undefined;
467
+ this.audioEncoder?.close();
468
+ this.audioEncoder = undefined;
469
+ this.audioFilter?.close();
470
+ this.audioFilter = undefined;
471
+ this.audioDecoder?.close();
472
+ this.audioDecoder = undefined;
473
+ this.hardwareContext?.dispose();
474
+ this.hardwareContext = undefined;
475
+ await this.input?.close();
476
+ this.input = undefined;
477
+ }
478
+ /**
479
+ * Check if the given audio codec is supported.
480
+ *
481
+ * @param codecId - The AVCodecID to check
482
+ *
483
+ * @returns True if the codec is supported, false otherwise
484
+ *
485
+ * @internal
486
+ */
487
+ isAudioCodecSupported(codecId) {
488
+ // Empty set means all codecs are supported (passthrough only)
489
+ if (this.supportedAudioCodecs.size === 0) {
490
+ return true;
491
+ }
492
+ const isSupported = this.supportedAudioCodecs.has(codecId);
493
+ if (!isSupported) {
494
+ try {
495
+ const ffEncoderCodecs = Array.from(this.supportedAudioCodecs).filter((c) => typeof c === 'string');
496
+ for (const encoderName of ffEncoderCodecs) {
497
+ const encoderCodec = Codec.findEncoderByName(encoderName);
498
+ if (encoderCodec?.id === codecId) {
499
+ return true;
500
+ }
501
+ }
502
+ }
503
+ catch {
504
+ // Ignore errors
505
+ }
506
+ }
507
+ return isSupported;
508
+ }
509
+ /**
510
+ * Check if the given video codec is supported.
511
+ *
512
+ * @param codecId - The AVCodecID to check
513
+ *
514
+ * @returns True if the codec is supported, false otherwise
515
+ *
516
+ * @internal
517
+ */
518
+ isVideoCodecSupported(codecId) {
519
+ // Empty set means all codecs are supported (passthrough only)
520
+ if (this.supportedVideoCodecs.size === 0) {
521
+ return true;
522
+ }
523
+ const isSupported = this.supportedVideoCodecs.has(codecId);
524
+ if (!isSupported) {
525
+ try {
526
+ const ffEncoderCodecs = Array.from(this.supportedVideoCodecs).filter((c) => typeof c === 'string');
527
+ for (const encoderName of ffEncoderCodecs) {
528
+ const encoderCodec = Codec.findEncoderByName(encoderName);
529
+ if (encoderCodec?.id === codecId) {
530
+ return true;
531
+ }
532
+ }
533
+ }
534
+ catch {
535
+ // Ignore errors
536
+ }
537
+ }
538
+ return isSupported;
539
+ }
540
+ /**
541
+ * Select the best supported sample format from codec.
542
+ *
543
+ * Returns the first supported format, or null if none available.
544
+ * This follows FFmpeg's approach of using the first supported format.
545
+ *
546
+ * @param codec - Audio encoder codec
547
+ *
548
+ * @param desiredFormat - Desired sample format
549
+ *
550
+ * @returns First supported sample format or null
551
+ *
552
+ * @internal
553
+ */
554
+ selectSampleFormat(codec, desiredFormat) {
555
+ const supportedFormats = codec.sampleFormats;
556
+ if (!supportedFormats || supportedFormats.length === 0) {
557
+ return desiredFormat; // should normally not happen
558
+ }
559
+ if (supportedFormats.includes(desiredFormat)) {
560
+ return desiredFormat;
561
+ }
562
+ return supportedFormats[0];
563
+ }
564
+ /**
565
+ * Select the best supported sample rate from codec.
566
+ *
567
+ * Returns the closest supported rate to the desired rate.
568
+ * If no rates are specified by the codec, returns the desired rate.
569
+ *
570
+ * @param codec - Audio encoder codec
571
+ *
572
+ * @param desiredRate - Desired sample rate
573
+ *
574
+ * @returns Best matching sample rate
575
+ *
576
+ * @internal
577
+ */
578
+ selectSampleRate(codec, desiredRate) {
579
+ const supportedRates = codec.supportedSamplerates;
580
+ if (!supportedRates || supportedRates.length === 0) {
581
+ return desiredRate; // should normally not happen
582
+ }
583
+ let bestSampleRate = supportedRates[0];
584
+ for (const rate of supportedRates) {
585
+ if (Math.abs(desiredRate - rate) < Math.abs(desiredRate - bestSampleRate)) {
586
+ bestSampleRate = rate;
587
+ }
588
+ }
589
+ return bestSampleRate;
590
+ }
591
+ /**
592
+ * Select the best supported channel layout from codec.
593
+ *
594
+ * Returns a layout matching the desired channel count, or the first supported layout.
595
+ *
596
+ * @param codec - Audio encoder codec
597
+ *
598
+ * @param desiredChannels - Desired number of channels
599
+ *
600
+ * @returns Best matching channel layout string
601
+ *
602
+ * @internal
603
+ */
604
+ selectChannelLayout(codec, desiredChannels) {
605
+ const supportedLayouts = codec.channelLayouts;
606
+ if (!supportedLayouts || supportedLayouts.length === 0) {
607
+ return desiredChannels === 1 ? 'mono' : 'stereo'; // should normally not happen
608
+ }
609
+ // Try to find exact match
610
+ for (const layout of supportedLayouts) {
611
+ if (layout.nbChannels === desiredChannels) {
612
+ // Use standard names for common layouts
613
+ if (desiredChannels === 1)
614
+ return 'mono';
615
+ if (desiredChannels === 2)
616
+ return 'stereo';
617
+ // For other channel counts, use the mask
618
+ return layout.mask.toString();
619
+ }
620
+ }
621
+ // No exact match, return first supported
622
+ const firstLayout = supportedLayouts[0];
623
+ if (firstLayout.nbChannels === 1)
624
+ return 'mono';
625
+ if (firstLayout.nbChannels === 2)
626
+ return 'stereo';
627
+ return firstLayout.mask.toString();
628
+ }
629
+ }
630
+ //# sourceMappingURL=rtp-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtp-stream.js","sourceRoot":"","sources":["../../src/api/rtp-stream.ts"],"names":[],"mappings":"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"}