node-rtc-connection 1.0.12 → 1.0.14
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 +355 -289
- package/dist/index.cjs +4318 -3095
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +4318 -3095
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/datachannel/RTCDataChannel.js +354 -0
- package/src/dtls/RTCCertificate.js +310 -0
- package/src/dtls/RTCDtlsTransport.js +247 -0
- package/src/foundation/ByteBufferQueue.js +235 -0
- package/src/foundation/RTCError.js +226 -0
- package/src/ice/RTCIceCandidate.js +301 -0
- package/src/ice/RTCIceTransport.js +956 -0
- package/src/index.d.ts +316 -145
- package/src/index.js +78 -45
- package/src/network/network-transport.js +478 -0
- package/src/peerconnection/RTCPeerConnection.js +847 -0
- package/src/sctp/RTCSctpTransport.js +253 -0
- package/src/sdp/RTCSessionDescription.js +102 -0
- package/src/sdp/sdp-utils.js +224 -0
- package/src/stun/stun-client.js +643 -0
- package/src/ICEGatherer.js +0 -341
- package/src/NativePeerConnectionFactory.js +0 -1044
- package/src/RTCDataChannel.js +0 -346
- package/src/RTCDataChannelEvent.js +0 -50
- package/src/RTCError.js +0 -66
- package/src/RTCIceCandidate.js +0 -184
- package/src/RTCPeerConnection.js +0 -505
- package/src/RTCPeerConnectionIceEvent.js +0 -58
- package/src/RTCSessionDescription.js +0 -62
- package/src/STUNClient.js +0 -222
- package/src/SecureConnection.js +0 -298
- package/src/TURNClient.js +0 -561
- package/src/UDPTransport.js +0 -236
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview RTCIceCandidate - ICE candidate representation.
|
|
3
|
+
*
|
|
4
|
+
* Ported from Chromium's WebRTC implementation:
|
|
5
|
+
* chromium/src/third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.{h,cc}
|
|
6
|
+
*
|
|
7
|
+
* Represents an ICE (Interactive Connectivity Establishment) candidate that
|
|
8
|
+
* describes a potential way to establish a connection with a peer.
|
|
9
|
+
*
|
|
10
|
+
* @license BSD-3-Clause
|
|
11
|
+
* @author nmhung1210
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* RTCIceCandidate represents a potential method for establishing connectivity.
|
|
18
|
+
*
|
|
19
|
+
* ICE candidates are described using SDP (Session Description Protocol) syntax.
|
|
20
|
+
* Each candidate describes a single address/port combination and transport protocol.
|
|
21
|
+
*/
|
|
22
|
+
class RTCIceCandidate {
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new RTCIceCandidate.
|
|
25
|
+
*
|
|
26
|
+
* @param {RTCIceCandidateInit} [candidateInit={}] - Initialization dictionary
|
|
27
|
+
* @param {string} [candidateInit.candidate=''] - SDP candidate string
|
|
28
|
+
* @param {string|null} [candidateInit.sdpMid] - Media stream ID
|
|
29
|
+
* @param {number|null} [candidateInit.sdpMLineIndex] - M-line index
|
|
30
|
+
* @param {string} [candidateInit.usernameFragment] - ICE username fragment
|
|
31
|
+
* @throws {TypeError} If both sdpMid and sdpMLineIndex are null
|
|
32
|
+
*/
|
|
33
|
+
constructor(candidateInit = {}) {
|
|
34
|
+
// Validate that at least one of sdpMid or sdpMLineIndex is present
|
|
35
|
+
if (candidateInit.sdpMid === null && candidateInit.sdpMLineIndex === null) {
|
|
36
|
+
throw new TypeError('sdpMid and sdpMLineIndex are both null');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* SDP candidate string.
|
|
41
|
+
* @private {string}
|
|
42
|
+
*/
|
|
43
|
+
this._candidate = candidateInit.candidate || '';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Media stream identification.
|
|
47
|
+
* @private {string|null}
|
|
48
|
+
*/
|
|
49
|
+
this._sdpMid = candidateInit.sdpMid !== undefined ? candidateInit.sdpMid : null;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Media line index (zero-based).
|
|
53
|
+
* @private {number|null}
|
|
54
|
+
*/
|
|
55
|
+
this._sdpMLineIndex = candidateInit.sdpMLineIndex !== undefined ?
|
|
56
|
+
candidateInit.sdpMLineIndex : null;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* ICE username fragment.
|
|
60
|
+
* @private {string|null}
|
|
61
|
+
*/
|
|
62
|
+
this._usernameFragment = candidateInit.usernameFragment || null;
|
|
63
|
+
|
|
64
|
+
// Parse candidate string for detailed attributes
|
|
65
|
+
this._parsedAttributes = this._parseCandidate(this._candidate);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parses an ICE candidate string to extract attributes.
|
|
70
|
+
* Format: "candidate:foundation component protocol priority address port typ type [raddr reladdr] [rport relport]"
|
|
71
|
+
*
|
|
72
|
+
* @private
|
|
73
|
+
* @param {string} candidateStr - Candidate string to parse
|
|
74
|
+
* @returns {Object} Parsed attributes
|
|
75
|
+
*/
|
|
76
|
+
_parseCandidate(candidateStr) {
|
|
77
|
+
const attrs = {
|
|
78
|
+
foundation: null,
|
|
79
|
+
component: null,
|
|
80
|
+
protocol: null,
|
|
81
|
+
priority: null,
|
|
82
|
+
address: null,
|
|
83
|
+
port: null,
|
|
84
|
+
type: null,
|
|
85
|
+
tcpType: null,
|
|
86
|
+
relatedAddress: null,
|
|
87
|
+
relatedPort: null
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (!candidateStr || !candidateStr.startsWith('candidate:')) {
|
|
91
|
+
return attrs;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Remove "candidate:" prefix
|
|
95
|
+
const parts = candidateStr.substring(10).trim().split(/\s+/);
|
|
96
|
+
|
|
97
|
+
if (parts.length < 8) {
|
|
98
|
+
return attrs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Parse fixed fields
|
|
102
|
+
attrs.foundation = parts[0];
|
|
103
|
+
attrs.component = parts[1];
|
|
104
|
+
attrs.protocol = parts[2].toLowerCase();
|
|
105
|
+
attrs.priority = parseInt(parts[3], 10);
|
|
106
|
+
attrs.address = parts[4];
|
|
107
|
+
attrs.port = parseInt(parts[5], 10);
|
|
108
|
+
|
|
109
|
+
// parts[6] should be "typ"
|
|
110
|
+
if (parts[6] === 'typ') {
|
|
111
|
+
attrs.type = parts[7];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Parse optional attributes
|
|
115
|
+
for (let i = 8; i < parts.length; i += 2) {
|
|
116
|
+
const key = parts[i];
|
|
117
|
+
const value = parts[i + 1];
|
|
118
|
+
|
|
119
|
+
if (key === 'raddr') {
|
|
120
|
+
attrs.relatedAddress = value;
|
|
121
|
+
} else if (key === 'rport') {
|
|
122
|
+
attrs.relatedPort = parseInt(value, 10);
|
|
123
|
+
} else if (key === 'tcptype') {
|
|
124
|
+
attrs.tcpType = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return attrs;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* SDP candidate attribute containing the candidate description.
|
|
133
|
+
* @type {string}
|
|
134
|
+
*/
|
|
135
|
+
get candidate() {
|
|
136
|
+
return this._candidate;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Media stream identification tag.
|
|
141
|
+
* @type {string|null}
|
|
142
|
+
*/
|
|
143
|
+
get sdpMid() {
|
|
144
|
+
return this._sdpMid;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Index of the m-line in the SDP this candidate is associated with.
|
|
149
|
+
* @type {number|null}
|
|
150
|
+
*/
|
|
151
|
+
get sdpMLineIndex() {
|
|
152
|
+
return this._sdpMLineIndex;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* ICE username fragment.
|
|
157
|
+
* @type {string|null}
|
|
158
|
+
*/
|
|
159
|
+
get usernameFragment() {
|
|
160
|
+
return this._usernameFragment;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Unique identifier for this candidate.
|
|
165
|
+
* @type {string|null}
|
|
166
|
+
*/
|
|
167
|
+
get foundation() {
|
|
168
|
+
return this._parsedAttributes.foundation;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Component identifier (rtp=1, rtcp=2).
|
|
173
|
+
* @type {string|null}
|
|
174
|
+
*/
|
|
175
|
+
get component() {
|
|
176
|
+
return this._parsedAttributes.component;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Priority value for this candidate.
|
|
181
|
+
* Higher priority candidates are preferred.
|
|
182
|
+
* @type {number|null}
|
|
183
|
+
*/
|
|
184
|
+
get priority() {
|
|
185
|
+
return this._parsedAttributes.priority;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* IP address of this candidate.
|
|
190
|
+
* @type {string|null}
|
|
191
|
+
*/
|
|
192
|
+
get address() {
|
|
193
|
+
return this._parsedAttributes.address;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Transport protocol (udp/tcp).
|
|
198
|
+
* @type {string|null}
|
|
199
|
+
*/
|
|
200
|
+
get protocol() {
|
|
201
|
+
return this._parsedAttributes.protocol;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Port number.
|
|
206
|
+
* @type {number|null}
|
|
207
|
+
*/
|
|
208
|
+
get port() {
|
|
209
|
+
return this._parsedAttributes.port;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Type of candidate (host, srflx, prflx, relay).
|
|
214
|
+
* @type {string|null}
|
|
215
|
+
*/
|
|
216
|
+
get type() {
|
|
217
|
+
return this._parsedAttributes.type;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* TCP candidate type (active, passive, so).
|
|
222
|
+
* Only applicable for TCP candidates.
|
|
223
|
+
* @type {string|null}
|
|
224
|
+
*/
|
|
225
|
+
get tcpType() {
|
|
226
|
+
return this._parsedAttributes.tcpType;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Related address for reflexive/relay candidates.
|
|
231
|
+
* @type {string|null}
|
|
232
|
+
*/
|
|
233
|
+
get relatedAddress() {
|
|
234
|
+
return this._parsedAttributes.relatedAddress;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Related port for reflexive/relay candidates.
|
|
239
|
+
* @type {number|null}
|
|
240
|
+
*/
|
|
241
|
+
get relatedPort() {
|
|
242
|
+
return this._parsedAttributes.relatedPort;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Converts candidate to JSON representation.
|
|
247
|
+
* @returns {Object} JSON representation
|
|
248
|
+
*/
|
|
249
|
+
toJSON() {
|
|
250
|
+
const json = {
|
|
251
|
+
candidate: this._candidate,
|
|
252
|
+
sdpMid: this._sdpMid,
|
|
253
|
+
sdpMLineIndex: this._sdpMLineIndex
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (this._usernameFragment) {
|
|
257
|
+
json.usernameFragment = this._usernameFragment;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return json;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Creates an RTCIceCandidate from a candidate string.
|
|
265
|
+
*
|
|
266
|
+
* @param {string} candidateStr - ICE candidate string
|
|
267
|
+
* @param {string|null} [sdpMid=null] - Media stream ID
|
|
268
|
+
* @param {number|null} [sdpMLineIndex=0] - M-line index
|
|
269
|
+
* @returns {RTCIceCandidate}
|
|
270
|
+
*/
|
|
271
|
+
static fromString(candidateStr, sdpMid = null, sdpMLineIndex = 0) {
|
|
272
|
+
return new RTCIceCandidate({
|
|
273
|
+
candidate: candidateStr,
|
|
274
|
+
sdpMid,
|
|
275
|
+
sdpMLineIndex
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Validates if a string is a valid candidate format.
|
|
281
|
+
*
|
|
282
|
+
* @param {string} candidateStr - String to validate
|
|
283
|
+
* @returns {boolean} True if valid candidate format
|
|
284
|
+
*/
|
|
285
|
+
static isValid(candidateStr) {
|
|
286
|
+
if (!candidateStr || typeof candidateStr !== 'string') {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Must start with "candidate:"
|
|
291
|
+
if (!candidateStr.startsWith('candidate:')) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Must have at least the minimum required fields
|
|
296
|
+
const parts = candidateStr.substring(10).trim().split(/\s+/);
|
|
297
|
+
return parts.length >= 8;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
module.exports = RTCIceCandidate;
|