node-av 3.1.2 → 4.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.
- package/README.md +65 -52
- package/binding.gyp +4 -0
- package/dist/api/audio-frame-buffer.d.ts +201 -0
- package/dist/api/audio-frame-buffer.js +275 -0
- package/dist/api/audio-frame-buffer.js.map +1 -0
- package/dist/api/bitstream-filter.d.ts +319 -78
- package/dist/api/bitstream-filter.js +680 -151
- package/dist/api/bitstream-filter.js.map +1 -1
- 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_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 +279 -17
- package/dist/api/decoder.js +998 -209
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/{media-input.d.ts → demuxer.d.ts} +294 -44
- package/dist/api/demuxer.js +1968 -0
- package/dist/api/demuxer.js.map +1 -0
- package/dist/api/encoder.d.ts +308 -50
- package/dist/api/encoder.js +1133 -111
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-presets.d.ts +12 -5
- package/dist/api/filter-presets.js +21 -7
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.d.ts +406 -40
- package/dist/api/filter.js +966 -139
- package/dist/api/filter.js.map +1 -1
- package/dist/api/{fmp4.d.ts → fmp4-stream.d.ts} +141 -140
- package/dist/api/fmp4-stream.js +539 -0
- package/dist/api/fmp4-stream.js.map +1 -0
- package/dist/api/hardware.d.ts +58 -6
- package/dist/api/hardware.js +127 -11
- package/dist/api/hardware.js.map +1 -1
- package/dist/api/index.d.ts +6 -4
- package/dist/api/index.js +14 -8
- package/dist/api/index.js.map +1 -1
- package/dist/api/io-stream.d.ts +3 -3
- package/dist/api/io-stream.js +5 -4
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/{media-output.d.ts → muxer.d.ts} +274 -60
- package/dist/api/muxer.js +1934 -0
- package/dist/api/muxer.js.map +1 -0
- package/dist/api/pipeline.d.ts +77 -29
- package/dist/api/pipeline.js +435 -425
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/rtp-stream.d.ts +312 -0
- package/dist/api/rtp-stream.js +630 -0
- package/dist/api/rtp-stream.js.map +1 -0
- package/dist/api/types.d.ts +476 -55
- package/dist/api/utilities/async-queue.d.ts +91 -0
- package/dist/api/utilities/async-queue.js +162 -0
- package/dist/api/utilities/async-queue.js.map +1 -0
- package/dist/api/utilities/audio-sample.d.ts +1 -1
- package/dist/api/utilities/image.d.ts +1 -1
- package/dist/api/utilities/index.d.ts +2 -0
- package/dist/api/utilities/index.js +4 -0
- package/dist/api/utilities/index.js.map +1 -1
- package/dist/api/utilities/media-type.d.ts +1 -1
- package/dist/api/utilities/pixel-format.d.ts +1 -1
- package/dist/api/utilities/sample-format.d.ts +1 -1
- package/dist/api/utilities/scheduler.d.ts +169 -0
- package/dist/api/utilities/scheduler.js +136 -0
- package/dist/api/utilities/scheduler.js.map +1 -0
- package/dist/api/utilities/streaming.d.ts +74 -15
- package/dist/api/utilities/streaming.js +170 -12
- package/dist/api/utilities/streaming.js.map +1 -1
- package/dist/api/utilities/timestamp.d.ts +1 -1
- package/dist/api/webrtc-stream.d.ts +288 -0
- package/dist/api/webrtc-stream.js +440 -0
- package/dist/api/webrtc-stream.js.map +1 -0
- package/dist/constants/constants.d.ts +51 -1
- package/dist/constants/constants.js +47 -1
- package/dist/constants/constants.js.map +1 -1
- package/dist/constants/encoders.d.ts +2 -1
- package/dist/constants/encoders.js +4 -3
- package/dist/constants/encoders.js.map +1 -1
- 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 +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/lib/binding.d.ts +19 -8
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/codec-context.d.ts +87 -0
- package/dist/lib/codec-context.js +125 -4
- package/dist/lib/codec-context.js.map +1 -1
- package/dist/lib/codec-parameters.d.ts +183 -1
- package/dist/lib/codec-parameters.js +209 -0
- package/dist/lib/codec-parameters.js.map +1 -1
- package/dist/lib/codec-parser.d.ts +23 -0
- package/dist/lib/codec-parser.js +25 -0
- package/dist/lib/codec-parser.js.map +1 -1
- package/dist/lib/codec.d.ts +26 -4
- package/dist/lib/codec.js +35 -0
- package/dist/lib/codec.js.map +1 -1
- package/dist/lib/dictionary.js +1 -0
- package/dist/lib/dictionary.js.map +1 -1
- package/dist/lib/error.js +1 -1
- package/dist/lib/error.js.map +1 -1
- package/dist/lib/filter-context.d.ts +52 -11
- package/dist/lib/filter-context.js +56 -12
- package/dist/lib/filter-context.js.map +1 -1
- package/dist/lib/filter-graph.d.ts +9 -0
- package/dist/lib/filter-graph.js +13 -0
- package/dist/lib/filter-graph.js.map +1 -1
- package/dist/lib/filter.d.ts +21 -0
- package/dist/lib/filter.js +28 -0
- package/dist/lib/filter.js.map +1 -1
- package/dist/lib/format-context.d.ts +48 -14
- package/dist/lib/format-context.js +76 -7
- package/dist/lib/format-context.js.map +1 -1
- package/dist/lib/frame.d.ts +168 -0
- package/dist/lib/frame.js +212 -0
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/hardware-device-context.d.ts +3 -2
- package/dist/lib/hardware-device-context.js.map +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/input-format.d.ts +21 -0
- package/dist/lib/input-format.js +42 -2
- package/dist/lib/input-format.js.map +1 -1
- package/dist/lib/native-types.d.ts +48 -26
- package/dist/lib/option.d.ts +25 -13
- package/dist/lib/option.js +28 -0
- package/dist/lib/option.js.map +1 -1
- package/dist/lib/output-format.d.ts +22 -1
- package/dist/lib/output-format.js +28 -0
- package/dist/lib/output-format.js.map +1 -1
- package/dist/lib/packet.d.ts +35 -0
- package/dist/lib/packet.js +52 -2
- package/dist/lib/packet.js.map +1 -1
- package/dist/lib/stream.d.ts +126 -0
- package/dist/lib/stream.js +188 -5
- package/dist/lib/stream.js.map +1 -1
- 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 +27 -1
- package/dist/lib/utilities.d.ts +281 -53
- package/dist/lib/utilities.js +298 -55
- package/dist/lib/utilities.js.map +1 -1
- package/install/check.js +18 -7
- package/package.json +20 -19
- package/dist/api/fmp4.js +0 -710
- package/dist/api/fmp4.js.map +0 -1
- package/dist/api/media-input.js +0 -1075
- package/dist/api/media-input.js.map +0 -1
- package/dist/api/media-output.js +0 -1040
- package/dist/api/media-output.js.map +0 -1
- package/dist/api/webrtc.d.ts +0 -664
- package/dist/api/webrtc.js +0 -1132
- package/dist/api/webrtc.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { FormatContext } from '../../lib/format-context.js';
|
|
2
|
+
import type { AVCodecID } from '../../constants/index.js';
|
|
3
|
+
import type { Muxer } from '../muxer.js';
|
|
3
4
|
/**
|
|
4
5
|
* Streaming protocol utilities.
|
|
5
6
|
*
|
|
@@ -8,11 +9,11 @@ import type { MediaOutput } from '../media-output.js';
|
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* ```typescript
|
|
11
|
-
* import { StreamingUtils,
|
|
12
|
+
* import { StreamingUtils, Muxer } from 'node-av/api';
|
|
12
13
|
*
|
|
13
14
|
* // Create RTP outputs
|
|
14
|
-
* const videoOutput = await
|
|
15
|
-
* const audioOutput = await
|
|
15
|
+
* const videoOutput = await Muxer.open('rtp://127.0.0.1:5004');
|
|
16
|
+
* const audioOutput = await Muxer.open('rtp://127.0.0.1:5006');
|
|
16
17
|
*
|
|
17
18
|
* // Generate SDP for streaming
|
|
18
19
|
* const sdp = StreamingUtils.createSdp([videoOutput, audioOutput]);
|
|
@@ -24,23 +25,24 @@ import type { MediaOutput } from '../media-output.js';
|
|
|
24
25
|
*/
|
|
25
26
|
export declare class StreamingUtils {
|
|
26
27
|
/**
|
|
27
|
-
* Create an SDP (Session Description Protocol) string from
|
|
28
|
+
* Create an SDP (Session Description Protocol) string from demuxer/muxer
|
|
28
29
|
*
|
|
29
30
|
* Generates an SDP description for RTP/RTSP streaming from one or more
|
|
30
|
-
* configured
|
|
31
|
+
* configured demuxer/muxer. The inputs/outputs should be configured with RTP
|
|
31
32
|
* format and have their streams set up before calling this method.
|
|
32
33
|
*
|
|
33
|
-
* @param inouts - Array of
|
|
34
|
+
* @param inouts - Array of Demuxer or Muxer objects configured for RTP
|
|
35
|
+
*
|
|
34
36
|
* @returns SDP string if successful, null if failed
|
|
35
37
|
*
|
|
36
38
|
* @example
|
|
37
39
|
* ```typescript
|
|
38
40
|
* // Set up RTP outputs with streams
|
|
39
|
-
* const output1 = await
|
|
40
|
-
* await output1.
|
|
41
|
+
* const output1 = await Muxer.open('rtp://239.0.0.1:5004');
|
|
42
|
+
* await output1.addStream(encoder1);
|
|
41
43
|
*
|
|
42
|
-
* const output2 = await
|
|
43
|
-
* await output2.
|
|
44
|
+
* const output2 = await Muxer.open('rtp://239.0.0.1:5006');
|
|
45
|
+
* await output2.addStream(encoder2);
|
|
44
46
|
*
|
|
45
47
|
* // Generate SDP for multicast streaming
|
|
46
48
|
* const sdp = StreamingUtils.createSdp([output1.getFormatContext(), output2.getFormatContext()]);
|
|
@@ -51,21 +53,70 @@ export declare class StreamingUtils {
|
|
|
51
53
|
* ```
|
|
52
54
|
*/
|
|
53
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
|
+
* @param sessionName - Optional session name for the SDP (default: 'RTP Stream')
|
|
64
|
+
*
|
|
65
|
+
* @returns SDP string with proper rtpmap and optional crypto attributes
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* import { StreamingUtils } from 'node-av/api';
|
|
70
|
+
* import { AV_CODEC_ID_OPUS, AV_CODEC_ID_H264 } from 'node-av/constants';
|
|
71
|
+
*
|
|
72
|
+
* // Single audio stream with SRTP
|
|
73
|
+
* const sdp = StreamingUtils.createInputSDP([{
|
|
74
|
+
* port: 5004,
|
|
75
|
+
* codecId: AV_CODEC_ID_OPUS,
|
|
76
|
+
* payloadType: 111,
|
|
77
|
+
* clockRate: 48000,
|
|
78
|
+
* channels: 2,
|
|
79
|
+
* srtp: {
|
|
80
|
+
* key: Buffer.alloc(16, 0x12),
|
|
81
|
+
* salt: Buffer.alloc(14, 0x34)
|
|
82
|
+
* }
|
|
83
|
+
* }]);
|
|
84
|
+
*
|
|
85
|
+
* // Multi-stream (video + audio)
|
|
86
|
+
* const sdp = StreamingUtils.createInputSDP([
|
|
87
|
+
* { port: 5006, codecId: AV_CODEC_ID_H264, payloadType: 96, clockRate: 90000 },
|
|
88
|
+
* { port: 5004, codecId: AV_CODEC_ID_OPUS, payloadType: 111, clockRate: 48000, channels: 2 }
|
|
89
|
+
* ], 'My Stream');
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
static createInputSDP(configs: {
|
|
93
|
+
port: number;
|
|
94
|
+
codecId: AVCodecID;
|
|
95
|
+
payloadType: number;
|
|
96
|
+
clockRate: number;
|
|
97
|
+
channels?: number;
|
|
98
|
+
fmtp?: string;
|
|
99
|
+
srtp?: {
|
|
100
|
+
key: Buffer;
|
|
101
|
+
salt: Buffer;
|
|
102
|
+
suite?: 'AES_CM_128_HMAC_SHA1_80' | 'AES_CM_256_HMAC_SHA1_80';
|
|
103
|
+
};
|
|
104
|
+
}[], sessionName?: string): string;
|
|
54
105
|
/**
|
|
55
106
|
* Validate if an output is configured for RTP streaming
|
|
56
107
|
*
|
|
57
|
-
* @param output -
|
|
108
|
+
* @param output - Muxer to check
|
|
58
109
|
* @returns true if configured for RTP
|
|
59
110
|
*
|
|
60
111
|
* @example
|
|
61
112
|
* ```typescript
|
|
62
|
-
* const output = await
|
|
113
|
+
* const output = await Muxer.open('rtp://127.0.0.1:5004');
|
|
63
114
|
* if (StreamingUtils.isRtpOutput(output)) {
|
|
64
115
|
* const sdp = StreamingUtils.createSdpForOutput(output);
|
|
65
116
|
* }
|
|
66
117
|
* ```
|
|
67
118
|
*/
|
|
68
|
-
static isRtpOutput(output:
|
|
119
|
+
static isRtpOutput(output: Muxer): boolean;
|
|
69
120
|
/**
|
|
70
121
|
* Build RTP URL from components
|
|
71
122
|
*
|
|
@@ -93,4 +144,12 @@ export declare class StreamingUtils {
|
|
|
93
144
|
localrtcpport?: number;
|
|
94
145
|
pkt_size?: number;
|
|
95
146
|
}): string;
|
|
147
|
+
/**
|
|
148
|
+
* Extract all UDP ports from SDP content
|
|
149
|
+
*
|
|
150
|
+
* @param sdp - SDP content string
|
|
151
|
+
*
|
|
152
|
+
* @returns Array of port numbers (one per stream)
|
|
153
|
+
*/
|
|
154
|
+
static extractPortsFromSDP(sdp: string): number[];
|
|
96
155
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
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';
|
|
1
5
|
import { avSdpCreate } from '../../lib/utilities.js';
|
|
2
6
|
/**
|
|
3
7
|
* Streaming protocol utilities.
|
|
@@ -7,11 +11,11 @@ import { avSdpCreate } from '../../lib/utilities.js';
|
|
|
7
11
|
*
|
|
8
12
|
* @example
|
|
9
13
|
* ```typescript
|
|
10
|
-
* import { StreamingUtils,
|
|
14
|
+
* import { StreamingUtils, Muxer } from 'node-av/api';
|
|
11
15
|
*
|
|
12
16
|
* // Create RTP outputs
|
|
13
|
-
* const videoOutput = await
|
|
14
|
-
* const audioOutput = await
|
|
17
|
+
* const videoOutput = await Muxer.open('rtp://127.0.0.1:5004');
|
|
18
|
+
* const audioOutput = await Muxer.open('rtp://127.0.0.1:5006');
|
|
15
19
|
*
|
|
16
20
|
* // Generate SDP for streaming
|
|
17
21
|
* const sdp = StreamingUtils.createSdp([videoOutput, audioOutput]);
|
|
@@ -23,23 +27,24 @@ import { avSdpCreate } from '../../lib/utilities.js';
|
|
|
23
27
|
*/
|
|
24
28
|
export class StreamingUtils {
|
|
25
29
|
/**
|
|
26
|
-
* Create an SDP (Session Description Protocol) string from
|
|
30
|
+
* Create an SDP (Session Description Protocol) string from demuxer/muxer
|
|
27
31
|
*
|
|
28
32
|
* Generates an SDP description for RTP/RTSP streaming from one or more
|
|
29
|
-
* configured
|
|
33
|
+
* configured demuxer/muxer. The inputs/outputs should be configured with RTP
|
|
30
34
|
* format and have their streams set up before calling this method.
|
|
31
35
|
*
|
|
32
|
-
* @param inouts - Array of
|
|
36
|
+
* @param inouts - Array of Demuxer or Muxer objects configured for RTP
|
|
37
|
+
*
|
|
33
38
|
* @returns SDP string if successful, null if failed
|
|
34
39
|
*
|
|
35
40
|
* @example
|
|
36
41
|
* ```typescript
|
|
37
42
|
* // Set up RTP outputs with streams
|
|
38
|
-
* const output1 = await
|
|
39
|
-
* await output1.
|
|
43
|
+
* const output1 = await Muxer.open('rtp://239.0.0.1:5004');
|
|
44
|
+
* await output1.addStream(encoder1);
|
|
40
45
|
*
|
|
41
|
-
* const output2 = await
|
|
42
|
-
* await output2.
|
|
46
|
+
* const output2 = await Muxer.open('rtp://239.0.0.1:5006');
|
|
47
|
+
* await output2.addStream(encoder2);
|
|
43
48
|
*
|
|
44
49
|
* // Generate SDP for multicast streaming
|
|
45
50
|
* const sdp = StreamingUtils.createSdp([output1.getFormatContext(), output2.getFormatContext()]);
|
|
@@ -55,15 +60,146 @@ export class StreamingUtils {
|
|
|
55
60
|
}
|
|
56
61
|
return avSdpCreate(contexts);
|
|
57
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
|
+
* @param sessionName - Optional session name for the SDP (default: 'RTP Stream')
|
|
71
|
+
*
|
|
72
|
+
* @returns SDP string with proper rtpmap and optional crypto attributes
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { StreamingUtils } from 'node-av/api';
|
|
77
|
+
* import { AV_CODEC_ID_OPUS, AV_CODEC_ID_H264 } from 'node-av/constants';
|
|
78
|
+
*
|
|
79
|
+
* // Single audio stream with SRTP
|
|
80
|
+
* const sdp = StreamingUtils.createInputSDP([{
|
|
81
|
+
* port: 5004,
|
|
82
|
+
* codecId: AV_CODEC_ID_OPUS,
|
|
83
|
+
* payloadType: 111,
|
|
84
|
+
* clockRate: 48000,
|
|
85
|
+
* channels: 2,
|
|
86
|
+
* srtp: {
|
|
87
|
+
* key: Buffer.alloc(16, 0x12),
|
|
88
|
+
* salt: Buffer.alloc(14, 0x34)
|
|
89
|
+
* }
|
|
90
|
+
* }]);
|
|
91
|
+
*
|
|
92
|
+
* // Multi-stream (video + audio)
|
|
93
|
+
* const sdp = StreamingUtils.createInputSDP([
|
|
94
|
+
* { port: 5006, codecId: AV_CODEC_ID_H264, payloadType: 96, clockRate: 90000 },
|
|
95
|
+
* { port: 5004, codecId: AV_CODEC_ID_OPUS, payloadType: 111, clockRate: 48000, channels: 2 }
|
|
96
|
+
* ], 'My Stream');
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
static createInputSDP(configs, sessionName = 'RTP Stream') {
|
|
100
|
+
const contexts = [];
|
|
101
|
+
try {
|
|
102
|
+
// Create one FormatContext per stream with individual URL (port)
|
|
103
|
+
for (const streamConfig of configs) {
|
|
104
|
+
const codec = Codec.findDecoder(streamConfig.codecId);
|
|
105
|
+
if (!codec) {
|
|
106
|
+
throw new Error(`Codec not found for codec ID: ${streamConfig.codecId}`);
|
|
107
|
+
}
|
|
108
|
+
// Create format context with URL containing the port
|
|
109
|
+
const ctx = new FormatContext();
|
|
110
|
+
ctx.allocContext();
|
|
111
|
+
ctx.url = `rtp://127.0.0.1:${streamConfig.port}`;
|
|
112
|
+
// Create stream with codec parameters
|
|
113
|
+
const stream = ctx.newStream(null);
|
|
114
|
+
stream.codecpar.codecId = streamConfig.codecId;
|
|
115
|
+
stream.codecpar.codecType = codec.type;
|
|
116
|
+
// Set audio-specific parameters
|
|
117
|
+
if (codec.type !== AVMEDIA_TYPE_VIDEO) {
|
|
118
|
+
if (streamConfig.clockRate) {
|
|
119
|
+
stream.codecpar.sampleRate = streamConfig.clockRate;
|
|
120
|
+
}
|
|
121
|
+
if (streamConfig.channels) {
|
|
122
|
+
stream.codecpar.channels = streamConfig.channels;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Set time base (required for SDP generation)
|
|
126
|
+
stream.timeBase = new Rational(1, streamConfig.clockRate);
|
|
127
|
+
contexts.push(ctx);
|
|
128
|
+
}
|
|
129
|
+
// Generate SDP using FFmpeg's native generator
|
|
130
|
+
// FFmpeg will automatically use the port from each context's URL and generate RFC-compliant codec names
|
|
131
|
+
let sdp = avSdpCreate(contexts);
|
|
132
|
+
if (!sdp) {
|
|
133
|
+
throw new Error('Failed to generate SDP');
|
|
134
|
+
}
|
|
135
|
+
// Post-process: Replace session name if provided
|
|
136
|
+
if (sessionName !== 'RTP Stream') {
|
|
137
|
+
sdp = sdp.replace(/^s=.*$/m, `s=${sessionName}`);
|
|
138
|
+
}
|
|
139
|
+
// Post-process: Replace payload types, add SRTP crypto lines, and/or custom fmtp
|
|
140
|
+
// Note: We always need to process because FFmpeg assigns payload types automatically
|
|
141
|
+
const sdpLines = sdp.split('\n');
|
|
142
|
+
const newLines = [];
|
|
143
|
+
let streamIndex = -1;
|
|
144
|
+
for (let i = 0; i < sdpLines.length; i++) {
|
|
145
|
+
const line = sdpLines[i];
|
|
146
|
+
if (line.startsWith('m=')) {
|
|
147
|
+
// New media section
|
|
148
|
+
streamIndex++;
|
|
149
|
+
const streamCfg = configs[streamIndex];
|
|
150
|
+
// Replace FFmpeg's auto-assigned payload type with user-specified one
|
|
151
|
+
const replaced = line.replace(/RTP\/AVP\s+(\d+)/, `RTP/AVP ${streamCfg.payloadType}`);
|
|
152
|
+
newLines.push(replaced);
|
|
153
|
+
// Add SRTP crypto line right after m= line if configured
|
|
154
|
+
if (streamCfg?.srtp) {
|
|
155
|
+
const suite = streamCfg.srtp.suite ?? 'AES_CM_128_HMAC_SHA1_80';
|
|
156
|
+
const keyMaterial = Buffer.concat([streamCfg.srtp.key, streamCfg.srtp.salt]).toString('base64');
|
|
157
|
+
newLines.push(`a=crypto:1 ${suite} inline:${keyMaterial}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (line.startsWith('a=rtpmap:')) {
|
|
161
|
+
// Replace payload type in rtpmap line
|
|
162
|
+
const streamCfg = configs[streamIndex];
|
|
163
|
+
const replaced = line.replace(/^a=rtpmap:(\d+)/, `a=rtpmap:${streamCfg.payloadType}`);
|
|
164
|
+
newLines.push(replaced);
|
|
165
|
+
// If custom fmtp is provided but FFmpeg didn't generate one, add it
|
|
166
|
+
if (streamCfg?.fmtp && !sdpLines[i + 1]?.startsWith('a=fmtp:')) {
|
|
167
|
+
newLines.push(`a=fmtp:${streamCfg.payloadType} ${streamCfg.fmtp}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (line.startsWith('a=fmtp:')) {
|
|
171
|
+
// Replace payload type in fmtp line and optionally replace content
|
|
172
|
+
const streamCfg = configs[streamIndex];
|
|
173
|
+
if (streamCfg?.fmtp) {
|
|
174
|
+
newLines.push(`a=fmtp:${streamCfg.payloadType} ${streamCfg.fmtp}`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const replaced = line.replace(/^a=fmtp:(\d+)/, `a=fmtp:${streamCfg.payloadType}`);
|
|
178
|
+
newLines.push(replaced);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
newLines.push(line);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return newLines.join('\n');
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
// Cleanup all contexts
|
|
189
|
+
for (const ctx of contexts) {
|
|
190
|
+
ctx.freeContext();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
58
194
|
/**
|
|
59
195
|
* Validate if an output is configured for RTP streaming
|
|
60
196
|
*
|
|
61
|
-
* @param output -
|
|
197
|
+
* @param output - Muxer to check
|
|
62
198
|
* @returns true if configured for RTP
|
|
63
199
|
*
|
|
64
200
|
* @example
|
|
65
201
|
* ```typescript
|
|
66
|
-
* const output = await
|
|
202
|
+
* const output = await Muxer.open('rtp://127.0.0.1:5004');
|
|
67
203
|
* if (StreamingUtils.isRtpOutput(output)) {
|
|
68
204
|
* const sdp = StreamingUtils.createSdpForOutput(output);
|
|
69
205
|
* }
|
|
@@ -116,5 +252,27 @@ export class StreamingUtils {
|
|
|
116
252
|
}
|
|
117
253
|
return url;
|
|
118
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Extract all UDP ports from SDP content
|
|
257
|
+
*
|
|
258
|
+
* @param sdp - SDP content string
|
|
259
|
+
*
|
|
260
|
+
* @returns Array of port numbers (one per stream)
|
|
261
|
+
*/
|
|
262
|
+
static extractPortsFromSDP(sdp) {
|
|
263
|
+
const ports = [];
|
|
264
|
+
const lines = sdp.split('\n');
|
|
265
|
+
for (const line of lines) {
|
|
266
|
+
if (line.startsWith('m=')) {
|
|
267
|
+
// m=audio 5004 RTP/AVP 111
|
|
268
|
+
// m=video 5006 RTP/AVP 96
|
|
269
|
+
const match = /m=\w+\s+(\d+)/.exec(line);
|
|
270
|
+
if (match) {
|
|
271
|
+
ports.push(parseInt(match[1], 10));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return ports;
|
|
276
|
+
}
|
|
119
277
|
}
|
|
120
278
|
//# sourceMappingURL=streaming.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../../src/api/utilities/streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAKrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,cAAc;IACzB
|
|
1
|
+
{"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../../src/api/utilities/streaming.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { RTCRtpCodecParameters } from 'werift';
|
|
2
|
+
import type { AVCodecID } from '../constants/index.js';
|
|
3
|
+
import type { RTPStreamOptions } from './rtp-stream.js';
|
|
4
|
+
/**
|
|
5
|
+
* Codec information for WebRTC streaming.
|
|
6
|
+
*
|
|
7
|
+
* Contains RTP codec parameters and FFmpeg codec IDs for video and audio streams.
|
|
8
|
+
* Used for codec negotiation in WebRTC peer connections.
|
|
9
|
+
*/
|
|
10
|
+
export interface WebRTCCodecInfo {
|
|
11
|
+
/**
|
|
12
|
+
* Video codec configuration.
|
|
13
|
+
* Combines RTP parameters (mimeType, clockRate, etc.) with FFmpeg codec ID.
|
|
14
|
+
*/
|
|
15
|
+
video?: Partial<RTCRtpCodecParameters> & {
|
|
16
|
+
codecId: AVCodecID;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Optional audio codec configuration.
|
|
20
|
+
* Combines RTP parameters (mimeType, clockRate, channels) with FFmpeg codec ID.
|
|
21
|
+
*/
|
|
22
|
+
audio?: Partial<RTCRtpCodecParameters> & {
|
|
23
|
+
codecId: AVCodecID;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Options for configuring WebRTC session with werift integration.
|
|
28
|
+
*/
|
|
29
|
+
export interface WebRTCStreamOptions extends Omit<RTPStreamOptions, 'onVideoPacket' | 'onAudioPacket' | 'supportedVideoCodecs' | 'supportedAudioCodecs'> {
|
|
30
|
+
/**
|
|
31
|
+
* ICE servers for NAT traversal and STUN/TURN configuration.
|
|
32
|
+
*
|
|
33
|
+
* @default []
|
|
34
|
+
*/
|
|
35
|
+
iceServers?: {
|
|
36
|
+
urls: string;
|
|
37
|
+
}[];
|
|
38
|
+
/**
|
|
39
|
+
* Callback invoked when a new ICE candidate is discovered.
|
|
40
|
+
* Send this candidate to the remote peer via signaling channel.
|
|
41
|
+
*
|
|
42
|
+
* @param candidate - ICE candidate string to send to remote peer
|
|
43
|
+
*/
|
|
44
|
+
onIceCandidate?: (candidate: string) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Complete WebRTC session management with werift integration.
|
|
48
|
+
*
|
|
49
|
+
* Provides end-to-end WebRTC streaming with automatic SDP negotiation,
|
|
50
|
+
* ICE candidate handling, and peer connection management.
|
|
51
|
+
* Built on top of {@link RTPStream} for generic RTP streaming with WebRTC-specific
|
|
52
|
+
* protocol details handled automatically.
|
|
53
|
+
* Integrates with werift library for RTCPeerConnection and media track handling.
|
|
54
|
+
* Ideal for building complete WebRTC streaming applications with minimal code.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { WebRTCStream } from 'node-av/api';
|
|
59
|
+
*
|
|
60
|
+
* // Create session from media source
|
|
61
|
+
* const session = await WebRTCStream.create('rtsp://camera.local/stream', {
|
|
62
|
+
* mtu: 1200,
|
|
63
|
+
* hardware: 'auto',
|
|
64
|
+
* iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
|
|
65
|
+
* onIceCandidate: (candidate) => {
|
|
66
|
+
* sendToClient({ type: 'candidate', value: candidate });
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* // Process SDP offer from client
|
|
71
|
+
* const answer = await session.setOffer(clientOffer);
|
|
72
|
+
* sendToClient({ type: 'answer', value: answer });
|
|
73
|
+
*
|
|
74
|
+
* // Start streaming
|
|
75
|
+
* await session.start();
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* // Complete WebSocket signaling server
|
|
81
|
+
* import { WebSocket } from 'ws';
|
|
82
|
+
*
|
|
83
|
+
* ws.on('message', async (data) => {
|
|
84
|
+
* const msg = JSON.parse(data);
|
|
85
|
+
*
|
|
86
|
+
* if (msg.type === 'offer') {
|
|
87
|
+
* const session = await WebRTCStream.create(msg.url, {
|
|
88
|
+
* hardware: 'auto',
|
|
89
|
+
* onIceCandidate: (candidate) => {
|
|
90
|
+
* ws.send(JSON.stringify({ type: 'candidate', value: candidate }));
|
|
91
|
+
* }
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* const answer = await session.setOffer(msg.value);
|
|
95
|
+
* ws.send(JSON.stringify({ type: 'answer', value: answer }));
|
|
96
|
+
*
|
|
97
|
+
* await session.start();
|
|
98
|
+
* } else if (msg.type === 'candidate') {
|
|
99
|
+
* session.addIceCandidate(msg.value);
|
|
100
|
+
* }
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @see {@link RTPStream} For library-agnostic RTP streaming
|
|
105
|
+
* @see {@link Demuxer} For input media handling
|
|
106
|
+
* @see {@link HardwareContext} For GPU acceleration
|
|
107
|
+
*/
|
|
108
|
+
export declare class WebRTCStream {
|
|
109
|
+
private stream;
|
|
110
|
+
private pc;
|
|
111
|
+
private videoTrack;
|
|
112
|
+
private audioTrack;
|
|
113
|
+
private options;
|
|
114
|
+
private pendingIceCandidates;
|
|
115
|
+
/**
|
|
116
|
+
* @param options - Session configuration options
|
|
117
|
+
*
|
|
118
|
+
* Use {@link create} factory method
|
|
119
|
+
*
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
122
|
+
private constructor();
|
|
123
|
+
/**
|
|
124
|
+
* Create a WebRTC session from a media source.
|
|
125
|
+
*
|
|
126
|
+
* Opens the input media, creates internal streaming components, and prepares
|
|
127
|
+
* for WebRTC peer connection negotiation. Does not start streaming yet.
|
|
128
|
+
* Call {@link setOffer} to negotiate SDP and {@link start} to begin streaming.
|
|
129
|
+
*
|
|
130
|
+
* @param inputUrl - Media source URL (RTSP, file path, HTTP, etc.)
|
|
131
|
+
*
|
|
132
|
+
* @param options - Session configuration options
|
|
133
|
+
*
|
|
134
|
+
* @returns Configured WebRTC session instance
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const session = await WebRTCStream.create('rtsp://camera.local/stream', {
|
|
139
|
+
* mtu: 1200,
|
|
140
|
+
* hardware: 'auto',
|
|
141
|
+
* iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
142
|
+
* });
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* // Session from file with hardware acceleration
|
|
148
|
+
* const session = await WebRTCStream.create('video.mp4', {
|
|
149
|
+
* hardware: {
|
|
150
|
+
* deviceType: AV_HWDEVICE_TYPE_CUDA
|
|
151
|
+
* }
|
|
152
|
+
* });
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
static create(inputUrl: string, options?: WebRTCStreamOptions): WebRTCStream;
|
|
156
|
+
/**
|
|
157
|
+
* Start streaming media to WebRTC peer connection.
|
|
158
|
+
*
|
|
159
|
+
* Begins the media processing pipeline, reading packets from input,
|
|
160
|
+
* transcoding if necessary, and sending RTP packets to media tracks.
|
|
161
|
+
* Note: The stream is automatically started by {@link setOffer}, so calling
|
|
162
|
+
* this method explicitly is optional. This method is provided for cases where
|
|
163
|
+
* you need explicit control over when streaming begins.
|
|
164
|
+
*
|
|
165
|
+
* @returns Promise that resolves when streaming completes
|
|
166
|
+
*
|
|
167
|
+
* @throws {FFmpegError} If transcoding or muxing fails
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* const session = await WebRTCStream.create('input.mp4');
|
|
172
|
+
* session.onIceCandidate = (c) => sendToRemote(c);
|
|
173
|
+
*
|
|
174
|
+
* const answer = await session.setOffer(remoteOffer);
|
|
175
|
+
* sendToRemote(answer);
|
|
176
|
+
* // Stream is already started by setOffer
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
start(): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Stop streaming gracefully and clean up all resources.
|
|
182
|
+
*
|
|
183
|
+
* Stops the stream, closes peer connection, and releases all resources.
|
|
184
|
+
* Safe to call multiple times. After stopping, you can call start() again
|
|
185
|
+
* to restart the session.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const session = await WebRTCStream.create('input.mp4');
|
|
190
|
+
* await session.start();
|
|
191
|
+
*
|
|
192
|
+
* // Stop after 10 seconds
|
|
193
|
+
* setTimeout(async () => await session.stop(), 10000);
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
stop(): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Get detected codec information.
|
|
199
|
+
*
|
|
200
|
+
* Returns RTP codec parameters and FFmpeg codec IDs for video and audio.
|
|
201
|
+
* Useful for inspecting what codecs will be used in the WebRTC session.
|
|
202
|
+
* The input is automatically opened during {@link create}, so codecs can be
|
|
203
|
+
* detected immediately after session creation.
|
|
204
|
+
*
|
|
205
|
+
* @returns Codec configuration for video and audio streams
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const session = await WebRTCStream.create('input.mp4');
|
|
210
|
+
* const codecs = session.getCodecs();
|
|
211
|
+
*
|
|
212
|
+
* console.log('Video:', codecs.video.mimeType);
|
|
213
|
+
* console.log('Audio:', codecs.audio?.mimeType);
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
getCodecs(): WebRTCCodecInfo;
|
|
217
|
+
/**
|
|
218
|
+
* Process SDP offer from remote peer and generate SDP answer.
|
|
219
|
+
*
|
|
220
|
+
* Creates RTCPeerConnection with detected codecs, sets up media tracks,
|
|
221
|
+
* processes the remote SDP offer, and generates a local SDP answer.
|
|
222
|
+
* Also configures ICE candidate handling via {@link onIceCandidate} callback.
|
|
223
|
+
* Must be called before {@link start}.
|
|
224
|
+
*
|
|
225
|
+
* @param offerSdp - SDP offer string from remote WebRTC peer
|
|
226
|
+
*
|
|
227
|
+
* @returns SDP answer string to send back to remote peer
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const session = await WebRTCStream.create('input.mp4');
|
|
232
|
+
*
|
|
233
|
+
* // Setup ICE candidate handler first
|
|
234
|
+
* session.onIceCandidate = (candidate) => {
|
|
235
|
+
* sendToRemote({ type: 'candidate', value: candidate });
|
|
236
|
+
* };
|
|
237
|
+
*
|
|
238
|
+
* // Process offer and send answer
|
|
239
|
+
* const answer = await session.setOffer(remoteOffer);
|
|
240
|
+
* sendToRemote({ type: 'answer', value: answer });
|
|
241
|
+
*
|
|
242
|
+
* // Start streaming
|
|
243
|
+
* await session.start();
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
setOffer(offerSdp: string): Promise<string>;
|
|
247
|
+
/**
|
|
248
|
+
* Add ICE candidate from remote peer.
|
|
249
|
+
*
|
|
250
|
+
* Processes ICE candidates received from the remote peer via signaling channel.
|
|
251
|
+
* Should be called whenever a new candidate message arrives from remote peer.
|
|
252
|
+
* Can be called multiple times as candidates are discovered.
|
|
253
|
+
*
|
|
254
|
+
* Supports ICE trickling: If called before {@link setOffer}, candidates are buffered
|
|
255
|
+
* and applied automatically once the remote description is set.
|
|
256
|
+
*
|
|
257
|
+
* @param candidate - ICE candidate string from remote peer
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* // In signaling message handler
|
|
262
|
+
* if (msg.type === 'candidate') {
|
|
263
|
+
* session.addIceCandidate(msg.value);
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
addIceCandidate(candidate: string): void;
|
|
268
|
+
/**
|
|
269
|
+
* Get video codec configuration from input stream.
|
|
270
|
+
*
|
|
271
|
+
* @param codecId - FFmpeg codec ID
|
|
272
|
+
*
|
|
273
|
+
* @returns RTP codec parameters with codec ID or undefined if unsupported
|
|
274
|
+
*
|
|
275
|
+
* @internal
|
|
276
|
+
*/
|
|
277
|
+
private getVideoCodecConfig;
|
|
278
|
+
/**
|
|
279
|
+
* Get audio codec configuration from input stream.
|
|
280
|
+
*
|
|
281
|
+
* @param codecId - FFmpeg codec ID
|
|
282
|
+
*
|
|
283
|
+
* @returns RTP codec parameters with codec ID or undefined if unsupported
|
|
284
|
+
*
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
private getAudioCodecConfig;
|
|
288
|
+
}
|