arnacon-webrtc-service 0.2.10 → 0.2.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arnacon-webrtc-service",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Arnacon WebRTC core runtime and service modules",
5
5
  "main": "./webRTCservice/core.js",
6
6
  "type": "commonjs",
@@ -190,8 +190,9 @@ function createOfferFlow({
190
190
  return routed.responseBody || { ok: true, sessionId, handled: true, type: "offer" };
191
191
  }
192
192
  }
193
- logger.warn(`[${sessionId}] existing pair offer arrived without reusable ingress handler`);
194
- return { ok: true, sessionId, handled: false, type: "offer", reusedPairContext: true };
193
+ // Never silently drop a fresh invite/reconnect attempt. If pair ingress
194
+ // cannot reuse the current context, force a clean handshake path.
195
+ logger.warn(`[${sessionId}] existing pair offer could not reuse ingress; forcing fresh handshake`);
195
196
  }
196
197
 
197
198
  sessionId = key;
@@ -93,6 +93,30 @@ class WebRtcClientLeg extends MediaLeg {
93
93
  return out;
94
94
  }
95
95
 
96
+ pickPreferredReceiverTrack(tracks = []) {
97
+ if (!Array.isArray(tracks) || tracks.length === 0) return null;
98
+ const remoteOrder = new Map();
99
+ for (let i = 0; i < (this.session.remoteTracks || []).length; i += 1) {
100
+ remoteOrder.set(this.session.remoteTracks[i], i);
101
+ }
102
+ const score = (track) => {
103
+ const isLive = track?.readyState === "live" ? 1 : 0;
104
+ const order = remoteOrder.has(track) ? remoteOrder.get(track) : -1;
105
+ return (isLive * 1000000) + order;
106
+ };
107
+ let best = tracks[0];
108
+ let bestScore = score(best);
109
+ for (let i = 1; i < tracks.length; i += 1) {
110
+ const candidate = tracks[i];
111
+ const candidateScore = score(candidate);
112
+ if (candidateScore > bestScore) {
113
+ best = candidate;
114
+ bestScore = candidateScore;
115
+ }
116
+ }
117
+ return best;
118
+ }
119
+
96
120
  onRtp(handler) {
97
121
  const disposers = [];
98
122
  const subscribed = new Set();
@@ -115,7 +139,19 @@ class WebRtcClientLeg extends MediaLeg {
115
139
  if (this.trackUnsubscribe) disposers.push(this.trackUnsubscribe);
116
140
  };
117
141
 
118
- for (const track of this.getReceiverAudioTracks()) subscribeTrack(track);
142
+ const receiverTracks = this.getReceiverAudioTracks();
143
+ const preferredTrack = this.pickPreferredReceiverTrack(receiverTracks);
144
+ if (preferredTrack) {
145
+ if (receiverTracks.length > 1) {
146
+ this.logger.warn(
147
+ `[${this.id}] WebRTC leg has ${receiverTracks.length} receiver audio tracks; preferring freshest live track`,
148
+ );
149
+ }
150
+ subscribeTrack(preferredTrack);
151
+ if (Array.isArray(this.session.remoteTracks) && this.session.remoteTracks.length > 1) {
152
+ this.session.remoteTracks = this.session.remoteTracks.filter((track) => track === preferredTrack);
153
+ }
154
+ }
119
155
  const onTrackSub = this.peerConnection?.onTrack?.subscribe?.((track) => subscribeTrack(track));
120
156
  if (onTrackSub?.unSubscribe) disposers.push(() => onTrackSub.unSubscribe());
121
157
 
@@ -1117,6 +1117,8 @@ async function onExistingPairOffer({ sessionId, offer, session, pairKey } = {})
1117
1117
  {
1118
1118
  sdp: offer?.sdp,
1119
1119
  candidates: offer?.candidates || [],
1120
+ iceRestart: offer?.iceRestart === true,
1121
+ offerUfrag: offer?.offerUfrag || null,
1120
1122
  },
1121
1123
  { forceOffer: true },
1122
1124
  ),