@revizly/node-av 5.2.2-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/BUILD_LINUX.md +61 -0
- package/LICENSE.md +22 -0
- package/README.md +662 -0
- package/build_mac_local.sh +69 -0
- package/dist/api/audio-frame-buffer.d.ts +205 -0
- package/dist/api/audio-frame-buffer.js +287 -0
- package/dist/api/audio-frame-buffer.js.map +1 -0
- package/dist/api/bitstream-filter.d.ts +820 -0
- package/dist/api/bitstream-filter.js +1242 -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 +1088 -0
- package/dist/api/decoder.js +1775 -0
- package/dist/api/decoder.js.map +1 -0
- package/dist/api/demuxer.d.ts +1219 -0
- package/dist/api/demuxer.js +2081 -0
- package/dist/api/demuxer.js.map +1 -0
- package/dist/api/device.d.ts +586 -0
- package/dist/api/device.js +961 -0
- package/dist/api/device.js.map +1 -0
- package/dist/api/encoder.d.ts +1132 -0
- package/dist/api/encoder.js +1988 -0
- package/dist/api/encoder.js.map +1 -0
- package/dist/api/filter-complex.d.ts +821 -0
- package/dist/api/filter-complex.js +1604 -0
- package/dist/api/filter-complex.js.map +1 -0
- package/dist/api/filter-presets.d.ts +1286 -0
- package/dist/api/filter-presets.js +2152 -0
- package/dist/api/filter-presets.js.map +1 -0
- package/dist/api/filter.d.ts +1234 -0
- package/dist/api/filter.js +1976 -0
- package/dist/api/filter.js.map +1 -0
- package/dist/api/fmp4-stream.d.ts +426 -0
- package/dist/api/fmp4-stream.js +739 -0
- package/dist/api/fmp4-stream.js.map +1 -0
- package/dist/api/hardware.d.ts +651 -0
- package/dist/api/hardware.js +1260 -0
- package/dist/api/hardware.js.map +1 -0
- package/dist/api/index.d.ts +17 -0
- package/dist/api/index.js +32 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/io-stream.d.ts +307 -0
- package/dist/api/io-stream.js +282 -0
- package/dist/api/io-stream.js.map +1 -0
- package/dist/api/muxer.d.ts +957 -0
- package/dist/api/muxer.js +2002 -0
- package/dist/api/muxer.js.map +1 -0
- package/dist/api/pipeline.d.ts +607 -0
- package/dist/api/pipeline.js +1145 -0
- package/dist/api/pipeline.js.map +1 -0
- package/dist/api/utilities/async-queue.d.ts +120 -0
- package/dist/api/utilities/async-queue.js +211 -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/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/electron-shared-texture.d.ts +328 -0
- package/dist/api/utilities/electron-shared-texture.js +503 -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 +12 -0
- package/dist/api/utilities/index.js +25 -0
- package/dist/api/utilities/index.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/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/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/whisper-model.d.ts +310 -0
- package/dist/api/utilities/whisper-model.js +528 -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/whisper.d.ts +324 -0
- package/dist/api/whisper.js +362 -0
- package/dist/api/whisper.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 +2325 -0
- package/dist/constants/constants.js +1887 -0
- package/dist/constants/constants.js.map +1 -0
- package/dist/constants/decoders.d.ts +633 -0
- package/dist/constants/decoders.js +641 -0
- package/dist/constants/decoders.js.map +1 -0
- package/dist/constants/encoders.d.ts +295 -0
- package/dist/constants/encoders.js +308 -0
- package/dist/constants/encoders.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 +5 -0
- package/dist/constants/index.js +6 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/ffmpeg/index.d.ts +99 -0
- package/dist/ffmpeg/index.js +115 -0
- package/dist/ffmpeg/index.js.map +1 -0
- package/dist/ffmpeg/utils.d.ts +31 -0
- package/dist/ffmpeg/utils.js +68 -0
- package/dist/ffmpeg/utils.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 +228 -0
- package/dist/lib/binding.js +60 -0
- package/dist/lib/binding.js.map +1 -0
- package/dist/lib/bitstream-filter-context.d.ts +379 -0
- package/dist/lib/bitstream-filter-context.js +441 -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 +1071 -0
- package/dist/lib/codec-context.js +1354 -0
- package/dist/lib/codec-context.js.map +1 -0
- package/dist/lib/codec-parameters.d.ts +616 -0
- package/dist/lib/codec-parameters.js +761 -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 +586 -0
- package/dist/lib/codec.js +713 -0
- package/dist/lib/codec.js.map +1 -0
- package/dist/lib/device.d.ts +291 -0
- package/dist/lib/device.js +324 -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 +242 -0
- package/dist/lib/error.js +303 -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 +712 -0
- package/dist/lib/filter-context.js +789 -0
- package/dist/lib/filter-context.js.map +1 -0
- package/dist/lib/filter-graph-segment.d.ts +160 -0
- package/dist/lib/filter-graph-segment.js +171 -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 +198 -0
- package/dist/lib/filter-inout.js +257 -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 +1254 -0
- package/dist/lib/format-context.js +1379 -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 +1222 -0
- package/dist/lib/frame.js +1435 -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 +419 -0
- package/dist/lib/hardware-frames-context.js +477 -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 +696 -0
- package/dist/lib/io-context.js +769 -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 +946 -0
- package/dist/lib/native-types.js +2 -0
- package/dist/lib/native-types.js.map +1 -0
- package/dist/lib/option.d.ts +927 -0
- package/dist/lib/option.js +1583 -0
- package/dist/lib/option.js.map +1 -0
- package/dist/lib/output-format.d.ts +180 -0
- package/dist/lib/output-format.js +213 -0
- package/dist/lib/output-format.js.map +1 -0
- package/dist/lib/packet.d.ts +501 -0
- package/dist/lib/packet.js +590 -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 +379 -0
- package/dist/lib/stream.js +526 -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 +1127 -0
- package/dist/lib/utilities.js +1225 -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 +4 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/install/check.js +121 -0
- package/install/ffmpeg.js +66 -0
- package/jellyfin-ffmpeg.patch +181 -0
- package/package.json +129 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { FormatContext } from '../../lib/format-context.js';
|
|
2
|
+
import type { AVCodecID } from '../../constants/index.js';
|
|
3
|
+
import type { Muxer } from '../muxer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Streaming protocol utilities.
|
|
6
|
+
*
|
|
7
|
+
* Provides static methods for SDP generation, RTP URL building, and
|
|
8
|
+
* network streaming helpers for RTP/RTSP protocols.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { StreamingUtils, Muxer } from 'node-av/api';
|
|
13
|
+
*
|
|
14
|
+
* // Create RTP outputs
|
|
15
|
+
* const videoOutput = await Muxer.open('rtp://127.0.0.1:5004');
|
|
16
|
+
* const audioOutput = await Muxer.open('rtp://127.0.0.1:5006');
|
|
17
|
+
*
|
|
18
|
+
* // Generate SDP for streaming
|
|
19
|
+
* const sdp = StreamingUtils.createSdp([videoOutput, audioOutput]);
|
|
20
|
+
* if (sdp) {
|
|
21
|
+
* console.log('SDP for streaming:', sdp);
|
|
22
|
+
* // Save to .sdp file or serve via RTSP server
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class StreamingUtils {
|
|
27
|
+
/**
|
|
28
|
+
* Create an SDP (Session Description Protocol) string from demuxer/muxer
|
|
29
|
+
*
|
|
30
|
+
* Generates an SDP description for RTP/RTSP streaming from one or more
|
|
31
|
+
* configured demuxer/muxer. The inputs/outputs should be configured with RTP
|
|
32
|
+
* format and have their streams set up before calling this method.
|
|
33
|
+
*
|
|
34
|
+
* @param contexts - Alternatively, array of FormatContext objects
|
|
35
|
+
*
|
|
36
|
+
* @returns SDP string if successful, null if failed
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Set up RTP outputs with streams
|
|
41
|
+
* const output1 = await Muxer.open('rtp://239.0.0.1:5004');
|
|
42
|
+
* await output1.addStream(encoder1);
|
|
43
|
+
*
|
|
44
|
+
* const output2 = await Muxer.open('rtp://239.0.0.1:5006');
|
|
45
|
+
* await output2.addStream(encoder2);
|
|
46
|
+
*
|
|
47
|
+
* // Generate SDP for multicast streaming
|
|
48
|
+
* const sdp = StreamingUtils.createSdp([output1.getFormatContext(), output2.getFormatContext()]);
|
|
49
|
+
* if (sdp) {
|
|
50
|
+
* // Write to file for VLC or other players
|
|
51
|
+
* await fs.writeFile('stream.sdp', sdp);
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
static createSdp(contexts: FormatContext[]): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Generate SDP for RTP/SRTP input using FFmpeg's native SDP generator
|
|
58
|
+
*
|
|
59
|
+
* Creates an RFC-compliant SDP description using FFmpeg's internal logic.
|
|
60
|
+
* This ensures correct codec names, clock rates, and formatting for all codecs.
|
|
61
|
+
*
|
|
62
|
+
* @param configs - Array of stream configurations
|
|
63
|
+
*
|
|
64
|
+
* @param sessionName - Optional session name for the SDP (default: 'RTP Stream')
|
|
65
|
+
*
|
|
66
|
+
* @returns SDP string with proper rtpmap and optional crypto attributes
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* import { StreamingUtils } from 'node-av/api';
|
|
71
|
+
* import { AV_CODEC_ID_OPUS, AV_CODEC_ID_H264 } from 'node-av/constants';
|
|
72
|
+
*
|
|
73
|
+
* // Single audio stream with SRTP
|
|
74
|
+
* const sdp = StreamingUtils.createInputSDP([{
|
|
75
|
+
* port: 5004,
|
|
76
|
+
* codecId: AV_CODEC_ID_OPUS,
|
|
77
|
+
* payloadType: 111,
|
|
78
|
+
* clockRate: 48000,
|
|
79
|
+
* channels: 2,
|
|
80
|
+
* srtp: {
|
|
81
|
+
* key: Buffer.alloc(16, 0x12),
|
|
82
|
+
* salt: Buffer.alloc(14, 0x34)
|
|
83
|
+
* }
|
|
84
|
+
* }]);
|
|
85
|
+
*
|
|
86
|
+
* // Multi-stream (video + audio)
|
|
87
|
+
* const sdp = StreamingUtils.createInputSDP([
|
|
88
|
+
* { port: 5006, codecId: AV_CODEC_ID_H264, payloadType: 96, clockRate: 90000 },
|
|
89
|
+
* { port: 5004, codecId: AV_CODEC_ID_OPUS, payloadType: 111, clockRate: 48000, channels: 2 }
|
|
90
|
+
* ], 'My Stream');
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
static createInputSDP(configs: {
|
|
94
|
+
port: number;
|
|
95
|
+
codecId: AVCodecID;
|
|
96
|
+
payloadType: number;
|
|
97
|
+
clockRate: number;
|
|
98
|
+
channels?: number;
|
|
99
|
+
fmtp?: string;
|
|
100
|
+
srtp?: {
|
|
101
|
+
key: Buffer;
|
|
102
|
+
salt: Buffer;
|
|
103
|
+
suite?: 'AES_CM_128_HMAC_SHA1_80' | 'AES_CM_256_HMAC_SHA1_80';
|
|
104
|
+
};
|
|
105
|
+
}[], sessionName?: string): string;
|
|
106
|
+
/**
|
|
107
|
+
* Validate if an output is configured for RTP streaming
|
|
108
|
+
*
|
|
109
|
+
* @param output - Muxer to check
|
|
110
|
+
*
|
|
111
|
+
* @returns true if configured for RTP
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const output = await Muxer.open('rtp://127.0.0.1:5004');
|
|
116
|
+
* if (StreamingUtils.isRtpOutput(output)) {
|
|
117
|
+
* const sdp = StreamingUtils.createSdpForOutput(output);
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
static isRtpOutput(output: Muxer): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Build RTP URL from components
|
|
124
|
+
*
|
|
125
|
+
* Helper to construct RTP URLs with proper formatting.
|
|
126
|
+
*
|
|
127
|
+
* @param host - IP address or hostname
|
|
128
|
+
*
|
|
129
|
+
* @param port - Port number
|
|
130
|
+
*
|
|
131
|
+
* @param options - Additional options
|
|
132
|
+
*
|
|
133
|
+
* @param options.ttl - Time-to-live for multicast
|
|
134
|
+
*
|
|
135
|
+
* @param options.localrtpport - Local RTP port
|
|
136
|
+
*
|
|
137
|
+
* @param options.localrtcpport - Local RTCP port
|
|
138
|
+
*
|
|
139
|
+
* @param options.pkt_size - Packet size
|
|
140
|
+
*
|
|
141
|
+
* @returns Formatted RTP URL
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* // Unicast
|
|
146
|
+
* const url1 = StreamingUtils.buildRtpUrl('127.0.0.1', 5004);
|
|
147
|
+
* // 'rtp://127.0.0.1:5004'
|
|
148
|
+
*
|
|
149
|
+
* // Multicast
|
|
150
|
+
* const url2 = StreamingUtils.buildRtpUrl('239.0.0.1', 5004, { ttl: 64 });
|
|
151
|
+
* // 'rtp://239.0.0.1:5004?ttl=64'
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
static buildRtpUrl(host: string, port: number, options?: {
|
|
155
|
+
ttl?: number;
|
|
156
|
+
localrtpport?: number;
|
|
157
|
+
localrtcpport?: number;
|
|
158
|
+
pkt_size?: number;
|
|
159
|
+
}): string;
|
|
160
|
+
/**
|
|
161
|
+
* Extract all UDP ports from SDP content
|
|
162
|
+
*
|
|
163
|
+
* @param sdp - SDP content string
|
|
164
|
+
*
|
|
165
|
+
* @returns Array of port numbers (one per stream)
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* import { StreamingUtils } from 'node-av/api';
|
|
170
|
+
*
|
|
171
|
+
* const sdp = `v=0
|
|
172
|
+
* o=- 0 0 IN IP4 127.0.0.1
|
|
173
|
+
* s=Test Stream
|
|
174
|
+
* c=IN IP4 127.0.0.1
|
|
175
|
+
* t=0 0
|
|
176
|
+
* m=audio 5004 RTP/AVP 111
|
|
177
|
+
* a=rtpmap:111 OPUS/48000/2
|
|
178
|
+
* m=video 5006 RTP/AVP 96
|
|
179
|
+
* a=rtpmap:96 H264/90000`;
|
|
180
|
+
*
|
|
181
|
+
* const ports = StreamingUtils.extractPortsFromSDP(sdp);
|
|
182
|
+
* console.log(ports); // [5004, 5006]
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
static extractPortsFromSDP(sdp: string): number[];
|
|
186
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { AVMEDIA_TYPE_VIDEO } from '../../constants/constants.js';
|
|
2
|
+
import { Codec } from '../../lib/codec.js';
|
|
3
|
+
import { FormatContext } from '../../lib/format-context.js';
|
|
4
|
+
import { Rational } from '../../lib/rational.js';
|
|
5
|
+
import { avSdpCreate } from '../../lib/utilities.js';
|
|
6
|
+
/**
|
|
7
|
+
* Streaming protocol utilities.
|
|
8
|
+
*
|
|
9
|
+
* Provides static methods for SDP generation, RTP URL building, and
|
|
10
|
+
* network streaming helpers for RTP/RTSP protocols.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { StreamingUtils, Muxer } from 'node-av/api';
|
|
15
|
+
*
|
|
16
|
+
* // Create RTP outputs
|
|
17
|
+
* const videoOutput = await Muxer.open('rtp://127.0.0.1:5004');
|
|
18
|
+
* const audioOutput = await Muxer.open('rtp://127.0.0.1:5006');
|
|
19
|
+
*
|
|
20
|
+
* // Generate SDP for streaming
|
|
21
|
+
* const sdp = StreamingUtils.createSdp([videoOutput, audioOutput]);
|
|
22
|
+
* if (sdp) {
|
|
23
|
+
* console.log('SDP for streaming:', sdp);
|
|
24
|
+
* // Save to .sdp file or serve via RTSP server
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class StreamingUtils {
|
|
29
|
+
/**
|
|
30
|
+
* Create an SDP (Session Description Protocol) string from demuxer/muxer
|
|
31
|
+
*
|
|
32
|
+
* Generates an SDP description for RTP/RTSP streaming from one or more
|
|
33
|
+
* configured demuxer/muxer. The inputs/outputs should be configured with RTP
|
|
34
|
+
* format and have their streams set up before calling this method.
|
|
35
|
+
*
|
|
36
|
+
* @param contexts - Alternatively, array of FormatContext objects
|
|
37
|
+
*
|
|
38
|
+
* @returns SDP string if successful, null if failed
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Set up RTP outputs with streams
|
|
43
|
+
* const output1 = await Muxer.open('rtp://239.0.0.1:5004');
|
|
44
|
+
* await output1.addStream(encoder1);
|
|
45
|
+
*
|
|
46
|
+
* const output2 = await Muxer.open('rtp://239.0.0.1:5006');
|
|
47
|
+
* await output2.addStream(encoder2);
|
|
48
|
+
*
|
|
49
|
+
* // Generate SDP for multicast streaming
|
|
50
|
+
* const sdp = StreamingUtils.createSdp([output1.getFormatContext(), output2.getFormatContext()]);
|
|
51
|
+
* if (sdp) {
|
|
52
|
+
* // Write to file for VLC or other players
|
|
53
|
+
* await fs.writeFile('stream.sdp', sdp);
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
static createSdp(contexts) {
|
|
58
|
+
if (contexts?.length === 0) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return avSdpCreate(contexts);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate SDP for RTP/SRTP input using FFmpeg's native SDP generator
|
|
65
|
+
*
|
|
66
|
+
* Creates an RFC-compliant SDP description using FFmpeg's internal logic.
|
|
67
|
+
* This ensures correct codec names, clock rates, and formatting for all codecs.
|
|
68
|
+
*
|
|
69
|
+
* @param configs - Array of stream configurations
|
|
70
|
+
*
|
|
71
|
+
* @param sessionName - Optional session name for the SDP (default: 'RTP Stream')
|
|
72
|
+
*
|
|
73
|
+
* @returns SDP string with proper rtpmap and optional crypto attributes
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* import { StreamingUtils } from 'node-av/api';
|
|
78
|
+
* import { AV_CODEC_ID_OPUS, AV_CODEC_ID_H264 } from 'node-av/constants';
|
|
79
|
+
*
|
|
80
|
+
* // Single audio stream with SRTP
|
|
81
|
+
* const sdp = StreamingUtils.createInputSDP([{
|
|
82
|
+
* port: 5004,
|
|
83
|
+
* codecId: AV_CODEC_ID_OPUS,
|
|
84
|
+
* payloadType: 111,
|
|
85
|
+
* clockRate: 48000,
|
|
86
|
+
* channels: 2,
|
|
87
|
+
* srtp: {
|
|
88
|
+
* key: Buffer.alloc(16, 0x12),
|
|
89
|
+
* salt: Buffer.alloc(14, 0x34)
|
|
90
|
+
* }
|
|
91
|
+
* }]);
|
|
92
|
+
*
|
|
93
|
+
* // Multi-stream (video + audio)
|
|
94
|
+
* const sdp = StreamingUtils.createInputSDP([
|
|
95
|
+
* { port: 5006, codecId: AV_CODEC_ID_H264, payloadType: 96, clockRate: 90000 },
|
|
96
|
+
* { port: 5004, codecId: AV_CODEC_ID_OPUS, payloadType: 111, clockRate: 48000, channels: 2 }
|
|
97
|
+
* ], 'My Stream');
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
static createInputSDP(configs, sessionName = 'RTP Stream') {
|
|
101
|
+
const contexts = [];
|
|
102
|
+
try {
|
|
103
|
+
// Create one FormatContext per stream with individual URL (port)
|
|
104
|
+
for (const streamConfig of configs) {
|
|
105
|
+
const codec = Codec.findDecoder(streamConfig.codecId);
|
|
106
|
+
if (!codec) {
|
|
107
|
+
throw new Error(`Codec not found for codec ID: ${streamConfig.codecId}`);
|
|
108
|
+
}
|
|
109
|
+
// Create format context with URL containing the port
|
|
110
|
+
const ctx = new FormatContext();
|
|
111
|
+
ctx.allocContext();
|
|
112
|
+
ctx.url = `rtp://127.0.0.1:${streamConfig.port}`;
|
|
113
|
+
// Create stream with codec parameters
|
|
114
|
+
const stream = ctx.newStream(null);
|
|
115
|
+
stream.codecpar.codecId = streamConfig.codecId;
|
|
116
|
+
stream.codecpar.codecType = codec.type;
|
|
117
|
+
// Set audio-specific parameters
|
|
118
|
+
if (codec.type !== AVMEDIA_TYPE_VIDEO) {
|
|
119
|
+
if (streamConfig.clockRate) {
|
|
120
|
+
stream.codecpar.sampleRate = streamConfig.clockRate;
|
|
121
|
+
}
|
|
122
|
+
if (streamConfig.channels) {
|
|
123
|
+
stream.codecpar.channels = streamConfig.channels;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Set time base (required for SDP generation)
|
|
127
|
+
stream.timeBase = new Rational(1, streamConfig.clockRate);
|
|
128
|
+
contexts.push(ctx);
|
|
129
|
+
}
|
|
130
|
+
// Generate SDP using FFmpeg's native generator
|
|
131
|
+
// FFmpeg will automatically use the port from each context's URL and generate RFC-compliant codec names
|
|
132
|
+
let sdp = avSdpCreate(contexts);
|
|
133
|
+
if (!sdp) {
|
|
134
|
+
throw new Error('Failed to generate SDP');
|
|
135
|
+
}
|
|
136
|
+
// Post-process: Replace session name if provided
|
|
137
|
+
if (sessionName !== 'RTP Stream') {
|
|
138
|
+
sdp = sdp.replace(/^s=.*$/m, `s=${sessionName}`);
|
|
139
|
+
}
|
|
140
|
+
// Post-process: Replace payload types, add SRTP crypto lines, and/or custom fmtp
|
|
141
|
+
// Note: We always need to process because FFmpeg assigns payload types automatically
|
|
142
|
+
const sdpLines = sdp.split('\n');
|
|
143
|
+
const newLines = [];
|
|
144
|
+
let streamIndex = -1;
|
|
145
|
+
for (let i = 0; i < sdpLines.length; i++) {
|
|
146
|
+
const line = sdpLines[i];
|
|
147
|
+
if (line.startsWith('m=')) {
|
|
148
|
+
// New media section
|
|
149
|
+
streamIndex++;
|
|
150
|
+
const streamCfg = configs[streamIndex];
|
|
151
|
+
// Replace FFmpeg's auto-assigned payload type with user-specified one
|
|
152
|
+
const replaced = line.replace(/RTP\/AVP\s+(\d+)/, `RTP/AVP ${streamCfg.payloadType}`);
|
|
153
|
+
newLines.push(replaced);
|
|
154
|
+
// Add SRTP crypto line right after m= line if configured
|
|
155
|
+
if (streamCfg?.srtp) {
|
|
156
|
+
const suite = streamCfg.srtp.suite ?? 'AES_CM_128_HMAC_SHA1_80';
|
|
157
|
+
const keyMaterial = Buffer.concat([streamCfg.srtp.key, streamCfg.srtp.salt]).toString('base64');
|
|
158
|
+
newLines.push(`a=crypto:1 ${suite} inline:${keyMaterial}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (line.startsWith('a=rtpmap:')) {
|
|
162
|
+
// Replace payload type in rtpmap line
|
|
163
|
+
const streamCfg = configs[streamIndex];
|
|
164
|
+
const replaced = line.replace(/^a=rtpmap:(\d+)/, `a=rtpmap:${streamCfg.payloadType}`);
|
|
165
|
+
newLines.push(replaced);
|
|
166
|
+
// If custom fmtp is provided but FFmpeg didn't generate one, add it
|
|
167
|
+
if (streamCfg?.fmtp && !sdpLines[i + 1]?.startsWith('a=fmtp:')) {
|
|
168
|
+
newLines.push(`a=fmtp:${streamCfg.payloadType} ${streamCfg.fmtp}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else if (line.startsWith('a=fmtp:')) {
|
|
172
|
+
// Replace payload type in fmtp line and optionally replace content
|
|
173
|
+
const streamCfg = configs[streamIndex];
|
|
174
|
+
if (streamCfg?.fmtp) {
|
|
175
|
+
newLines.push(`a=fmtp:${streamCfg.payloadType} ${streamCfg.fmtp}`);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const replaced = line.replace(/^a=fmtp:(\d+)/, `a=fmtp:${streamCfg.payloadType}`);
|
|
179
|
+
newLines.push(replaced);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
newLines.push(line);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return newLines.join('\n');
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
// Cleanup all contexts
|
|
190
|
+
for (const ctx of contexts) {
|
|
191
|
+
ctx.freeContext();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Validate if an output is configured for RTP streaming
|
|
197
|
+
*
|
|
198
|
+
* @param output - Muxer to check
|
|
199
|
+
*
|
|
200
|
+
* @returns true if configured for RTP
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const output = await Muxer.open('rtp://127.0.0.1:5004');
|
|
205
|
+
* if (StreamingUtils.isRtpOutput(output)) {
|
|
206
|
+
* const sdp = StreamingUtils.createSdpForOutput(output);
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
static isRtpOutput(output) {
|
|
211
|
+
// Check if the output format is RTP
|
|
212
|
+
const formatContext = output.getFormatContext();
|
|
213
|
+
const oformat = formatContext?.oformat;
|
|
214
|
+
if (oformat) {
|
|
215
|
+
const name = oformat.name;
|
|
216
|
+
return name === 'rtp' || name === 'rtp_mpegts';
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Build RTP URL from components
|
|
222
|
+
*
|
|
223
|
+
* Helper to construct RTP URLs with proper formatting.
|
|
224
|
+
*
|
|
225
|
+
* @param host - IP address or hostname
|
|
226
|
+
*
|
|
227
|
+
* @param port - Port number
|
|
228
|
+
*
|
|
229
|
+
* @param options - Additional options
|
|
230
|
+
*
|
|
231
|
+
* @param options.ttl - Time-to-live for multicast
|
|
232
|
+
*
|
|
233
|
+
* @param options.localrtpport - Local RTP port
|
|
234
|
+
*
|
|
235
|
+
* @param options.localrtcpport - Local RTCP port
|
|
236
|
+
*
|
|
237
|
+
* @param options.pkt_size - Packet size
|
|
238
|
+
*
|
|
239
|
+
* @returns Formatted RTP URL
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* // Unicast
|
|
244
|
+
* const url1 = StreamingUtils.buildRtpUrl('127.0.0.1', 5004);
|
|
245
|
+
* // 'rtp://127.0.0.1:5004'
|
|
246
|
+
*
|
|
247
|
+
* // Multicast
|
|
248
|
+
* const url2 = StreamingUtils.buildRtpUrl('239.0.0.1', 5004, { ttl: 64 });
|
|
249
|
+
* // 'rtp://239.0.0.1:5004?ttl=64'
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
static buildRtpUrl(host, port, options) {
|
|
253
|
+
let url = `rtp://${host}:${port}`;
|
|
254
|
+
if (options && Object.keys(options).length > 0) {
|
|
255
|
+
const params = new URLSearchParams();
|
|
256
|
+
for (const [key, value] of Object.entries(options)) {
|
|
257
|
+
if (value !== undefined) {
|
|
258
|
+
params.append(key, String(value));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const queryString = params.toString();
|
|
262
|
+
if (queryString) {
|
|
263
|
+
url += `?${queryString}`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return url;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Extract all UDP ports from SDP content
|
|
270
|
+
*
|
|
271
|
+
* @param sdp - SDP content string
|
|
272
|
+
*
|
|
273
|
+
* @returns Array of port numbers (one per stream)
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```typescript
|
|
277
|
+
* import { StreamingUtils } from 'node-av/api';
|
|
278
|
+
*
|
|
279
|
+
* const sdp = `v=0
|
|
280
|
+
* o=- 0 0 IN IP4 127.0.0.1
|
|
281
|
+
* s=Test Stream
|
|
282
|
+
* c=IN IP4 127.0.0.1
|
|
283
|
+
* t=0 0
|
|
284
|
+
* m=audio 5004 RTP/AVP 111
|
|
285
|
+
* a=rtpmap:111 OPUS/48000/2
|
|
286
|
+
* m=video 5006 RTP/AVP 96
|
|
287
|
+
* a=rtpmap:96 H264/90000`;
|
|
288
|
+
*
|
|
289
|
+
* const ports = StreamingUtils.extractPortsFromSDP(sdp);
|
|
290
|
+
* console.log(ports); // [5004, 5006]
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
static extractPortsFromSDP(sdp) {
|
|
294
|
+
const ports = [];
|
|
295
|
+
const lines = sdp.split('\n');
|
|
296
|
+
for (const line of lines) {
|
|
297
|
+
if (line.startsWith('m=')) {
|
|
298
|
+
// m=audio 5004 RTP/AVP 111
|
|
299
|
+
// m=video 5006 RTP/AVP 96
|
|
300
|
+
const match = /m=\w+\s+(\d+)/.exec(line);
|
|
301
|
+
if (match) {
|
|
302
|
+
ports.push(parseInt(match[1], 10));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return ports;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=streaming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../../src/api/utilities/streaming.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { AVRounding } from '../../constants/index.js';
|
|
2
|
+
import type { IRational } from '../../lib/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Timestamp and time base utilities.
|
|
5
|
+
*
|
|
6
|
+
* Provides static methods for converting, rescaling, and comparing timestamps.
|
|
7
|
+
* These utilities are essential for working with different time bases in
|
|
8
|
+
* multimedia streams.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { TimestampUtils } from 'node-av';
|
|
13
|
+
* import { AV_ROUND_NEAR_INF } from 'node-av/constants';
|
|
14
|
+
*
|
|
15
|
+
* // Convert timestamp to string representations
|
|
16
|
+
* const pts = 450000n;
|
|
17
|
+
* console.log(TimestampUtils.toString(pts)); // "450000"
|
|
18
|
+
*
|
|
19
|
+
* const timebase = { num: 1, den: 90000 };
|
|
20
|
+
* console.log(TimestampUtils.toTimeString(pts, timebase)); // "5.000000"
|
|
21
|
+
*
|
|
22
|
+
* // Rescale between time bases
|
|
23
|
+
* const srcTb = { num: 1, den: 90000 };
|
|
24
|
+
* const dstTb = { num: 1, den: 1000 };
|
|
25
|
+
* const rescaled = TimestampUtils.rescale(pts, srcTb, dstTb);
|
|
26
|
+
* console.log(rescaled); // 5000n
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class TimestampUtils {
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Convert timestamp to string.
|
|
33
|
+
*
|
|
34
|
+
* Converts a timestamp value to its string representation.
|
|
35
|
+
* Handles special values like AV_NOPTS_VALUE.
|
|
36
|
+
* Direct mapping to av_ts2str()
|
|
37
|
+
*
|
|
38
|
+
* @param ts - Timestamp value (bigint or number), or null
|
|
39
|
+
*
|
|
40
|
+
* @returns String representation
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { TimestampUtils } from 'node-av';
|
|
45
|
+
* import { AV_NOPTS_VALUE } from 'node-av/constants';
|
|
46
|
+
*
|
|
47
|
+
* console.log(TimestampUtils.toString(12345n)); // "12345"
|
|
48
|
+
* console.log(TimestampUtils.toString(AV_NOPTS_VALUE)); // "NOPTS"
|
|
49
|
+
* console.log(TimestampUtils.toString(null)); // "NOPTS"
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
static toString(ts: bigint | number | null): string;
|
|
53
|
+
/**
|
|
54
|
+
* Convert timestamp to time string.
|
|
55
|
+
*
|
|
56
|
+
* Converts a timestamp to a time string in seconds using the provided time base.
|
|
57
|
+
* Direct mapping to av_ts2timestr()
|
|
58
|
+
*
|
|
59
|
+
* @param ts - Timestamp value
|
|
60
|
+
*
|
|
61
|
+
* @param timeBase - Time base for conversion
|
|
62
|
+
*
|
|
63
|
+
* @returns Time string in seconds with decimal places
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* import { TimestampUtils } from 'node-av';
|
|
68
|
+
*
|
|
69
|
+
* const pts = 450000n;
|
|
70
|
+
* const timebase = { num: 1, den: 90000 }; // 90kHz
|
|
71
|
+
*
|
|
72
|
+
* console.log(TimestampUtils.toTimeString(pts, timebase)); // "5.000000"
|
|
73
|
+
* console.log(TimestampUtils.toTimeString(90000n, timebase)); // "1.000000"
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
static toTimeString(ts: bigint | number | null, timeBase: IRational): string;
|
|
77
|
+
/**
|
|
78
|
+
* Compare timestamps from different time bases.
|
|
79
|
+
*
|
|
80
|
+
* Compares two timestamps that may have different time bases.
|
|
81
|
+
* Direct mapping to av_compare_ts()
|
|
82
|
+
*
|
|
83
|
+
* @param tsA - First timestamp
|
|
84
|
+
*
|
|
85
|
+
* @param tbA - Time base of first timestamp
|
|
86
|
+
*
|
|
87
|
+
* @param tsB - Second timestamp
|
|
88
|
+
*
|
|
89
|
+
* @param tbB - Time base of second timestamp
|
|
90
|
+
*
|
|
91
|
+
* @returns -1 if tsA < tsB, 0 if equal, 1 if tsA > tsB
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* import { TimestampUtils } from 'node-av';
|
|
96
|
+
*
|
|
97
|
+
* // Compare timestamps from different time bases
|
|
98
|
+
* const pts1 = 90000n;
|
|
99
|
+
* const tb1 = { num: 1, den: 90000 }; // 1 second in 90kHz
|
|
100
|
+
*
|
|
101
|
+
* const pts2 = 1000n;
|
|
102
|
+
* const tb2 = { num: 1, den: 1000 }; // 1 second in 1kHz
|
|
103
|
+
*
|
|
104
|
+
* const result = TimestampUtils.compare(pts1, tb1, pts2, tb2);
|
|
105
|
+
* console.log(result); // 0 (equal - both represent 1 second)
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
static compare(tsA: bigint | number | null, tbA: IRational, tsB: bigint | number | null, tbB: IRational): number;
|
|
109
|
+
/**
|
|
110
|
+
* Rescale timestamp from one time base to another.
|
|
111
|
+
*
|
|
112
|
+
* Converts a timestamp from source time base to destination time base.
|
|
113
|
+
* Uses AV_ROUND_NEAR_INF rounding.
|
|
114
|
+
* Direct mapping to av_rescale_q()
|
|
115
|
+
*
|
|
116
|
+
* @param a - Timestamp to rescale
|
|
117
|
+
*
|
|
118
|
+
* @param bq - Source time base
|
|
119
|
+
*
|
|
120
|
+
* @param cq - Destination time base
|
|
121
|
+
*
|
|
122
|
+
* @returns Rescaled timestamp
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* import { TimestampUtils } from 'node-av';
|
|
127
|
+
*
|
|
128
|
+
* // Convert from 90kHz to milliseconds
|
|
129
|
+
* const pts = 450000n;
|
|
130
|
+
* const srcTb = { num: 1, den: 90000 };
|
|
131
|
+
* const dstTb = { num: 1, den: 1000 };
|
|
132
|
+
*
|
|
133
|
+
* const rescaled = TimestampUtils.rescale(pts, srcTb, dstTb);
|
|
134
|
+
* console.log(rescaled); // 5000n (5000 milliseconds = 5 seconds)
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
static rescale(a: bigint | number | null, bq: IRational, cq: IRational): bigint;
|
|
138
|
+
/**
|
|
139
|
+
* Rescale with specified rounding.
|
|
140
|
+
*
|
|
141
|
+
* Rescales a value with explicit rounding mode.
|
|
142
|
+
* More general than rescale() as it doesn't use time bases.
|
|
143
|
+
* Direct mapping to av_rescale_rnd()
|
|
144
|
+
*
|
|
145
|
+
* @param a - Value to rescale
|
|
146
|
+
*
|
|
147
|
+
* @param b - Multiplier
|
|
148
|
+
*
|
|
149
|
+
* @param c - Divisor
|
|
150
|
+
*
|
|
151
|
+
* @param rnd - Rounding mode
|
|
152
|
+
*
|
|
153
|
+
* @returns Rescaled value: a * b / c
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* import { TimestampUtils } from 'node-av';
|
|
158
|
+
* import { AV_ROUND_UP, AV_ROUND_DOWN } from 'node-av/constants';
|
|
159
|
+
*
|
|
160
|
+
* // Scale with different rounding modes
|
|
161
|
+
* const value = 100n;
|
|
162
|
+
* const mul = 3n;
|
|
163
|
+
* const div = 7n;
|
|
164
|
+
*
|
|
165
|
+
* const roundUp = TimestampUtils.rescaleRounded(value, mul, div, AV_ROUND_UP);
|
|
166
|
+
* const roundDown = TimestampUtils.rescaleRounded(value, mul, div, AV_ROUND_DOWN);
|
|
167
|
+
*
|
|
168
|
+
* console.log(roundUp); // 43n (rounds up)
|
|
169
|
+
* console.log(roundDown); // 42n (rounds down)
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
static rescaleRounded(a: bigint | number, b: bigint | number, c: bigint | number, rnd: AVRounding): bigint;
|
|
173
|
+
/**
|
|
174
|
+
* Sleep for specified microseconds.
|
|
175
|
+
*
|
|
176
|
+
* Sleeps for the specified number of microseconds.
|
|
177
|
+
* Direct mapping to av_usleep()
|
|
178
|
+
*
|
|
179
|
+
* @param usec - Microseconds to sleep
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* import { TimestampUtils } from 'node-av';
|
|
184
|
+
*
|
|
185
|
+
* // Sleep for 100 milliseconds
|
|
186
|
+
* TimestampUtils.sleep(100000);
|
|
187
|
+
*
|
|
188
|
+
* // Sleep for 1 second
|
|
189
|
+
* TimestampUtils.sleep(1000000);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
static sleep(usec: number): void;
|
|
193
|
+
}
|