node-rtc-connection 1.0.5 → 1.0.6
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/dist/index.cjs +125 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +125 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/NativePeerConnectionFactory.js +125 -17
package/dist/index.mjs
CHANGED
|
@@ -2398,6 +2398,9 @@ function requireNativePeerConnectionFactory () {
|
|
|
2398
2398
|
this._secureConnection = null;
|
|
2399
2399
|
this._udpTransport = null;
|
|
2400
2400
|
this._iceCandidates = [];
|
|
2401
|
+
this._remoteCandidates = [];
|
|
2402
|
+
this._selectedLocalCandidate = null;
|
|
2403
|
+
this._selectedRemoteCandidate = null;
|
|
2401
2404
|
|
|
2402
2405
|
console.log('[NativePeerConnection] Created with STUN/ICE/Encryption support');
|
|
2403
2406
|
console.log(` - Encryption: ${this._useEncryption ? 'enabled' : 'disabled (requires valid certs)'}`);
|
|
@@ -2543,22 +2546,106 @@ function requireNativePeerConnectionFactory () {
|
|
|
2543
2546
|
}
|
|
2544
2547
|
|
|
2545
2548
|
if (!candidate || !candidate.candidate) {
|
|
2549
|
+
// null candidate signals end of candidates - try to select best pair
|
|
2550
|
+
if (this._remoteCandidates.length > 0 && !this._selectedRemoteCandidate) {
|
|
2551
|
+
this._selectBestCandidatePair();
|
|
2552
|
+
}
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
// Parse ICE candidate
|
|
2557
|
+
const parsed = this._parseIceCandidate(candidate.candidate);
|
|
2558
|
+
if (!parsed) {
|
|
2546
2559
|
return;
|
|
2547
2560
|
}
|
|
2548
2561
|
|
|
2549
|
-
//
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2562
|
+
// Store the remote candidate
|
|
2563
|
+
this._remoteCandidates.push(parsed);
|
|
2564
|
+
console.log(`[NativePeerConnection] Added remote ICE candidate (${parsed.type}): ${parsed.ip}:${parsed.port}`);
|
|
2565
|
+
|
|
2566
|
+
// For backward compatibility, set remote address immediately if not set
|
|
2567
|
+
if (!this._remoteAddress) {
|
|
2568
|
+
this._remoteAddress = parsed.ip;
|
|
2569
|
+
this._remotePort = parsed.port;
|
|
2554
2570
|
console.log(`[NativePeerConnection] Remote address: ${this._remoteAddress}:${this._remotePort}`);
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// If we haven't selected a pair yet, try to select now
|
|
2574
|
+
if (!this._selectedRemoteCandidate && this._iceCandidates.length > 0) {
|
|
2575
|
+
this._selectBestCandidatePair();
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
// If we selected a pair and are offerer, connect
|
|
2579
|
+
if (this._selectedRemoteCandidate && this._isOfferer &&
|
|
2580
|
+
this._signalingState === 0 && !this._socket) {
|
|
2581
|
+
await this._connectToPeer();
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
/**
|
|
2586
|
+
* Parse ICE candidate string
|
|
2587
|
+
* @private
|
|
2588
|
+
*/
|
|
2589
|
+
_parseIceCandidate(candidateStr) {
|
|
2590
|
+
const parts = candidateStr.split(' ');
|
|
2591
|
+
if (parts.length < 6) {
|
|
2592
|
+
return null;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
return {
|
|
2596
|
+
foundation: parts[0].replace('candidate:', ''),
|
|
2597
|
+
component: parts[1],
|
|
2598
|
+
protocol: parts[2],
|
|
2599
|
+
priority: parseInt(parts[3], 10),
|
|
2600
|
+
ip: parts[4],
|
|
2601
|
+
port: parseInt(parts[5], 10),
|
|
2602
|
+
type: parts[7], // typ host/srflx/relay
|
|
2603
|
+
relatedAddress: parts[9] || null,
|
|
2604
|
+
relatedPort: parts[11] ? parseInt(parts[11], 10) : null
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
/**
|
|
2609
|
+
* Select best candidate pair for connection
|
|
2610
|
+
* Prioritizes: relay > srflx > host
|
|
2611
|
+
* @private
|
|
2612
|
+
*/
|
|
2613
|
+
_selectBestCandidatePair() {
|
|
2614
|
+
if (this._remoteCandidates.length === 0 || this._iceCandidates.length === 0) {
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
// Priority order: relay > srflx > host
|
|
2619
|
+
const typePriority = { 'relay': 3, 'srflx': 2, 'host': 1 };
|
|
2620
|
+
|
|
2621
|
+
// Find best local candidate
|
|
2622
|
+
let bestLocal = this._iceCandidates[0];
|
|
2623
|
+
for (const candidate of this._iceCandidates) {
|
|
2624
|
+
if (typePriority[candidate.type] > typePriority[bestLocal.type]) {
|
|
2625
|
+
bestLocal = candidate;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
// Find best remote candidate
|
|
2630
|
+
let bestRemote = this._remoteCandidates[0];
|
|
2631
|
+
for (const candidate of this._remoteCandidates) {
|
|
2632
|
+
if (typePriority[candidate.type] > typePriority[bestRemote.type]) {
|
|
2633
|
+
bestRemote = candidate;
|
|
2560
2634
|
}
|
|
2561
2635
|
}
|
|
2636
|
+
|
|
2637
|
+
this._selectedLocalCandidate = bestLocal;
|
|
2638
|
+
this._selectedRemoteCandidate = bestRemote;
|
|
2639
|
+
|
|
2640
|
+
// Update addresses for connection
|
|
2641
|
+
this._localAddress = bestLocal.ip;
|
|
2642
|
+
this._localPort = bestLocal.port;
|
|
2643
|
+
this._remoteAddress = bestRemote.ip;
|
|
2644
|
+
this._remotePort = bestRemote.port;
|
|
2645
|
+
|
|
2646
|
+
console.log(`[NativePeerConnection] Selected candidate pair:`);
|
|
2647
|
+
console.log(` Local: ${bestLocal.type} ${this._localAddress}:${this._localPort}`);
|
|
2648
|
+
console.log(` Remote: ${bestRemote.type} ${this._remoteAddress}:${this._remotePort}`);
|
|
2562
2649
|
}
|
|
2563
2650
|
|
|
2564
2651
|
/**
|
|
@@ -2694,14 +2781,24 @@ function requireNativePeerConnectionFactory () {
|
|
|
2694
2781
|
return;
|
|
2695
2782
|
}
|
|
2696
2783
|
|
|
2697
|
-
//
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2784
|
+
// Check if we're using relay candidates - if so, skip tie-breaking
|
|
2785
|
+
const usingRelay = this._selectedLocalCandidate?.type === 'relay' ||
|
|
2786
|
+
this._selectedRemoteCandidate?.type === 'relay';
|
|
2787
|
+
|
|
2788
|
+
if (!usingRelay) {
|
|
2789
|
+
// Tie-breaking: only connect if our port is higher than remote port
|
|
2790
|
+
// This ensures only one peer connects, avoiding the race condition
|
|
2791
|
+
// Note: This only works for direct connections (host/srflx)
|
|
2792
|
+
if (this._localPort < this._remotePort) {
|
|
2793
|
+
console.log(`[NativePeerConnection] Not connecting (local port ${this._localPort} < remote port ${this._remotePort}), waiting for incoming`);
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2702
2796
|
}
|
|
2703
2797
|
|
|
2704
2798
|
console.log(`[NativePeerConnection] Connecting to ${this._remoteAddress}:${this._remotePort}`);
|
|
2799
|
+
if (usingRelay) {
|
|
2800
|
+
console.log(`[NativePeerConnection] Using TURN relay connection`);
|
|
2801
|
+
}
|
|
2705
2802
|
|
|
2706
2803
|
this._socket = new net.Socket();
|
|
2707
2804
|
|
|
@@ -2994,7 +3091,12 @@ a=max-message-size:262144
|
|
|
2994
3091
|
|
|
2995
3092
|
// Emit each candidate
|
|
2996
3093
|
for (const candidate of candidates) {
|
|
2997
|
-
|
|
3094
|
+
// Parse and store the candidate
|
|
3095
|
+
const parsed = this._parseIceCandidate(candidate.candidate);
|
|
3096
|
+
if (parsed) {
|
|
3097
|
+
this._iceCandidates.push(parsed);
|
|
3098
|
+
}
|
|
3099
|
+
|
|
2998
3100
|
this.emit('icecandidate', {
|
|
2999
3101
|
candidate: candidate.candidate,
|
|
3000
3102
|
sdpMid: candidate.sdpMid,
|
|
@@ -3008,8 +3110,14 @@ a=max-message-size:262144
|
|
|
3008
3110
|
|
|
3009
3111
|
// Fallback: emit only local host candidate
|
|
3010
3112
|
if (this._localAddress && this._localPort) {
|
|
3113
|
+
const candidateStr = `candidate:1 1 tcp 2130706431 ${this._localAddress} ${this._localPort} typ host`;
|
|
3114
|
+
const parsed = this._parseIceCandidate(candidateStr);
|
|
3115
|
+
if (parsed) {
|
|
3116
|
+
this._iceCandidates.push(parsed);
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3011
3119
|
const candidate = {
|
|
3012
|
-
candidate:
|
|
3120
|
+
candidate: candidateStr,
|
|
3013
3121
|
sdpMid: 'data',
|
|
3014
3122
|
sdpMLineIndex: 0
|
|
3015
3123
|
};
|