ulai-js-sdk 0.1.4 → 0.1.5
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/room.d.ts +3 -0
- package/dist/room.js +35 -9
- package/package.json +1 -1
package/dist/room.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class Room extends EventEmitter<RoomEvent> {
|
|
|
12
12
|
participantId: string | null;
|
|
13
13
|
private readonly isPolite;
|
|
14
14
|
private makingOffer;
|
|
15
|
+
private negotiationOwed;
|
|
15
16
|
private candidateQueue;
|
|
16
17
|
private signalQueue;
|
|
17
18
|
constructor(cpBase: string);
|
|
@@ -20,6 +21,8 @@ export declare class Room extends EventEmitter<RoomEvent> {
|
|
|
20
21
|
private setupSignaling;
|
|
21
22
|
private handleSignal;
|
|
22
23
|
private safeNegotiate;
|
|
24
|
+
/** Makes an offer that was skipped earlier, once nothing else is being negotiated. */
|
|
25
|
+
private settleOwedNegotiation;
|
|
23
26
|
private handleIncomingCandidate;
|
|
24
27
|
private drainCandidateQueue;
|
|
25
28
|
/**
|
package/dist/room.js
CHANGED
|
@@ -20,6 +20,10 @@ class Room extends eventemitter3_1.default {
|
|
|
20
20
|
// Perfect Negotiation State (Client is the polite peer)
|
|
21
21
|
isPolite = true;
|
|
22
22
|
makingOffer = false;
|
|
23
|
+
// An offer we still owe the bridge: a negotiationneeded we had to skip, or an offer of ours that a
|
|
24
|
+
// colliding bridge offer rolled back. The browser fires negotiationneeded only when its flag turns
|
|
25
|
+
// on, so it won't reliably come again for either: we have to remember to make the offer ourselves.
|
|
26
|
+
negotiationOwed = false;
|
|
23
27
|
candidateQueue = [];
|
|
24
28
|
// Signaling messages are handled one at a time, in arrival order. Handled concurrently, a bridge
|
|
25
29
|
// offer could be processed in the middle of our own offer and dropped.
|
|
@@ -149,6 +153,9 @@ class Room extends eventemitter3_1.default {
|
|
|
149
153
|
}
|
|
150
154
|
if (offerCollision) {
|
|
151
155
|
console.log('[Room] Polite collision: rolling back local offer to accept server offer');
|
|
156
|
+
// Whatever our offer carried (the mic, typically) is lost with it, and the browser doesn't
|
|
157
|
+
// reliably fire negotiationneeded again, so offer it again once this exchange is done.
|
|
158
|
+
this.negotiationOwed = true;
|
|
152
159
|
}
|
|
153
160
|
// On a collision this implicitly rolls back our own offer, even one still being made. An
|
|
154
161
|
// explicit rollback throws if we're stable by the time it runs, which is what happens when
|
|
@@ -162,6 +169,7 @@ class Room extends eventemitter3_1.default {
|
|
|
162
169
|
type: 'answer',
|
|
163
170
|
sdp: pc.localDescription?.sdp,
|
|
164
171
|
});
|
|
172
|
+
this.settleOwedNegotiation();
|
|
165
173
|
}
|
|
166
174
|
else if (msg.type === 'answer') {
|
|
167
175
|
if (!msg.sdp)
|
|
@@ -173,6 +181,7 @@ class Room extends eventemitter3_1.default {
|
|
|
173
181
|
}
|
|
174
182
|
await pc.setRemoteDescription({ type: 'answer', sdp: msg.sdp });
|
|
175
183
|
await this.drainCandidateQueue();
|
|
184
|
+
this.settleOwedNegotiation();
|
|
176
185
|
}
|
|
177
186
|
else if (msg.type === 'candidate' && msg.candidate) {
|
|
178
187
|
const candidateInit = JSON.parse(msg.candidate);
|
|
@@ -185,22 +194,28 @@ class Room extends eventemitter3_1.default {
|
|
|
185
194
|
}
|
|
186
195
|
}
|
|
187
196
|
async safeNegotiate() {
|
|
188
|
-
|
|
197
|
+
const pc = this.peerConnection;
|
|
198
|
+
if (!pc)
|
|
189
199
|
return;
|
|
190
|
-
// Mid-negotiation
|
|
191
|
-
//
|
|
192
|
-
if (this.
|
|
200
|
+
// Mid-negotiation we can't offer: setLocalDescription() would make an answer, or race our own
|
|
201
|
+
// offer. Remember it instead; settleOwedNegotiation() makes the offer once we're stable again.
|
|
202
|
+
if (this.makingOffer || pc.signalingState !== 'stable') {
|
|
203
|
+
this.negotiationOwed = true;
|
|
193
204
|
return;
|
|
205
|
+
}
|
|
206
|
+
this.negotiationOwed = false;
|
|
194
207
|
try {
|
|
195
208
|
this.makingOffer = true;
|
|
196
209
|
// Without an argument the offer is created when it's applied, so it can't go stale against
|
|
197
210
|
// an offer from the bridge that arrives meanwhile.
|
|
198
|
-
await
|
|
199
|
-
if (
|
|
200
|
-
|
|
211
|
+
await pc.setLocalDescription();
|
|
212
|
+
if (pc.pendingLocalDescription?.type !== 'offer') {
|
|
213
|
+
this.negotiationOwed = true; // rolled back by a bridge offer; make it again afterwards
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
201
216
|
this.signaling?.send({
|
|
202
217
|
type: 'offer',
|
|
203
|
-
sdp:
|
|
218
|
+
sdp: pc.localDescription?.sdp,
|
|
204
219
|
});
|
|
205
220
|
}
|
|
206
221
|
catch (err) {
|
|
@@ -208,6 +223,13 @@ class Room extends eventemitter3_1.default {
|
|
|
208
223
|
}
|
|
209
224
|
finally {
|
|
210
225
|
this.makingOffer = false;
|
|
226
|
+
this.settleOwedNegotiation();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/** Makes an offer that was skipped earlier, once nothing else is being negotiated. */
|
|
230
|
+
settleOwedNegotiation() {
|
|
231
|
+
if (this.negotiationOwed && !this.makingOffer && this.peerConnection?.signalingState === 'stable') {
|
|
232
|
+
void this.safeNegotiate();
|
|
211
233
|
}
|
|
212
234
|
}
|
|
213
235
|
async handleIncomingCandidate(candidate) {
|
|
@@ -270,7 +292,10 @@ class Room extends eventemitter3_1.default {
|
|
|
270
292
|
this.peerConnection.addTrack(audioTrack, this.localStream);
|
|
271
293
|
}
|
|
272
294
|
}
|
|
273
|
-
//
|
|
295
|
+
// Offer the mic ourselves rather than waiting for negotiationneeded: when this runs while an
|
|
296
|
+
// exchange is in progress (e.g. the bridge offering us someone already talking), the browser
|
|
297
|
+
// may never fire it. If something is being negotiated, this is made as soon as it's done.
|
|
298
|
+
void this.safeNegotiate();
|
|
274
299
|
}
|
|
275
300
|
return this.localStream;
|
|
276
301
|
}
|
|
@@ -326,6 +351,7 @@ class Room extends eventemitter3_1.default {
|
|
|
326
351
|
}
|
|
327
352
|
this.audioTransceiver = null;
|
|
328
353
|
this.candidateQueue = [];
|
|
354
|
+
this.negotiationOwed = false;
|
|
329
355
|
this.remoteStreams.clear();
|
|
330
356
|
if (this.signaling) {
|
|
331
357
|
this.signaling.close();
|