node-rtc-connection 1.0.12 → 1.0.15
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 +4357 -3092
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +4357 -3092
- 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 +998 -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
package/src/RTCDataChannel.js
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* RTCDataChannel represents a bidirectional data channel between two peers.
|
|
5
|
-
* Ported from Chromium's implementation, DataChannel-only functionality.
|
|
6
|
-
*/
|
|
7
|
-
class RTCDataChannel extends EventEmitter {
|
|
8
|
-
constructor(nativeChannel, peerConnectionHandler) {
|
|
9
|
-
super();
|
|
10
|
-
|
|
11
|
-
this._nativeChannel = nativeChannel;
|
|
12
|
-
this._peerConnectionHandler = peerConnectionHandler;
|
|
13
|
-
this._state = 'connecting';
|
|
14
|
-
this._binaryType = 'arraybuffer';
|
|
15
|
-
this._bufferedAmount = 0;
|
|
16
|
-
this._bufferedAmountLowThreshold = 0;
|
|
17
|
-
this._closed = false;
|
|
18
|
-
this._scheduledEvents = [];
|
|
19
|
-
|
|
20
|
-
// Setup native channel observers
|
|
21
|
-
this._setupObservers();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* The label specified when creating the data channel
|
|
26
|
-
*/
|
|
27
|
-
get label() {
|
|
28
|
-
return this._nativeChannel ? this._nativeChannel.label : '';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Whether the channel is ordered or allows out-of-order delivery
|
|
33
|
-
*/
|
|
34
|
-
get ordered() {
|
|
35
|
-
return this._nativeChannel ? this._nativeChannel.ordered : true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Maximum packet lifetime in milliseconds
|
|
40
|
-
*/
|
|
41
|
-
get maxPacketLifeTime() {
|
|
42
|
-
if (!this._nativeChannel) return null;
|
|
43
|
-
const lifetime = this._nativeChannel.maxPacketLifeTime;
|
|
44
|
-
return lifetime >= 0 ? lifetime : null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Maximum number of retransmit attempts
|
|
49
|
-
*/
|
|
50
|
-
get maxRetransmits() {
|
|
51
|
-
if (!this._nativeChannel) return null;
|
|
52
|
-
const retransmits = this._nativeChannel.maxRetransmits;
|
|
53
|
-
return retransmits >= 0 ? retransmits : null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Subprotocol name
|
|
58
|
-
*/
|
|
59
|
-
get protocol() {
|
|
60
|
-
return this._nativeChannel ? this._nativeChannel.protocol : '';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Whether the channel was negotiated by the application or the WebRTC layer
|
|
65
|
-
*/
|
|
66
|
-
get negotiated() {
|
|
67
|
-
return this._nativeChannel ? this._nativeChannel.negotiated : false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* The ID for this data channel
|
|
72
|
-
*/
|
|
73
|
-
get id() {
|
|
74
|
-
if (!this._nativeChannel) return null;
|
|
75
|
-
const channelId = this._nativeChannel.id;
|
|
76
|
-
return channelId >= 0 ? channelId : null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The state of the data channel
|
|
81
|
-
* Values: 'connecting', 'open', 'closing', 'closed'
|
|
82
|
-
*/
|
|
83
|
-
get readyState() {
|
|
84
|
-
return this._state;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* The number of bytes currently queued to be sent
|
|
89
|
-
*/
|
|
90
|
-
get bufferedAmount() {
|
|
91
|
-
return this._bufferedAmount;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Threshold for the bufferedAmount at which bufferedamountlow event fires
|
|
96
|
-
*/
|
|
97
|
-
get bufferedAmountLowThreshold() {
|
|
98
|
-
return this._bufferedAmountLowThreshold;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
set bufferedAmountLowThreshold(value) {
|
|
102
|
-
this._bufferedAmountLowThreshold = value;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Format for received binary data: 'blob' or 'arraybuffer'
|
|
107
|
-
*/
|
|
108
|
-
get binaryType() {
|
|
109
|
-
return this._binaryType;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
set binaryType(value) {
|
|
113
|
-
if (value !== 'blob' && value !== 'arraybuffer') {
|
|
114
|
-
throw new Error('binaryType must be either "blob" or "arraybuffer"');
|
|
115
|
-
}
|
|
116
|
-
this._binaryType = value;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Send data over the channel
|
|
121
|
-
* @param {string|ArrayBuffer|ArrayBufferView} data - Data to send
|
|
122
|
-
*/
|
|
123
|
-
send(data) {
|
|
124
|
-
if (this._state !== 'open') {
|
|
125
|
-
throw new Error('RTCDataChannel.send() called on a channel that is not open');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (!this._nativeChannel) {
|
|
129
|
-
throw new Error('Native data channel is not available');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
let buffer;
|
|
133
|
-
let isBinary = false;
|
|
134
|
-
|
|
135
|
-
if (typeof data === 'string') {
|
|
136
|
-
buffer = Buffer.from(data, 'utf8');
|
|
137
|
-
isBinary = false;
|
|
138
|
-
} else if (data instanceof ArrayBuffer) {
|
|
139
|
-
buffer = Buffer.from(data);
|
|
140
|
-
isBinary = true;
|
|
141
|
-
} else if (ArrayBuffer.isView(data)) {
|
|
142
|
-
buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
143
|
-
isBinary = true;
|
|
144
|
-
} else {
|
|
145
|
-
throw new Error('Unsupported data type');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const length = buffer.length;
|
|
149
|
-
|
|
150
|
-
// Validate send length
|
|
151
|
-
if (length > 65536) { // Maximum WebRTC message size
|
|
152
|
-
throw new Error('Message too long');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
this._bufferedAmount += length;
|
|
156
|
-
|
|
157
|
-
// Send through native channel
|
|
158
|
-
try {
|
|
159
|
-
this._nativeChannel.send({
|
|
160
|
-
data: buffer,
|
|
161
|
-
binary: isBinary
|
|
162
|
-
});
|
|
163
|
-
} catch (error) {
|
|
164
|
-
throw new Error(`Failed to send data: ${error.message}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Close the data channel
|
|
170
|
-
*/
|
|
171
|
-
close() {
|
|
172
|
-
if (this._closed) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
this._closed = true;
|
|
177
|
-
|
|
178
|
-
if (this._nativeChannel) {
|
|
179
|
-
this._nativeChannel.close();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (this._state === 'closing' || this._state === 'closed') {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
this._setState('closing');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Setup observers for the native channel
|
|
191
|
-
* @private
|
|
192
|
-
*/
|
|
193
|
-
_setupObservers() {
|
|
194
|
-
if (!this._nativeChannel) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// State change observer
|
|
199
|
-
this._nativeChannel.on('statechange', (state) => {
|
|
200
|
-
this._onStateChange(state);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Message observer
|
|
204
|
-
this._nativeChannel.on('message', (buffer) => {
|
|
205
|
-
this._onMessage(buffer);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Buffered amount change observer
|
|
209
|
-
this._nativeChannel.on('bufferedamountlow', (amount) => {
|
|
210
|
-
this._onBufferedAmountChange(amount);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Error observer
|
|
214
|
-
this._nativeChannel.on('error', (error) => {
|
|
215
|
-
this.emit('error', error);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Handle state change from native channel
|
|
221
|
-
* @private
|
|
222
|
-
*/
|
|
223
|
-
_onStateChange(state) {
|
|
224
|
-
let newState;
|
|
225
|
-
let shouldEmitOpen = false;
|
|
226
|
-
let shouldEmitClosing = false;
|
|
227
|
-
let shouldEmitClose = false;
|
|
228
|
-
|
|
229
|
-
switch (state) {
|
|
230
|
-
case 0: // kConnecting
|
|
231
|
-
newState = 'connecting';
|
|
232
|
-
break;
|
|
233
|
-
case 1: // kOpen
|
|
234
|
-
newState = 'open';
|
|
235
|
-
shouldEmitOpen = true;
|
|
236
|
-
break;
|
|
237
|
-
case 2: // kClosing
|
|
238
|
-
newState = 'closing';
|
|
239
|
-
shouldEmitClosing = true;
|
|
240
|
-
break;
|
|
241
|
-
case 3: // kClosed
|
|
242
|
-
newState = 'closed';
|
|
243
|
-
shouldEmitClose = true;
|
|
244
|
-
break;
|
|
245
|
-
default:
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Only update state if it actually changed
|
|
250
|
-
if (this._readyState !== newState) {
|
|
251
|
-
this._setState(newState);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Emit events AFTER state is updated
|
|
255
|
-
if (shouldEmitOpen) {
|
|
256
|
-
this.emit('open');
|
|
257
|
-
}
|
|
258
|
-
if (shouldEmitClosing) {
|
|
259
|
-
this.emit('closing');
|
|
260
|
-
}
|
|
261
|
-
if (shouldEmitClose) {
|
|
262
|
-
this.emit('close');
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Handle incoming message from native channel
|
|
268
|
-
* @private
|
|
269
|
-
*/
|
|
270
|
-
_onMessage(buffer) {
|
|
271
|
-
if (this._state !== 'open') {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
let data;
|
|
276
|
-
if (buffer.binary) {
|
|
277
|
-
if (this._binaryType === 'arraybuffer') {
|
|
278
|
-
data = buffer.data.buffer.slice(
|
|
279
|
-
buffer.data.byteOffset,
|
|
280
|
-
buffer.data.byteOffset + buffer.data.byteLength
|
|
281
|
-
);
|
|
282
|
-
} else {
|
|
283
|
-
// For 'blob' type, we'll just pass the buffer
|
|
284
|
-
// In a browser environment, this would be converted to a Blob
|
|
285
|
-
data = buffer.data;
|
|
286
|
-
}
|
|
287
|
-
} else {
|
|
288
|
-
data = buffer.data.toString('utf8');
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
this.emit('message', { data });
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Handle buffered amount change from native channel
|
|
296
|
-
* @private
|
|
297
|
-
*/
|
|
298
|
-
_onBufferedAmountChange(newAmount) {
|
|
299
|
-
const previousAmount = this._bufferedAmount;
|
|
300
|
-
this._bufferedAmount = newAmount;
|
|
301
|
-
|
|
302
|
-
if (previousAmount > this._bufferedAmountLowThreshold &&
|
|
303
|
-
newAmount <= this._bufferedAmountLowThreshold) {
|
|
304
|
-
this.emit('bufferedamountlow');
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Set the channel state
|
|
310
|
-
* @private
|
|
311
|
-
*/
|
|
312
|
-
_setState(state) {
|
|
313
|
-
if (this._state === state) {
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
this._state = state;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Set state to open without dispatching event (used for remote channels)
|
|
321
|
-
*/
|
|
322
|
-
setStateToOpenWithoutEvent() {
|
|
323
|
-
this._setState('open');
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Dispatch open event (used for remote channels)
|
|
328
|
-
*/
|
|
329
|
-
dispatchOpenEvent() {
|
|
330
|
-
this.emit('open');
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Dispose of resources
|
|
335
|
-
*/
|
|
336
|
-
dispose() {
|
|
337
|
-
if (this._nativeChannel) {
|
|
338
|
-
this._nativeChannel.removeAllListeners();
|
|
339
|
-
this._nativeChannel = null;
|
|
340
|
-
}
|
|
341
|
-
this._peerConnectionHandler = null;
|
|
342
|
-
this.removeAllListeners();
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
module.exports = RTCDataChannel;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RTCDataChannelEvent is fired when a data channel is added to the connection.
|
|
3
|
-
* Ported from Chromium's implementation.
|
|
4
|
-
*/
|
|
5
|
-
class RTCDataChannelEvent {
|
|
6
|
-
constructor(type, eventInitDict = {}) {
|
|
7
|
-
this._type = type;
|
|
8
|
-
this._channel = eventInitDict.channel || null;
|
|
9
|
-
this._bubbles = eventInitDict.bubbles || false;
|
|
10
|
-
this._cancelable = eventInitDict.cancelable || false;
|
|
11
|
-
this._timestamp = Date.now();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The event type
|
|
16
|
-
*/
|
|
17
|
-
get type() {
|
|
18
|
-
return this._type;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The RTCDataChannel associated with the event
|
|
23
|
-
*/
|
|
24
|
-
get channel() {
|
|
25
|
-
return this._channel;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Whether the event bubbles
|
|
30
|
-
*/
|
|
31
|
-
get bubbles() {
|
|
32
|
-
return this._bubbles;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Whether the event is cancelable
|
|
37
|
-
*/
|
|
38
|
-
get cancelable() {
|
|
39
|
-
return this._cancelable;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The timestamp when the event was created
|
|
44
|
-
*/
|
|
45
|
-
get timeStamp() {
|
|
46
|
-
return this._timestamp;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
module.exports = RTCDataChannelEvent;
|
package/src/RTCError.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RTCErrorEvent represents an error event.
|
|
3
|
-
* Ported from Chromium's implementation.
|
|
4
|
-
*/
|
|
5
|
-
class RTCErrorEvent {
|
|
6
|
-
constructor(type, eventInitDict = {}) {
|
|
7
|
-
this._type = type;
|
|
8
|
-
this._error = eventInitDict.error || null;
|
|
9
|
-
this._bubbles = eventInitDict.bubbles || false;
|
|
10
|
-
this._cancelable = eventInitDict.cancelable || false;
|
|
11
|
-
this._timestamp = Date.now();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The event type
|
|
16
|
-
*/
|
|
17
|
-
get type() {
|
|
18
|
-
return this._type;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The error associated with the event
|
|
23
|
-
*/
|
|
24
|
-
get error() {
|
|
25
|
-
return this._error;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Whether the event bubbles
|
|
30
|
-
*/
|
|
31
|
-
get bubbles() {
|
|
32
|
-
return this._bubbles;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Whether the event is cancelable
|
|
37
|
-
*/
|
|
38
|
-
get cancelable() {
|
|
39
|
-
return this._cancelable;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The timestamp when the event was created
|
|
44
|
-
*/
|
|
45
|
-
get timeStamp() {
|
|
46
|
-
return this._timestamp;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* RTCError represents a WebRTC-specific error.
|
|
52
|
-
*/
|
|
53
|
-
class RTCError extends Error {
|
|
54
|
-
constructor(errorDetail, message) {
|
|
55
|
-
super(message);
|
|
56
|
-
this.name = 'RTCError';
|
|
57
|
-
this.errorDetail = errorDetail;
|
|
58
|
-
this.sdpLineNumber = null;
|
|
59
|
-
this.httpRequestStatusCode = null;
|
|
60
|
-
this.sctpCauseCode = null;
|
|
61
|
-
this.receivedAlert = null;
|
|
62
|
-
this.sentAlert = null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports = { RTCErrorEvent, RTCError };
|
package/src/RTCIceCandidate.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RTCIceCandidate represents an ICE candidate.
|
|
3
|
-
* Ported from Chromium's implementation.
|
|
4
|
-
*/
|
|
5
|
-
class RTCIceCandidate {
|
|
6
|
-
constructor(candidateInitDict = {}) {
|
|
7
|
-
this._candidate = candidateInitDict.candidate || '';
|
|
8
|
-
this._sdpMid = candidateInitDict.sdpMid || null;
|
|
9
|
-
this._sdpMLineIndex = candidateInitDict.sdpMLineIndex !== undefined
|
|
10
|
-
? candidateInitDict.sdpMLineIndex
|
|
11
|
-
: null;
|
|
12
|
-
this._usernameFragment = candidateInitDict.usernameFragment || null;
|
|
13
|
-
|
|
14
|
-
// Parse candidate string if provided
|
|
15
|
-
if (this._candidate) {
|
|
16
|
-
this._parseCandidateString();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The candidate string
|
|
22
|
-
*/
|
|
23
|
-
get candidate() {
|
|
24
|
-
return this._candidate;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* The media stream identification tag
|
|
29
|
-
*/
|
|
30
|
-
get sdpMid() {
|
|
31
|
-
return this._sdpMid;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* The media line index
|
|
36
|
-
*/
|
|
37
|
-
get sdpMLineIndex() {
|
|
38
|
-
return this._sdpMLineIndex;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The username fragment
|
|
43
|
-
*/
|
|
44
|
-
get usernameFragment() {
|
|
45
|
-
return this._usernameFragment;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* The foundation
|
|
50
|
-
*/
|
|
51
|
-
get foundation() {
|
|
52
|
-
return this._foundation || null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* The component (RTP or RTCP)
|
|
57
|
-
*/
|
|
58
|
-
get component() {
|
|
59
|
-
return this._component || null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The priority
|
|
64
|
-
*/
|
|
65
|
-
get priority() {
|
|
66
|
-
return this._priority || null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The IP address
|
|
71
|
-
*/
|
|
72
|
-
get address() {
|
|
73
|
-
return this._address || null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The protocol (udp or tcp)
|
|
78
|
-
*/
|
|
79
|
-
get protocol() {
|
|
80
|
-
return this._protocol || null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The port
|
|
85
|
-
*/
|
|
86
|
-
get port() {
|
|
87
|
-
return this._port || null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* The candidate type (host, srflx, prflx, or relay)
|
|
92
|
-
*/
|
|
93
|
-
get type() {
|
|
94
|
-
return this._type || null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* The TCP type (active, passive, or so)
|
|
99
|
-
*/
|
|
100
|
-
get tcpType() {
|
|
101
|
-
return this._tcpType || null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* The related address (for non-host candidates)
|
|
106
|
-
*/
|
|
107
|
-
get relatedAddress() {
|
|
108
|
-
return this._relatedAddress || null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* The related port (for non-host candidates)
|
|
113
|
-
*/
|
|
114
|
-
get relatedPort() {
|
|
115
|
-
return this._relatedPort || null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Parse the candidate string to extract individual fields
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
_parseCandidateString() {
|
|
123
|
-
if (!this._candidate || !this._candidate.startsWith('candidate:')) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Basic parsing of candidate string
|
|
128
|
-
// Format: candidate:<foundation> <component> <protocol> <priority> <address> <port> typ <type> ...
|
|
129
|
-
const parts = this._candidate.split(' ');
|
|
130
|
-
|
|
131
|
-
if (parts.length >= 8) {
|
|
132
|
-
this._foundation = parts[0].replace('candidate:', '');
|
|
133
|
-
this._component = parts[1];
|
|
134
|
-
this._protocol = parts[2];
|
|
135
|
-
this._priority = parseInt(parts[3], 10);
|
|
136
|
-
this._address = parts[4];
|
|
137
|
-
this._port = parseInt(parts[5], 10);
|
|
138
|
-
// parts[6] is 'typ'
|
|
139
|
-
this._type = parts[7];
|
|
140
|
-
|
|
141
|
-
// Parse optional fields
|
|
142
|
-
for (let i = 8; i < parts.length; i += 2) {
|
|
143
|
-
const key = parts[i];
|
|
144
|
-
const value = parts[i + 1];
|
|
145
|
-
|
|
146
|
-
switch (key) {
|
|
147
|
-
case 'raddr':
|
|
148
|
-
this._relatedAddress = value;
|
|
149
|
-
break;
|
|
150
|
-
case 'rport':
|
|
151
|
-
this._relatedPort = parseInt(value, 10);
|
|
152
|
-
break;
|
|
153
|
-
case 'tcptype':
|
|
154
|
-
this._tcpType = value;
|
|
155
|
-
break;
|
|
156
|
-
case 'ufrag':
|
|
157
|
-
this._usernameFragment = value;
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Convert to JSON
|
|
166
|
-
*/
|
|
167
|
-
toJSON() {
|
|
168
|
-
return {
|
|
169
|
-
candidate: this._candidate,
|
|
170
|
-
sdpMid: this._sdpMid,
|
|
171
|
-
sdpMLineIndex: this._sdpMLineIndex,
|
|
172
|
-
usernameFragment: this._usernameFragment
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Convert to string
|
|
178
|
-
*/
|
|
179
|
-
toString() {
|
|
180
|
-
return `RTCIceCandidate { candidate: "${this._candidate}", sdpMid: "${this._sdpMid}", sdpMLineIndex: ${this._sdpMLineIndex} }`;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
module.exports = RTCIceCandidate;
|