ep_webrtc 2.0.2 → 2.1.0
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 +1 -1
- package/static/js/index.js +34 -64
package/package.json
CHANGED
package/static/js/index.js
CHANGED
|
@@ -135,10 +135,6 @@ class ClosedEvent extends CustomEvent {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
// The WebRTC negotiation logic used here is based on the "Perfect Negotiation Example" at
|
|
139
|
-
// https://www.w3.org/TR/2021/REC-webrtc-20210126/#perfect-negotiation-example. See there for
|
|
140
|
-
// details about how it works.
|
|
141
|
-
//
|
|
142
138
|
// Events:
|
|
143
139
|
// * 'stream' (see StreamEvent): Emitted when the remote stream is ready. For every 'stream' event
|
|
144
140
|
// there will be a corresponding 'streamgone' event. Once a stream is added another stream will
|
|
@@ -148,15 +144,15 @@ class ClosedEvent extends CustomEvent {
|
|
|
148
144
|
// * 'closed' (see ClosedEvent): Emitted when the PeerState is closed, except when closed by a
|
|
149
145
|
// call to close(). The PeerState must not be used after it is closed.
|
|
150
146
|
class PeerState extends EventTargetPolyfill {
|
|
151
|
-
constructor(pcConfig,
|
|
147
|
+
constructor(pcConfig, caller, sendMessage, localTracks, debug) {
|
|
152
148
|
super();
|
|
153
149
|
this._pcConfig = pcConfig;
|
|
154
|
-
this.
|
|
150
|
+
this._caller = caller;
|
|
155
151
|
this._sendMessage = (msg) => sendMessage(Object.assign({ids: this._ids}, msg));
|
|
156
152
|
this._localTracks = localTracks;
|
|
157
153
|
this._failedSLDAttempts = 0;
|
|
158
154
|
this._debug = debug;
|
|
159
|
-
this._debug(`I am the ${this.
|
|
155
|
+
this._debug(`I am the ${this._caller ? 'calling' : 'answering'} peer`);
|
|
160
156
|
this._ids = {
|
|
161
157
|
from: {
|
|
162
158
|
// Only changes when the user reloads the page.
|
|
@@ -224,15 +220,6 @@ class PeerState extends EventTargetPolyfill {
|
|
|
224
220
|
this._setRemoteStream(null);
|
|
225
221
|
this._ids.from.instance = ++nextInstanceId;
|
|
226
222
|
this._ids.to = peerIds;
|
|
227
|
-
// This negotiation state is captured in closures (instead of doing something like
|
|
228
|
-
// this._negotiationState) because this._resetConnection() needs to be sure that all of the
|
|
229
|
-
// event handlers for the old RTCPeerConnection do not mutate the negotiation state for the new
|
|
230
|
-
// RTCPeerConnection.
|
|
231
|
-
const negotiationState = {
|
|
232
|
-
makingOffer: false,
|
|
233
|
-
ignoreOffer: false,
|
|
234
|
-
isSettingRemoteAnswerPending: false,
|
|
235
|
-
};
|
|
236
223
|
const pc = new RTCPeerConnection(this._pcConfig);
|
|
237
224
|
pc.addEventListener('track', ({track, streams}) => {
|
|
238
225
|
if (streams.length !== 1) throw new Error('Expected track to be in exactly one stream');
|
|
@@ -240,8 +227,16 @@ class PeerState extends EventTargetPolyfill {
|
|
|
240
227
|
});
|
|
241
228
|
pc.addEventListener('icecandidate', ({candidate}) => this._sendMessage({candidate}));
|
|
242
229
|
pc.addEventListener('negotiationneeded', async () => {
|
|
230
|
+
if (!this._caller) {
|
|
231
|
+
this._debug('Waiting for peer to call');
|
|
232
|
+
// It is possible that the last invite sent to the peer was sent before the peer was ready
|
|
233
|
+
// to accept invites. If that is the case and there are no local tracks to trigger a WebRTC
|
|
234
|
+
// message exchange, the peer won't know that it is OK to connect back. Send another invite
|
|
235
|
+
// just in case. The peer will ignore any superfluous invites.
|
|
236
|
+
this._sendMessage({invite: 'invite'});
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
243
239
|
try {
|
|
244
|
-
negotiationState.makingOffer = true;
|
|
245
240
|
await pc.setLocalDescription();
|
|
246
241
|
this._failedSLDAttempts = 0;
|
|
247
242
|
this._sendMessage({description: pc.localDescription});
|
|
@@ -250,8 +245,6 @@ class PeerState extends EventTargetPolyfill {
|
|
|
250
245
|
if (++this._failedSLDAttempts > 10) throw err; // Avoid an infinite loop.
|
|
251
246
|
this._resetConnection(err);
|
|
252
247
|
return;
|
|
253
|
-
} finally {
|
|
254
|
-
negotiationState.makingOffer = false;
|
|
255
248
|
}
|
|
256
249
|
});
|
|
257
250
|
pc.addEventListener('connectionstatechange', () => {
|
|
@@ -299,46 +292,12 @@ class PeerState extends EventTargetPolyfill {
|
|
|
299
292
|
|
|
300
293
|
if (this._pc != null) this._pc.close();
|
|
301
294
|
this._pc = pc;
|
|
302
|
-
this._setRemoteDescription = async (description) => {
|
|
303
|
-
const readyForOffer = !negotiationState.makingOffer &&
|
|
304
|
-
(pc.signalingState === 'stable' || negotiationState.isSettingRemoteAnswerPending);
|
|
305
|
-
const offerCollision = description.type === 'offer' && !readyForOffer;
|
|
306
|
-
negotiationState.ignoreOffer = !this._polite && offerCollision;
|
|
307
|
-
if (negotiationState.ignoreOffer) {
|
|
308
|
-
this._debug('ignoring offer due to offer collision');
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
negotiationState.isSettingRemoteAnswerPending = description.type === 'answer';
|
|
312
|
-
await pc.setRemoteDescription(description);
|
|
313
|
-
// The "Perfect Negotiation Example" doesn't put this next line inside a `finally` block. It
|
|
314
|
-
// is unclear whether that is intentional. Fortunately it doesn't matter here: If the above
|
|
315
|
-
// pc.setRemoteDescription() call throws, _resetConnection() is called to restart the
|
|
316
|
-
// negotiation anyway.
|
|
317
|
-
negotiationState.isSettingRemoteAnswerPending = false;
|
|
318
|
-
if (description.type === 'offer') {
|
|
319
|
-
await pc.setLocalDescription();
|
|
320
|
-
this._sendMessage({description: pc.localDescription});
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
this._addIceCandidate = async (candidate) => {
|
|
324
|
-
try {
|
|
325
|
-
await pc.addIceCandidate(candidate);
|
|
326
|
-
} catch (err) {
|
|
327
|
-
if (!negotiationState.ignoreOffer) throw err;
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
295
|
|
|
331
296
|
const tracks = this._localTracks.stream.getTracks();
|
|
332
297
|
for (const track of tracks) {
|
|
333
298
|
this._debug(`start streaming ${track.kind} track ${track.id}`);
|
|
334
299
|
pc.addTrack(track, this._localTracks.stream);
|
|
335
300
|
}
|
|
336
|
-
// Creating an RTCPeerConnection doesn't actually generate any control messages until
|
|
337
|
-
// RTCPeerConnection.addTrack() is called. It is possible that the last invite sent to the peer
|
|
338
|
-
// was sent before the peer was ready to accept invites. If that is the case and there are no
|
|
339
|
-
// local tracks to trigger a WebRTC message exchange, the peer won't know that it is OK to
|
|
340
|
-
// connect back. Send another invite just in case. The peer will ignore any superfluous invites.
|
|
341
|
-
if (tracks.length === 0) this._sendMessage({invite: 'invite'});
|
|
342
301
|
}
|
|
343
302
|
|
|
344
303
|
async receiveMessage(msg) {
|
|
@@ -374,8 +333,14 @@ class PeerState extends EventTargetPolyfill {
|
|
|
374
333
|
}
|
|
375
334
|
if (this._pc == null) this._resetConnection(null, this._ids.to);
|
|
376
335
|
try {
|
|
377
|
-
if (description != null)
|
|
378
|
-
|
|
336
|
+
if (description != null) {
|
|
337
|
+
await this._pc.setRemoteDescription(description);
|
|
338
|
+
if (description.type === 'offer') {
|
|
339
|
+
await this._pc.setLocalDescription();
|
|
340
|
+
this._sendMessage({description: this._pc.localDescription});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (candidate != null) await this._pc.addIceCandidate(candidate);
|
|
379
344
|
} catch (err) {
|
|
380
345
|
err.peerMessage = msg;
|
|
381
346
|
console.error('Error processing message from peer:', err);
|
|
@@ -396,14 +361,13 @@ class PeerState extends EventTargetPolyfill {
|
|
|
396
361
|
}
|
|
397
362
|
}
|
|
398
363
|
|
|
399
|
-
const
|
|
400
|
-
// Compare user IDs to determine which of the two users
|
|
401
|
-
const
|
|
402
|
-
if ((otherId.localeCompare(myId) < 0) ===
|
|
403
|
-
// One peer must be polite and the other must be impolite.
|
|
364
|
+
const isCaller = (myId, otherId) => {
|
|
365
|
+
// Compare user IDs to determine which of the two users will initiate the call.
|
|
366
|
+
const caller = myId.localeCompare(otherId) < 0;
|
|
367
|
+
if ((otherId.localeCompare(myId) < 0) === caller) {
|
|
404
368
|
throw new Error(`Peer ID ${otherId} compares equivalent to own ID ${myId}`);
|
|
405
369
|
}
|
|
406
|
-
return
|
|
370
|
+
return caller;
|
|
407
371
|
};
|
|
408
372
|
|
|
409
373
|
// Periods in element IDs make it hard to build a selector string because period is for class match.
|
|
@@ -886,7 +850,10 @@ exports.rtc = new class {
|
|
|
886
850
|
autoplay: '',
|
|
887
851
|
muted: isLocal ? '' : null,
|
|
888
852
|
})
|
|
889
|
-
.prop(
|
|
853
|
+
.prop({
|
|
854
|
+
muted: isLocal, // Setting the 'muted' attribute isn't sufficient for some reason.
|
|
855
|
+
volume: isLocal ? 0.0 : 1.0, // Long shot attempt at fixing echo in Safari.
|
|
856
|
+
});
|
|
890
857
|
const $interface = $('<div>')
|
|
891
858
|
.addClass('interface-container')
|
|
892
859
|
.attr('id', `interface_${videoId}`);
|
|
@@ -1239,6 +1206,9 @@ exports.rtc = new class {
|
|
|
1239
1206
|
}
|
|
1240
1207
|
|
|
1241
1208
|
sendMessage(to, data) {
|
|
1209
|
+
if (data.ids == null) data.ids = {};
|
|
1210
|
+
if (data.ids.from == null) data.ids.from = {};
|
|
1211
|
+
data.ids.from.session = sessionId;
|
|
1242
1212
|
debug(`(${to == null ? 'to everyone on the pad' : `peer ${to}`}) sending message`, data);
|
|
1243
1213
|
this._pad.collabClient.sendMessage({
|
|
1244
1214
|
type: 'RTC_MESSAGE',
|
|
@@ -1276,7 +1246,7 @@ exports.rtc = new class {
|
|
|
1276
1246
|
// the WebRTC signaling messages. This is bad because WebRTC assumes reliable, in-order delivery
|
|
1277
1247
|
// of signaling messages, so the discards will break future connection attempts.
|
|
1278
1248
|
invitePeer(userId) {
|
|
1279
|
-
this.sendMessage(userId, {
|
|
1249
|
+
this.sendMessage(userId, {invite: 'invite'});
|
|
1280
1250
|
}
|
|
1281
1251
|
|
|
1282
1252
|
getPeerConnection(userId) {
|
|
@@ -1286,7 +1256,7 @@ exports.rtc = new class {
|
|
|
1286
1256
|
_debug('creating PeerState');
|
|
1287
1257
|
peer = new PeerState(
|
|
1288
1258
|
{iceServers: this._settings.iceServers},
|
|
1289
|
-
|
|
1259
|
+
isCaller(this.getUserId(), userId),
|
|
1290
1260
|
(msg) => this.sendMessage(userId, msg),
|
|
1291
1261
|
this._localTracks,
|
|
1292
1262
|
_debug);
|