livekit-client 1.2.4 → 1.2.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/livekit-client.esm.mjs +980 -760
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +34 -34
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +124 -124
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/DeviceManager.d.ts +1 -0
- package/dist/src/room/DeviceManager.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +4 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +1 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +4 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +0 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts +0 -2
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +1 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/package.json +36 -34
- package/src/room/DeviceManager.ts +23 -1
- package/src/room/RTCEngine.ts +30 -6
- package/src/room/Room.ts +143 -145
- package/src/room/participant/LocalParticipant.ts +13 -3
- package/src/room/track/LocalAudioTrack.ts +0 -2
- package/src/room/track/LocalVideoTrack.ts +3 -7
- package/src/room/track/RemoteTrackPublication.ts +1 -1
- package/src/room/track/RemoteVideoTrack.ts +0 -3
- package/src/room/track/create.ts +16 -1
- package/src/room/utils.ts +7 -5
package/src/room/Room.ts
CHANGED
@@ -99,8 +99,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
99
99
|
/** used for aborting pending connections to a LiveKit server */
|
100
100
|
private abortController?: AbortController;
|
101
101
|
|
102
|
+
/** future holding client initiated connection attempt */
|
102
103
|
private connectFuture?: Future<void>;
|
103
104
|
|
105
|
+
/** future holding sdk initiated reconnection attempt */
|
106
|
+
private reconnectFuture?: Future<void>;
|
107
|
+
|
104
108
|
/**
|
105
109
|
* Creates a new Room, the primary construct for a LiveKit session.
|
106
110
|
* @param options
|
@@ -156,12 +160,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
156
160
|
.on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
|
157
161
|
.on(EngineEvent.DataPacketReceived, this.handleDataPacket)
|
158
162
|
.on(EngineEvent.Resuming, () => {
|
163
|
+
if (!this.reconnectFuture) {
|
164
|
+
this.reconnectFuture = new Future();
|
165
|
+
}
|
159
166
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
160
167
|
this.emit(RoomEvent.Reconnecting);
|
161
168
|
}
|
162
169
|
})
|
163
170
|
.on(EngineEvent.Resumed, () => {
|
164
171
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
172
|
+
this.reconnectFuture?.resolve();
|
173
|
+
this.reconnectFuture = undefined;
|
165
174
|
this.emit(RoomEvent.Reconnected);
|
166
175
|
this.updateSubscriptions();
|
167
176
|
})
|
@@ -193,145 +202,145 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
193
202
|
return DeviceManager.getInstance().getDevices(kind, requestPermissions);
|
194
203
|
}
|
195
204
|
|
196
|
-
connect =
|
205
|
+
connect = (url: string, token: string, opts?: RoomConnectOptions): Promise<void> => {
|
197
206
|
if (this.state === ConnectionState.Connected) {
|
198
207
|
// when the state is reconnecting or connected, this function returns immediately
|
199
208
|
log.warn(`already connected to room ${this.name}`);
|
200
|
-
return;
|
209
|
+
return Promise.resolve();
|
201
210
|
}
|
202
211
|
|
203
212
|
if (this.connectFuture) {
|
204
213
|
return this.connectFuture.promise;
|
214
|
+
} else if (this.reconnectFuture) {
|
215
|
+
this.connectFuture = this.reconnectFuture;
|
216
|
+
return this.connectFuture.promise;
|
205
217
|
}
|
206
|
-
|
218
|
+
const connectPromise = new Promise<void>(async (resolve, reject) => {
|
219
|
+
this.setAndEmitConnectionState(ConnectionState.Connecting);
|
220
|
+
if (!this.abortController || this.abortController.signal.aborted) {
|
221
|
+
this.abortController = new AbortController();
|
222
|
+
}
|
207
223
|
|
208
|
-
|
209
|
-
this.
|
210
|
-
}
|
224
|
+
// recreate engine if previously disconnected
|
225
|
+
this.createEngine();
|
211
226
|
|
212
|
-
|
213
|
-
this.createEngine();
|
227
|
+
this.acquireAudioContext();
|
214
228
|
|
215
|
-
|
229
|
+
if (opts?.rtcConfig) {
|
230
|
+
this.engine.rtcConfig = opts.rtcConfig;
|
231
|
+
}
|
216
232
|
|
217
|
-
|
218
|
-
|
219
|
-
|
233
|
+
this.connOptions = opts;
|
234
|
+
|
235
|
+
try {
|
236
|
+
const joinResponse = await this.engine.join(
|
237
|
+
url,
|
238
|
+
token,
|
239
|
+
{
|
240
|
+
autoSubscribe: opts?.autoSubscribe,
|
241
|
+
publishOnly: opts?.publishOnly,
|
242
|
+
adaptiveStream:
|
243
|
+
typeof this.options?.adaptiveStream === 'object'
|
244
|
+
? true
|
245
|
+
: this.options?.adaptiveStream,
|
246
|
+
},
|
247
|
+
this.abortController.signal,
|
248
|
+
);
|
249
|
+
log.debug(
|
250
|
+
`connected to Livekit Server version: ${joinResponse.serverVersion}, region: ${joinResponse.serverRegion}`,
|
251
|
+
);
|
220
252
|
|
221
|
-
|
253
|
+
if (!joinResponse.serverVersion) {
|
254
|
+
throw new UnsupportedServer('unknown server version');
|
255
|
+
}
|
222
256
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
autoSubscribe: opts?.autoSubscribe,
|
229
|
-
publishOnly: opts?.publishOnly,
|
230
|
-
adaptiveStream:
|
231
|
-
typeof this.options?.adaptiveStream === 'object' ? true : this.options?.adaptiveStream,
|
232
|
-
},
|
233
|
-
this.abortController.signal,
|
234
|
-
);
|
235
|
-
log.debug(
|
236
|
-
`connected to Livekit Server version: ${joinResponse.serverVersion}, region: ${joinResponse.serverRegion}`,
|
237
|
-
);
|
257
|
+
if (joinResponse.serverVersion === '0.15.1' && this.options.dynacast) {
|
258
|
+
log.debug('disabling dynacast due to server version');
|
259
|
+
// dynacast has a bug in 0.15.1, so we cannot use it then
|
260
|
+
this.options.dynacast = false;
|
261
|
+
}
|
238
262
|
|
239
|
-
|
240
|
-
|
241
|
-
|
263
|
+
const pi = joinResponse.participant!;
|
264
|
+
|
265
|
+
this.localParticipant.sid = pi.sid;
|
266
|
+
this.localParticipant.identity = pi.identity;
|
267
|
+
|
268
|
+
this.localParticipant.updateInfo(pi);
|
269
|
+
// forward metadata changed for the local participant
|
270
|
+
this.localParticipant
|
271
|
+
.on(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged)
|
272
|
+
.on(ParticipantEvent.TrackMuted, this.onLocalTrackMuted)
|
273
|
+
.on(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted)
|
274
|
+
.on(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished)
|
275
|
+
.on(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished)
|
276
|
+
.on(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged)
|
277
|
+
.on(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError)
|
278
|
+
.on(
|
279
|
+
ParticipantEvent.ParticipantPermissionsChanged,
|
280
|
+
this.onLocalParticipantPermissionsChanged,
|
281
|
+
);
|
242
282
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
283
|
+
// populate remote participants, these should not trigger new events
|
284
|
+
joinResponse.otherParticipants.forEach((info) => {
|
285
|
+
if (
|
286
|
+
info.sid !== this.localParticipant.sid &&
|
287
|
+
info.identity !== this.localParticipant.identity
|
288
|
+
) {
|
289
|
+
this.getOrCreateParticipant(info.sid, info);
|
290
|
+
} else {
|
291
|
+
log.warn('received info to create local participant as remote participant', {
|
292
|
+
info,
|
293
|
+
localParticipant: this.localParticipant,
|
294
|
+
});
|
295
|
+
}
|
296
|
+
});
|
248
297
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
.on(ParticipantEvent.TrackMuted, this.onLocalTrackMuted)
|
259
|
-
.on(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted)
|
260
|
-
.on(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished)
|
261
|
-
.on(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished)
|
262
|
-
.on(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged)
|
263
|
-
.on(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError)
|
264
|
-
.on(
|
265
|
-
ParticipantEvent.ParticipantPermissionsChanged,
|
266
|
-
this.onLocalParticipantPermissionsChanged,
|
267
|
-
);
|
298
|
+
this.name = joinResponse.room!.name;
|
299
|
+
this.sid = joinResponse.room!.sid;
|
300
|
+
this.metadata = joinResponse.room!.metadata;
|
301
|
+
this.emit(RoomEvent.SignalConnected);
|
302
|
+
} catch (err) {
|
303
|
+
this.recreateEngine();
|
304
|
+
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish);
|
305
|
+
reject(new ConnectionError('could not establish signal connection'));
|
306
|
+
}
|
268
307
|
|
269
|
-
//
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
)
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
308
|
+
// don't return until ICE connected
|
309
|
+
const connectTimeout = setTimeout(() => {
|
310
|
+
// timeout
|
311
|
+
this.recreateEngine();
|
312
|
+
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish);
|
313
|
+
reject(new ConnectionError('could not connect PeerConnection after timeout'));
|
314
|
+
}, maxICEConnectTimeout);
|
315
|
+
const abortHandler = () => {
|
316
|
+
log.warn('closing engine');
|
317
|
+
clearTimeout(connectTimeout);
|
318
|
+
this.recreateEngine();
|
319
|
+
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish);
|
320
|
+
reject(new ConnectionError('room connection has been cancelled'));
|
321
|
+
};
|
322
|
+
if (this.abortController?.signal.aborted) {
|
323
|
+
abortHandler();
|
324
|
+
}
|
325
|
+
this.abortController?.signal.addEventListener('abort', abortHandler);
|
326
|
+
|
327
|
+
this.engine.once(EngineEvent.Connected, () => {
|
328
|
+
clearTimeout(connectTimeout);
|
329
|
+
this.abortController?.signal.removeEventListener('abort', abortHandler);
|
330
|
+
// also hook unload event
|
331
|
+
if (isWeb()) {
|
332
|
+
window.addEventListener('beforeunload', this.onBeforeUnload);
|
333
|
+
navigator.mediaDevices?.addEventListener('devicechange', this.handleDeviceChange);
|
281
334
|
}
|
335
|
+
this.setAndEmitConnectionState(ConnectionState.Connected);
|
336
|
+
resolve();
|
282
337
|
});
|
283
|
-
|
284
|
-
this.name = joinResponse.room!.name;
|
285
|
-
this.sid = joinResponse.room!.sid;
|
286
|
-
this.metadata = joinResponse.room!.metadata;
|
287
|
-
this.emit(RoomEvent.SignalConnected);
|
288
|
-
} catch (err) {
|
289
|
-
this.recreateEngine();
|
290
|
-
this.setAndEmitConnectionState(
|
291
|
-
ConnectionState.Disconnected,
|
292
|
-
new ConnectionError('could not establish signal connection'),
|
293
|
-
);
|
294
|
-
throw err;
|
295
|
-
}
|
296
|
-
|
297
|
-
// don't return until ICE connected
|
298
|
-
const connectTimeout = setTimeout(() => {
|
299
|
-
// timeout
|
300
|
-
this.recreateEngine();
|
301
|
-
this.setAndEmitConnectionState(
|
302
|
-
ConnectionState.Disconnected,
|
303
|
-
new ConnectionError('could not connect PeerConnection after timeout'),
|
304
|
-
);
|
305
|
-
}, maxICEConnectTimeout);
|
306
|
-
const abortHandler = () => {
|
307
|
-
log.warn('closing engine');
|
308
|
-
clearTimeout(connectTimeout);
|
309
|
-
this.recreateEngine();
|
310
|
-
this.setAndEmitConnectionState(
|
311
|
-
ConnectionState.Disconnected,
|
312
|
-
new ConnectionError('room connection has been cancelled'),
|
313
|
-
);
|
314
|
-
};
|
315
|
-
if (this.abortController?.signal.aborted) {
|
316
|
-
abortHandler();
|
317
|
-
}
|
318
|
-
this.abortController?.signal.addEventListener('abort', abortHandler);
|
319
|
-
|
320
|
-
this.engine.once(EngineEvent.Connected, () => {
|
321
|
-
clearTimeout(connectTimeout);
|
322
|
-
this.abortController?.signal.removeEventListener('abort', abortHandler);
|
323
|
-
// also hook unload event
|
324
|
-
if (isWeb()) {
|
325
|
-
window.addEventListener('beforeunload', this.onBeforeUnload);
|
326
|
-
navigator.mediaDevices?.addEventListener('devicechange', this.handleDeviceChange);
|
327
|
-
}
|
328
|
-
this.setAndEmitConnectionState(ConnectionState.Connected);
|
329
338
|
});
|
339
|
+
this.connectFuture = new Future(connectPromise);
|
330
340
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
}
|
341
|
+
this.connectFuture.promise.finally(() => (this.connectFuture = undefined));
|
342
|
+
|
343
|
+
return this.connectFuture.promise;
|
335
344
|
};
|
336
345
|
|
337
346
|
/**
|
@@ -339,11 +348,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
339
348
|
*/
|
340
349
|
disconnect = async (stopTracks = true) => {
|
341
350
|
log.info('disconnect from room', { identity: this.localParticipant.identity });
|
342
|
-
if (this.state === ConnectionState.Connecting) {
|
351
|
+
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
|
343
352
|
// try aborting pending connection attempt
|
344
353
|
log.warn('abort connection attempt');
|
345
354
|
this.abortController?.abort();
|
346
|
-
|
355
|
+
// in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
|
356
|
+
this.connectFuture?.reject(new ConnectionError('Client initiated disconnect'));
|
357
|
+
this.connectFuture = undefined;
|
347
358
|
}
|
348
359
|
// send leave
|
349
360
|
if (this.engine?.client.isConnected) {
|
@@ -353,7 +364,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
353
364
|
if (this.engine) {
|
354
365
|
this.engine.close();
|
355
366
|
}
|
356
|
-
|
357
367
|
this.handleDisconnect(stopTracks, DisconnectReason.CLIENT_INITIATED);
|
358
368
|
/* @ts-ignore */
|
359
369
|
this.engine = undefined;
|
@@ -571,6 +581,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
571
581
|
}
|
572
582
|
|
573
583
|
private handleRestarting = () => {
|
584
|
+
if (!this.reconnectFuture) {
|
585
|
+
this.reconnectFuture = new Future();
|
586
|
+
}
|
574
587
|
// also unwind existing participants & existing subscriptions
|
575
588
|
for (const p of this.participants.values()) {
|
576
589
|
this.handleParticipantDisconnected(p.sid, p);
|
@@ -587,6 +600,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
587
600
|
});
|
588
601
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
589
602
|
this.emit(RoomEvent.Reconnected);
|
603
|
+
this.reconnectFuture?.resolve();
|
604
|
+
this.reconnectFuture = undefined;
|
590
605
|
|
591
606
|
// rehydrate participants
|
592
607
|
if (joinResponse.participant) {
|
@@ -630,6 +645,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
630
645
|
if (this.state === ConnectionState.Disconnected) {
|
631
646
|
return;
|
632
647
|
}
|
648
|
+
// reject potentially ongoing reconnection attempt
|
649
|
+
if (this.connectFuture === this.reconnectFuture) {
|
650
|
+
this.connectFuture?.reject(undefined);
|
651
|
+
this.connectFuture = undefined;
|
652
|
+
this.reconnectFuture = undefined;
|
653
|
+
}
|
654
|
+
|
633
655
|
this.participants.forEach((p) => {
|
634
656
|
p.tracks.forEach((pub) => {
|
635
657
|
p.unpublishTrack(pub.trackSid);
|
@@ -1030,35 +1052,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1030
1052
|
}
|
1031
1053
|
}
|
1032
1054
|
|
1033
|
-
private setAndEmitConnectionState(state: ConnectionState
|
1055
|
+
private setAndEmitConnectionState(state: ConnectionState): boolean {
|
1034
1056
|
if (state === this.state) {
|
1035
1057
|
// unchanged
|
1036
1058
|
return false;
|
1037
1059
|
}
|
1038
|
-
switch (state) {
|
1039
|
-
case ConnectionState.Connecting:
|
1040
|
-
case ConnectionState.Reconnecting:
|
1041
|
-
if (!this.connectFuture) {
|
1042
|
-
// reuse existing connect future if possible
|
1043
|
-
this.connectFuture = new Future<void>();
|
1044
|
-
}
|
1045
|
-
break;
|
1046
|
-
case ConnectionState.Connected:
|
1047
|
-
if (this.connectFuture) {
|
1048
|
-
this.connectFuture.resolve();
|
1049
|
-
this.connectFuture = undefined;
|
1050
|
-
}
|
1051
|
-
break;
|
1052
|
-
case ConnectionState.Disconnected:
|
1053
|
-
if (this.connectFuture) {
|
1054
|
-
error ??= new Error('disconnected from Room');
|
1055
|
-
this.connectFuture.reject(error);
|
1056
|
-
this.connectFuture = undefined;
|
1057
|
-
}
|
1058
|
-
break;
|
1059
|
-
default:
|
1060
|
-
// nothing
|
1061
|
-
}
|
1062
1060
|
this.state = state;
|
1063
1061
|
this.emit(RoomEvent.ConnectionStateChanged, this.state);
|
1064
1062
|
return true;
|
@@ -722,10 +722,20 @@ export default class LocalParticipant extends Participant {
|
|
722
722
|
track.sender
|
723
723
|
) {
|
724
724
|
try {
|
725
|
-
this.engine.
|
726
|
-
|
725
|
+
this.engine.removeTrack(track.sender);
|
726
|
+
if (track instanceof LocalVideoTrack) {
|
727
|
+
for (const [, trackInfo] of track.simulcastCodecs) {
|
728
|
+
if (trackInfo.sender) {
|
729
|
+
this.engine.removeTrack(trackInfo.sender);
|
730
|
+
trackInfo.sender = undefined;
|
731
|
+
}
|
732
|
+
}
|
733
|
+
track.simulcastCodecs.clear();
|
734
|
+
}
|
727
735
|
} catch (e) {
|
728
|
-
log.warn('failed to
|
736
|
+
log.warn('failed to unpublish track', { error: e, method: 'unpublishTrack' });
|
737
|
+
} finally {
|
738
|
+
this.engine.negotiate();
|
729
739
|
}
|
730
740
|
}
|
731
741
|
|
@@ -27,17 +27,15 @@ export class SimulcastTrackInfo {
|
|
27
27
|
const refreshSubscribedCodecAfterNewCodec = 5000;
|
28
28
|
|
29
29
|
export default class LocalVideoTrack extends LocalTrack {
|
30
|
-
/* internal */
|
30
|
+
/* @internal */
|
31
31
|
signalClient?: SignalClient;
|
32
32
|
|
33
33
|
private prevStats?: Map<string, VideoSenderStats>;
|
34
34
|
|
35
35
|
private encodings?: RTCRtpEncodingParameters[];
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
SimulcastTrackInfo
|
40
|
-
>();
|
37
|
+
/* @internal */
|
38
|
+
simulcastCodecs: Map<VideoCodec, SimulcastTrackInfo> = new Map<VideoCodec, SimulcastTrackInfo>();
|
41
39
|
|
42
40
|
private subscribedCodecs?: SubscribedCodec[];
|
43
41
|
|
@@ -78,9 +76,7 @@ export default class LocalVideoTrack extends LocalTrack {
|
|
78
76
|
this._mediaStreamTrack.getConstraints();
|
79
77
|
this.simulcastCodecs.forEach((trackInfo) => {
|
80
78
|
trackInfo.mediaStreamTrack.stop();
|
81
|
-
trackInfo.sender = undefined;
|
82
79
|
});
|
83
|
-
this.simulcastCodecs.clear();
|
84
80
|
super.stop();
|
85
81
|
}
|
86
82
|
|
@@ -8,7 +8,7 @@ import { TrackPublication } from './TrackPublication';
|
|
8
8
|
import { RemoteTrack } from './types';
|
9
9
|
|
10
10
|
export default class RemoteTrackPublication extends TrackPublication {
|
11
|
-
track?: RemoteTrack;
|
11
|
+
track?: RemoteTrack = undefined;
|
12
12
|
|
13
13
|
/** @internal */
|
14
14
|
protected allowed = true;
|
@@ -10,9 +10,6 @@ import { AdaptiveStreamSettings } from './types';
|
|
10
10
|
const REACTION_DELAY = 100;
|
11
11
|
|
12
12
|
export default class RemoteVideoTrack extends RemoteTrack {
|
13
|
-
/** @internal */
|
14
|
-
receiver?: RTCRtpReceiver;
|
15
|
-
|
16
13
|
private prevStats?: VideoReceiverStats;
|
17
14
|
|
18
15
|
private elementInfos: ElementInfo[] = [];
|
package/src/room/track/create.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import DeviceManager from '../DeviceManager';
|
1
2
|
import { TrackInvalidError } from '../errors';
|
2
3
|
import { mediaTrackToLocalTrack } from '../participant/publishUtils';
|
3
4
|
import { audioDefaults, videoDefaults } from './defaults';
|
@@ -30,7 +31,21 @@ export async function createLocalTracks(
|
|
30
31
|
|
31
32
|
const opts = mergeDefaultOptions(options, audioDefaults, videoDefaults);
|
32
33
|
const constraints = constraintsForOptions(opts);
|
33
|
-
|
34
|
+
|
35
|
+
// Keep a reference to the promise on DeviceManager and await it in getLocalDevices()
|
36
|
+
// works around iOS Safari Bug https://bugs.webkit.org/show_bug.cgi?id=179363
|
37
|
+
const mediaPromise = navigator.mediaDevices.getUserMedia(constraints);
|
38
|
+
|
39
|
+
if (options.audio) {
|
40
|
+
DeviceManager.userMediaPromiseMap.set('audioinput', mediaPromise);
|
41
|
+
mediaPromise.catch(() => DeviceManager.userMediaPromiseMap.delete('audioinput'));
|
42
|
+
}
|
43
|
+
if (options.video) {
|
44
|
+
DeviceManager.userMediaPromiseMap.set('videoinput', mediaPromise);
|
45
|
+
mediaPromise.catch(() => DeviceManager.userMediaPromiseMap.delete('videoinput'));
|
46
|
+
}
|
47
|
+
|
48
|
+
const stream = await mediaPromise;
|
34
49
|
return stream.getTracks().map((mediaStreamTrack) => {
|
35
50
|
const isAudio = mediaStreamTrack.kind === 'audio';
|
36
51
|
let trackOptions = isAudio ? options!.audio : options!.video;
|
package/src/room/utils.ts
CHANGED
@@ -122,10 +122,12 @@ export class Future<T> {
|
|
122
122
|
|
123
123
|
reject!: (e: any) => void;
|
124
124
|
|
125
|
-
constructor() {
|
126
|
-
this.promise =
|
127
|
-
|
128
|
-
|
129
|
-
|
125
|
+
constructor(promise?: Promise<T>) {
|
126
|
+
this.promise =
|
127
|
+
promise ??
|
128
|
+
new Promise<T>((resolve, reject) => {
|
129
|
+
this.resolve = resolve;
|
130
|
+
this.reject = reject;
|
131
|
+
});
|
130
132
|
}
|
131
133
|
}
|