livekit-client 1.9.7 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +2972 -2583
- 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/api/SignalClient.d.ts +2 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +2 -3
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +2 -3
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +108 -10
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +513 -194
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +2 -4
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +6 -6
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +5 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +4 -6
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +2 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts +8 -0
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +33 -0
- package/dist/src/room/track/LocalTrack.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 +4 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts +1 -0
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +2 -4
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +4 -5
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +14 -5
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/processor/types.d.ts +19 -0
- package/dist/src/room/track/processor/types.d.ts.map +1 -0
- package/dist/src/room/track/types.d.ts +2 -1
- package/dist/src/room/track/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -1
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +2 -3
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +2 -3
- package/dist/ts4.2/src/index.d.ts +1 -0
- package/dist/ts4.2/src/proto/livekit_models.d.ts +126 -12
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +617 -254
- package/dist/ts4.2/src/room/PCTransport.d.ts +1 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +2 -4
- package/dist/ts4.2/src/room/Room.d.ts +6 -6
- package/dist/ts4.2/src/room/events.d.ts +5 -1
- package/dist/ts4.2/src/room/participant/Participant.d.ts +4 -6
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -1
- package/dist/ts4.2/src/room/participant/publishUtils.d.ts +8 -0
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +33 -0
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +4 -1
- package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +2 -4
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +4 -5
- package/dist/ts4.2/src/room/track/options.d.ts +14 -5
- package/dist/ts4.2/src/room/track/processor/types.d.ts +19 -0
- package/dist/ts4.2/src/room/track/types.d.ts +2 -1
- package/package.json +14 -15
- package/src/api/SignalClient.ts +8 -1
- package/src/connectionHelper/ConnectionCheck.ts +2 -3
- package/src/connectionHelper/checks/Checker.ts +2 -3
- package/src/index.ts +1 -0
- package/src/logger.ts +4 -4
- package/src/proto/google/protobuf/timestamp.ts +3 -3
- package/src/proto/livekit_models.ts +254 -161
- package/src/proto/livekit_rtc.ts +334 -180
- package/src/room/PCTransport.ts +1 -1
- package/src/room/RTCEngine.ts +4 -4
- package/src/room/Room.ts +67 -12
- package/src/room/events.ts +4 -0
- package/src/room/participant/LocalParticipant.ts +33 -5
- package/src/room/participant/Participant.ts +4 -5
- package/src/room/participant/RemoteParticipant.ts +8 -4
- package/src/room/participant/publishUtils.ts +47 -20
- package/src/room/track/LocalTrack.ts +180 -57
- package/src/room/track/LocalVideoTrack.ts +98 -33
- package/src/room/track/RemoteTrackPublication.ts +8 -1
- package/src/room/track/RemoteVideoTrack.ts +23 -6
- package/src/room/track/Track.ts +5 -7
- package/src/room/track/TrackPublication.ts +4 -5
- package/src/room/track/options.ts +14 -5
- package/src/room/track/processor/types.ts +20 -0
- package/src/room/track/types.ts +2 -1
- package/src/room/utils.ts +6 -3
package/src/room/PCTransport.ts
CHANGED
package/src/room/RTCEngine.ts
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
import
|
2
|
-
import type TypedEventEmitter from 'typed-emitter';
|
1
|
+
import EventEmitter from 'eventemitter3';
|
3
2
|
import { SignalClient } from '../api/SignalClient';
|
4
3
|
import type { SignalOptions } from '../api/SignalClient';
|
5
4
|
import log from '../logger';
|
@@ -65,7 +64,7 @@ enum PCState {
|
|
65
64
|
}
|
66
65
|
|
67
66
|
/** @internal */
|
68
|
-
export default class RTCEngine extends
|
67
|
+
export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
69
68
|
publisher?: PCTransport;
|
70
69
|
|
71
70
|
subscriber?: PCTransport;
|
@@ -1046,6 +1045,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1046
1045
|
};
|
1047
1046
|
this.once(EngineEvent.Restarted, onRestarted);
|
1048
1047
|
this.once(EngineEvent.Disconnected, onDisconnected);
|
1048
|
+
this.once(EngineEvent.Closing, onDisconnected);
|
1049
1049
|
});
|
1050
1050
|
};
|
1051
1051
|
|
@@ -1172,7 +1172,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1172
1172
|
this.hasPublished = true;
|
1173
1173
|
|
1174
1174
|
const handleClosed = () => {
|
1175
|
-
log.
|
1175
|
+
log.warn('engine disconnected while negotiation was ongoing');
|
1176
1176
|
cleanup();
|
1177
1177
|
resolve();
|
1178
1178
|
return;
|
package/src/room/Room.ts
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
import
|
2
|
-
import type TypedEmitter from 'typed-emitter';
|
1
|
+
import EventEmitter from 'eventemitter3';
|
3
2
|
import 'webrtc-adapter';
|
4
3
|
import { toProtoSessionDescription } from '../api/SignalClient';
|
5
4
|
import log from '../logger';
|
@@ -18,6 +17,7 @@ import {
|
|
18
17
|
Room as RoomModel,
|
19
18
|
ServerInfo,
|
20
19
|
SpeakerInfo,
|
20
|
+
SubscriptionError,
|
21
21
|
TrackInfo,
|
22
22
|
TrackSource,
|
23
23
|
TrackType,
|
@@ -29,6 +29,7 @@ import {
|
|
29
29
|
SimulateScenario,
|
30
30
|
StreamStateUpdate,
|
31
31
|
SubscriptionPermissionUpdate,
|
32
|
+
SubscriptionResponse,
|
32
33
|
} from '../proto/livekit_rtc';
|
33
34
|
import DeviceManager from './DeviceManager';
|
34
35
|
import RTCEngine from './RTCEngine';
|
@@ -63,6 +64,7 @@ import {
|
|
63
64
|
createDummyVideoStreamTrack,
|
64
65
|
getEmptyAudioStreamTrack,
|
65
66
|
isCloud,
|
67
|
+
isSafari,
|
66
68
|
isWeb,
|
67
69
|
supportsSetSinkId,
|
68
70
|
unpackStreamId,
|
@@ -88,7 +90,7 @@ export const RoomState = ConnectionState;
|
|
88
90
|
*
|
89
91
|
* @noInheritDoc
|
90
92
|
*/
|
91
|
-
class Room extends
|
93
|
+
class Room extends EventEmitter<RoomEventCallbacks> {
|
92
94
|
state: ConnectionState = ConnectionState.Disconnected;
|
93
95
|
|
94
96
|
/** map of sid: [[RemoteParticipant]] */
|
@@ -138,7 +140,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
138
140
|
*/
|
139
141
|
constructor(options?: RoomOptions) {
|
140
142
|
super();
|
141
|
-
this.setMaxListeners(100);
|
142
143
|
this.participants = new Map();
|
143
144
|
this.cachedParticipantSids = [];
|
144
145
|
this.identityToSid = new Map();
|
@@ -207,6 +208,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
207
208
|
this.engine.client.onStreamStateUpdate = this.handleStreamStateUpdate;
|
208
209
|
this.engine.client.onSubscriptionPermissionUpdate = this.handleSubscriptionPermissionUpdate;
|
209
210
|
this.engine.client.onConnectionQuality = this.handleConnectionQualityUpdate;
|
211
|
+
this.engine.client.onSubscriptionError = this.handleSubscriptionError;
|
210
212
|
|
211
213
|
this.engine
|
212
214
|
.on(
|
@@ -492,6 +494,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
492
494
|
// capturing both 'pagehide' and 'beforeunload' to capture broadest set of browser behaviors
|
493
495
|
window.addEventListener('pagehide', this.onPageLeave);
|
494
496
|
window.addEventListener('beforeunload', this.onPageLeave);
|
497
|
+
}
|
498
|
+
if (isWeb()) {
|
499
|
+
document.addEventListener('freeze', this.onPageLeave);
|
495
500
|
navigator.mediaDevices?.addEventListener('devicechange', this.handleDeviceChange);
|
496
501
|
}
|
497
502
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
@@ -654,8 +659,31 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
654
659
|
*/
|
655
660
|
async startAudio() {
|
656
661
|
await this.acquireAudioContext();
|
657
|
-
|
658
662
|
const elements: Array<HTMLMediaElement> = [];
|
663
|
+
|
664
|
+
if (isSafari()) {
|
665
|
+
/**
|
666
|
+
* iOS Safari blocks audio element playback if
|
667
|
+
* - user is not publishing audio themselves and
|
668
|
+
* - no other audio source is playing
|
669
|
+
*
|
670
|
+
* as a workaround, we create an audio element with an empty track, so that
|
671
|
+
* silent audio is always playing
|
672
|
+
*/
|
673
|
+
const audioId = 'livekit-dummy-audio-el';
|
674
|
+
let dummyAudioEl = document.getElementById(audioId) as HTMLAudioElement | null;
|
675
|
+
if (!dummyAudioEl) {
|
676
|
+
dummyAudioEl = document.createElement('audio');
|
677
|
+
dummyAudioEl.autoplay = true;
|
678
|
+
dummyAudioEl.hidden = true;
|
679
|
+
const track = getEmptyAudioStreamTrack();
|
680
|
+
track.enabled = true;
|
681
|
+
dummyAudioEl.srcObject = new MediaStream([track]);
|
682
|
+
document.body.append(dummyAudioEl);
|
683
|
+
}
|
684
|
+
elements.push(dummyAudioEl);
|
685
|
+
}
|
686
|
+
|
659
687
|
this.participants.forEach((p) => {
|
660
688
|
p.audioTracks.forEach((t) => {
|
661
689
|
if (t.track) {
|
@@ -962,6 +990,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
962
990
|
if (isWeb()) {
|
963
991
|
window.removeEventListener('beforeunload', this.onPageLeave);
|
964
992
|
window.removeEventListener('pagehide', this.onPageLeave);
|
993
|
+
window.removeEventListener('freeze', this.onPageLeave);
|
965
994
|
navigator.mediaDevices?.removeEventListener('devicechange', this.handleDeviceChange);
|
966
995
|
}
|
967
996
|
} finally {
|
@@ -1114,6 +1143,21 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1114
1143
|
pub.setAllowed(update.allowed);
|
1115
1144
|
};
|
1116
1145
|
|
1146
|
+
private handleSubscriptionError = (update: SubscriptionResponse) => {
|
1147
|
+
const participant = Array.from(this.participants.values()).find((p) =>
|
1148
|
+
p.tracks.has(update.trackSid),
|
1149
|
+
);
|
1150
|
+
if (!participant) {
|
1151
|
+
return;
|
1152
|
+
}
|
1153
|
+
const pub = participant.getTrackPublication(update.trackSid);
|
1154
|
+
if (!pub) {
|
1155
|
+
return;
|
1156
|
+
}
|
1157
|
+
|
1158
|
+
pub.setSubscriptionError(update.err);
|
1159
|
+
};
|
1160
|
+
|
1117
1161
|
private handleDataPacket = (userPacket: UserPacket, kind: DataPacket_Kind) => {
|
1118
1162
|
// find the participant
|
1119
1163
|
const participant = this.participants.get(userPacket.participantSid);
|
@@ -1280,6 +1324,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1280
1324
|
.on(ParticipantEvent.TrackSubscriptionStatusChanged, (pub, status) => {
|
1281
1325
|
this.emitWhenConnected(RoomEvent.TrackSubscriptionStatusChanged, pub, status, participant);
|
1282
1326
|
})
|
1327
|
+
.on(ParticipantEvent.TrackSubscriptionFailed, (trackSid, error) => {
|
1328
|
+
this.emit(RoomEvent.TrackSubscriptionFailed, trackSid, participant, error);
|
1329
|
+
})
|
1283
1330
|
.on(ParticipantEvent.TrackSubscriptionPermissionChanged, (pub, status) => {
|
1284
1331
|
this.emitWhenConnected(
|
1285
1332
|
RoomEvent.TrackSubscriptionPermissionChanged,
|
@@ -1399,9 +1446,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1399
1446
|
return true;
|
1400
1447
|
}
|
1401
1448
|
|
1402
|
-
private emitWhenConnected<
|
1403
|
-
event:
|
1404
|
-
...args:
|
1449
|
+
private emitWhenConnected<T extends EventEmitter.EventNames<RoomEventCallbacks>>(
|
1450
|
+
event: T,
|
1451
|
+
...args: EventEmitter.EventArgs<RoomEventCallbacks, T>
|
1405
1452
|
): boolean {
|
1406
1453
|
if (this.state === ConnectionState.Connected) {
|
1407
1454
|
return this.emit(event, ...args);
|
@@ -1578,10 +1625,14 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1578
1625
|
}
|
1579
1626
|
|
1580
1627
|
// /** @internal */
|
1581
|
-
emit<
|
1582
|
-
event:
|
1583
|
-
...args:
|
1628
|
+
emit<T extends EventEmitter.EventNames<RoomEventCallbacks>>(
|
1629
|
+
event: T,
|
1630
|
+
...args: EventEmitter.EventArgs<RoomEventCallbacks, T>
|
1584
1631
|
): boolean {
|
1632
|
+
// emit<E extends keyof RoomEventCallbacks>(
|
1633
|
+
// event: E,
|
1634
|
+
// ...args: Parameters<RoomEventCallbacks[E]>
|
1635
|
+
// ): boolean {
|
1585
1636
|
// active speaker updates are too spammy
|
1586
1637
|
if (event !== RoomEvent.ActiveSpeakersChanged) {
|
1587
1638
|
log.debug(`room event ${event}`, { event, args });
|
@@ -1609,7 +1660,11 @@ export type RoomEventCallbacks = {
|
|
1609
1660
|
publication: RemoteTrackPublication,
|
1610
1661
|
participant: RemoteParticipant,
|
1611
1662
|
) => void;
|
1612
|
-
trackSubscriptionFailed: (
|
1663
|
+
trackSubscriptionFailed: (
|
1664
|
+
trackSid: string,
|
1665
|
+
participant: RemoteParticipant,
|
1666
|
+
reason?: SubscriptionError,
|
1667
|
+
) => void;
|
1613
1668
|
trackUnpublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void;
|
1614
1669
|
trackUnsubscribed: (
|
1615
1670
|
track: RemoteTrack,
|
package/src/room/events.ts
CHANGED
@@ -143,8 +143,11 @@ export default class LocalParticipant extends Participant {
|
|
143
143
|
};
|
144
144
|
|
145
145
|
private handleDisconnected = () => {
|
146
|
-
this.reconnectFuture
|
147
|
-
|
146
|
+
if (this.reconnectFuture) {
|
147
|
+
this.reconnectFuture.promise.catch((e) => log.warn(e));
|
148
|
+
this.reconnectFuture?.reject?.('Got disconnected during reconnection attempt');
|
149
|
+
this.reconnectFuture = undefined;
|
150
|
+
}
|
148
151
|
};
|
149
152
|
|
150
153
|
/**
|
@@ -625,8 +628,8 @@ export default class LocalParticipant extends Participant {
|
|
625
628
|
// for svc codecs, disable simulcast and use vp8 for backup codec
|
626
629
|
if (track instanceof LocalVideoTrack) {
|
627
630
|
if (isSVCCodec(opts.videoCodec)) {
|
628
|
-
// set scalabilityMode to '
|
629
|
-
opts.scalabilityMode = opts.scalabilityMode ?? '
|
631
|
+
// set scalabilityMode to 'L3T3_KEY' by default
|
632
|
+
opts.scalabilityMode = opts.scalabilityMode ?? 'L3T3_KEY';
|
630
633
|
}
|
631
634
|
|
632
635
|
// set up backup
|
@@ -647,6 +650,16 @@ export default class LocalParticipant extends Participant {
|
|
647
650
|
enableSimulcastLayers: true,
|
648
651
|
},
|
649
652
|
];
|
653
|
+
} else if (opts.videoCodec) {
|
654
|
+
// pass codec info to sfu so it can prefer codec for the client which don't support
|
655
|
+
// setCodecPreferences
|
656
|
+
req.simulcastCodecs = [
|
657
|
+
{
|
658
|
+
codec: opts.videoCodec,
|
659
|
+
cid: track.mediaStreamTrack.id,
|
660
|
+
enableSimulcastLayers: opts.simulcast ?? false,
|
661
|
+
},
|
662
|
+
];
|
650
663
|
}
|
651
664
|
}
|
652
665
|
|
@@ -656,7 +669,12 @@ export default class LocalParticipant extends Participant {
|
|
656
669
|
dims.height,
|
657
670
|
opts,
|
658
671
|
);
|
659
|
-
req.layers = videoLayersFromEncodings(
|
672
|
+
req.layers = videoLayersFromEncodings(
|
673
|
+
req.width,
|
674
|
+
req.height,
|
675
|
+
encodings,
|
676
|
+
isSVCCodec(opts.videoCodec),
|
677
|
+
);
|
660
678
|
} else if (track.kind === Track.Kind.Audio) {
|
661
679
|
encodings = [
|
662
680
|
{
|
@@ -850,6 +868,16 @@ export default class LocalParticipant extends Participant {
|
|
850
868
|
trackSender
|
851
869
|
) {
|
852
870
|
try {
|
871
|
+
for (const transceiver of this.engine.publisher.pc.getTransceivers()) {
|
872
|
+
// if sender is not currently sending (after replaceTrack(null))
|
873
|
+
// removeTrack would have no effect.
|
874
|
+
// to ensure we end up successfully removing the track, manually set
|
875
|
+
// the transceiver to inactive
|
876
|
+
if (transceiver.sender === trackSender) {
|
877
|
+
transceiver.direction = 'inactive';
|
878
|
+
negotiationNeeded = true;
|
879
|
+
}
|
880
|
+
}
|
853
881
|
if (this.engine.removeTrack(trackSender)) {
|
854
882
|
negotiationNeeded = true;
|
855
883
|
}
|
@@ -1,11 +1,11 @@
|
|
1
|
-
import
|
2
|
-
import type TypedEmitter from 'typed-emitter';
|
1
|
+
import EventEmitter from 'eventemitter3';
|
3
2
|
import log from '../../logger';
|
4
3
|
import {
|
5
4
|
DataPacket_Kind,
|
6
5
|
ParticipantInfo,
|
7
6
|
ParticipantPermission,
|
8
7
|
ConnectionQuality as ProtoQuality,
|
8
|
+
SubscriptionError,
|
9
9
|
} from '../../proto/livekit_models';
|
10
10
|
import { ParticipantEvent, TrackEvent } from '../events';
|
11
11
|
import type LocalTrackPublication from '../track/LocalTrackPublication';
|
@@ -34,7 +34,7 @@ function qualityFromProto(q: ProtoQuality): ConnectionQuality {
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
|
37
|
-
export default class Participant extends
|
37
|
+
export default class Participant extends EventEmitter<ParticipantEventCallbacks> {
|
38
38
|
protected participantInfo?: ParticipantInfo;
|
39
39
|
|
40
40
|
audioTracks: Map<string, TrackPublication>;
|
@@ -71,7 +71,6 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
71
71
|
/** @internal */
|
72
72
|
constructor(sid: string, identity: string, name?: string, metadata?: string) {
|
73
73
|
super();
|
74
|
-
this.setMaxListeners(100);
|
75
74
|
this.sid = sid;
|
76
75
|
this.identity = identity;
|
77
76
|
this.name = name;
|
@@ -265,7 +264,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
265
264
|
export type ParticipantEventCallbacks = {
|
266
265
|
trackPublished: (publication: RemoteTrackPublication) => void;
|
267
266
|
trackSubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void;
|
268
|
-
trackSubscriptionFailed: (trackSid: string) => void;
|
267
|
+
trackSubscriptionFailed: (trackSid: string, reason?: SubscriptionError) => void;
|
269
268
|
trackUnpublished: (publication: RemoteTrackPublication) => void;
|
270
269
|
trackUnsubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void;
|
271
270
|
trackMuted: (publication: TrackPublication) => void;
|
@@ -1,6 +1,7 @@
|
|
1
|
+
import type EventEmitter from 'eventemitter3';
|
1
2
|
import type { SignalClient } from '../../api/SignalClient';
|
2
3
|
import log from '../../logger';
|
3
|
-
import type { ParticipantInfo } from '../../proto/livekit_models';
|
4
|
+
import type { ParticipantInfo, SubscriptionError } from '../../proto/livekit_models';
|
4
5
|
import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc';
|
5
6
|
import { ParticipantEvent, TrackEvent } from '../events';
|
6
7
|
import RemoteAudioTrack from '../track/RemoteAudioTrack';
|
@@ -81,6 +82,9 @@ export default class RemoteParticipant extends Participant {
|
|
81
82
|
publication.on(TrackEvent.Unsubscribed, (previousTrack: RemoteTrack) => {
|
82
83
|
this.emit(ParticipantEvent.TrackUnsubscribed, previousTrack, publication);
|
83
84
|
});
|
85
|
+
publication.on(TrackEvent.SubscriptionFailed, (error: SubscriptionError) => {
|
86
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, publication.trackSid, error);
|
87
|
+
});
|
84
88
|
}
|
85
89
|
|
86
90
|
getTrack(source: Track.Source): RemoteTrackPublication | undefined {
|
@@ -342,9 +346,9 @@ export default class RemoteParticipant extends Participant {
|
|
342
346
|
}
|
343
347
|
|
344
348
|
/** @internal */
|
345
|
-
emit<
|
346
|
-
event:
|
347
|
-
...args:
|
349
|
+
emit<T extends EventEmitter.EventNames<ParticipantEventCallbacks>>(
|
350
|
+
event: T,
|
351
|
+
...args: EventEmitter.EventArgs<ParticipantEventCallbacks, T>
|
348
352
|
): boolean {
|
349
353
|
log.trace('participant event', { participant: this.sid, event, args });
|
350
354
|
return super.emit(event, ...args);
|
@@ -123,29 +123,25 @@ export function computeVideoEncodings(
|
|
123
123
|
if (scalabilityMode && isSVCCodec(videoCodec)) {
|
124
124
|
log.debug(`using svc with scalabilityMode ${scalabilityMode}`);
|
125
125
|
|
126
|
-
const
|
126
|
+
const sm = new ScalabilityMode(scalabilityMode);
|
127
127
|
|
128
|
-
|
129
|
-
switch (scalabilityMode) {
|
130
|
-
case 'L3T3':
|
131
|
-
for (let i = 0; i < 3; i += 1) {
|
132
|
-
encodings.push({
|
133
|
-
rid: videoRids[2 - i],
|
134
|
-
scaleResolutionDownBy: 2 ** i,
|
135
|
-
maxBitrate: videoEncoding.maxBitrate / 3 ** i,
|
136
|
-
/* @ts-ignore */
|
137
|
-
maxFramerate: original.encoding.maxFramerate,
|
138
|
-
/* @ts-ignore */
|
139
|
-
scalabilityMode: 'L3T3',
|
140
|
-
});
|
141
|
-
}
|
142
|
-
log.debug('encodings', encodings);
|
143
|
-
return encodings;
|
128
|
+
const encodings: RTCRtpEncodingParameters[] = [];
|
144
129
|
|
145
|
-
|
146
|
-
|
147
|
-
|
130
|
+
if (sm.spatial > 3) {
|
131
|
+
throw new Error(`unsupported scalabilityMode: ${scalabilityMode}`);
|
132
|
+
}
|
133
|
+
for (let i = 0; i < sm.spatial; i += 1) {
|
134
|
+
encodings.push({
|
135
|
+
rid: videoRids[2 - i],
|
136
|
+
maxBitrate: videoEncoding.maxBitrate / 3 ** i,
|
137
|
+
/* @ts-ignore */
|
138
|
+
maxFramerate: original.encoding.maxFramerate,
|
139
|
+
});
|
148
140
|
}
|
141
|
+
/* @ts-ignore */
|
142
|
+
encodings[0].scalabilityMode = scalabilityMode;
|
143
|
+
log.debug('encodings', encodings);
|
144
|
+
return encodings;
|
149
145
|
}
|
150
146
|
|
151
147
|
if (!useSimulcast) {
|
@@ -368,3 +364,34 @@ export function sortPresets(presets: Array<VideoPreset> | undefined) {
|
|
368
364
|
return 0;
|
369
365
|
});
|
370
366
|
}
|
367
|
+
|
368
|
+
/** @internal */
|
369
|
+
export class ScalabilityMode {
|
370
|
+
spatial: number;
|
371
|
+
|
372
|
+
temporal: number;
|
373
|
+
|
374
|
+
suffix: undefined | 'h' | '_KEY' | '_KEY_SHIFT';
|
375
|
+
|
376
|
+
constructor(scalabilityMode: string) {
|
377
|
+
const results = scalabilityMode.match(/^L(\d)T(\d)(h|_KEY|_KEY_SHIFT){0,1}$/);
|
378
|
+
if (!results) {
|
379
|
+
throw new Error('invalid scalability mode');
|
380
|
+
}
|
381
|
+
|
382
|
+
this.spatial = parseInt(results[1]);
|
383
|
+
this.temporal = parseInt(results[2]);
|
384
|
+
if (results.length > 3) {
|
385
|
+
switch (results[3]) {
|
386
|
+
case 'h':
|
387
|
+
case '_KEY':
|
388
|
+
case '_KEY_SHIFT':
|
389
|
+
this.suffix = results[3];
|
390
|
+
}
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
toString(): string {
|
395
|
+
return `L${this.spatial}T${this.temporal}${this.suffix ?? ''}`;
|
396
|
+
}
|
397
|
+
}
|