livekit-client 0.15.4 → 0.16.3
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/api/RequestQueue.d.ts +12 -0
- package/dist/api/RequestQueue.js +61 -0
- package/dist/api/RequestQueue.js.map +1 -0
- package/dist/api/SignalClient.d.ts +7 -3
- package/dist/api/SignalClient.js +25 -4
- package/dist/api/SignalClient.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +0 -10
- package/dist/proto/livekit_rtc.d.ts +15 -10
- package/dist/proto/livekit_rtc.js +36 -22
- package/dist/proto/livekit_rtc.js.map +1 -1
- package/dist/room/RTCEngine.d.ts +27 -6
- package/dist/room/RTCEngine.js +163 -46
- package/dist/room/RTCEngine.js.map +1 -1
- package/dist/room/Room.d.ts +50 -6
- package/dist/room/Room.js +128 -67
- package/dist/room/Room.js.map +1 -1
- package/dist/room/events.d.ts +13 -4
- package/dist/room/events.js +15 -6
- package/dist/room/events.js.map +1 -1
- package/dist/room/participant/LocalParticipant.d.ts +1 -2
- package/dist/room/participant/LocalParticipant.js +7 -8
- package/dist/room/participant/LocalParticipant.js.map +1 -1
- package/dist/room/participant/Participant.d.ts +30 -4
- package/dist/room/participant/Participant.js +2 -2
- package/dist/room/participant/Participant.js.map +1 -1
- package/dist/room/participant/RemoteParticipant.d.ts +3 -4
- package/dist/room/participant/RemoteParticipant.js +3 -0
- package/dist/room/participant/RemoteParticipant.js.map +1 -1
- package/dist/room/track/LocalAudioTrack.js +8 -1
- package/dist/room/track/LocalAudioTrack.js.map +1 -1
- package/dist/room/track/LocalTrackPublication.d.ts +2 -0
- package/dist/room/track/LocalTrackPublication.js.map +1 -1
- package/dist/room/track/LocalVideoTrack.d.ts +1 -5
- package/dist/room/track/LocalVideoTrack.js +12 -117
- package/dist/room/track/LocalVideoTrack.js.map +1 -1
- package/dist/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/room/track/RemoteTrackPublication.js +7 -1
- package/dist/room/track/RemoteTrackPublication.js.map +1 -1
- package/dist/room/track/RemoteVideoTrack.js +12 -7
- package/dist/room/track/RemoteVideoTrack.js.map +1 -1
- package/dist/room/track/Track.d.ts +16 -3
- package/dist/room/track/Track.js +30 -20
- package/dist/room/track/Track.js.map +1 -1
- package/dist/room/track/types.d.ts +4 -4
- package/dist/room/utils.d.ts +1 -0
- package/dist/room/utils.js +5 -21
- package/dist/room/utils.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +3 -4
- package/src/api/RequestQueue.ts +53 -0
- package/src/api/SignalClient.ts +33 -5
- package/src/index.ts +1 -1
- package/src/options.ts +0 -12
- package/src/proto/livekit_rtc.ts +55 -41
- package/src/room/RTCEngine.ts +198 -53
- package/src/room/Room.ts +227 -96
- package/src/room/events.ts +15 -4
- package/src/room/participant/LocalParticipant.ts +6 -7
- package/src/room/participant/Participant.ts +39 -4
- package/src/room/participant/RemoteParticipant.ts +9 -4
- package/src/room/track/LocalAudioTrack.ts +8 -1
- package/src/room/track/LocalTrackPublication.ts +3 -0
- package/src/room/track/LocalVideoTrack.ts +11 -142
- package/src/room/track/RemoteTrackPublication.ts +8 -2
- package/src/room/track/RemoteVideoTrack.ts +14 -7
- package/src/room/track/Track.ts +46 -24
- package/src/room/track/types.ts +4 -4
- package/src/room/utils.ts +4 -16
- package/src/version.ts +2 -2
package/src/room/Room.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
2
3
|
import { toProtoSessionDescription } from '../api/SignalClient';
|
3
4
|
import log from '../logger';
|
4
5
|
import { RoomConnectOptions, RoomOptions } from '../options';
|
@@ -7,7 +8,11 @@ import {
|
|
7
8
|
ParticipantInfo_State, Room as RoomModel, SpeakerInfo, UserPacket,
|
8
9
|
} from '../proto/livekit_models';
|
9
10
|
import {
|
10
|
-
ConnectionQualityUpdate,
|
11
|
+
ConnectionQualityUpdate,
|
12
|
+
JoinResponse,
|
13
|
+
SimulateScenario,
|
14
|
+
StreamStateUpdate,
|
15
|
+
SubscriptionPermissionUpdate,
|
11
16
|
} from '../proto/livekit_rtc';
|
12
17
|
import DeviceManager from './DeviceManager';
|
13
18
|
import { ConnectionError, UnsupportedServer } from './errors';
|
@@ -40,7 +45,7 @@ export enum RoomState {
|
|
40
45
|
*
|
41
46
|
* @noInheritDoc
|
42
47
|
*/
|
43
|
-
class Room extends EventEmitter {
|
48
|
+
class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>) {
|
44
49
|
state: RoomState = RoomState.Disconnected;
|
45
50
|
|
46
51
|
/** map of sid: [[RemoteParticipant]] */
|
@@ -113,48 +118,49 @@ class Room extends EventEmitter {
|
|
113
118
|
}
|
114
119
|
|
115
120
|
this.engine = new RTCEngine();
|
116
|
-
this.engine.client.signalLatency = this.options.expSignalLatency;
|
117
|
-
|
118
|
-
this.engine.on(
|
119
|
-
EngineEvent.MediaTrackAdded,
|
120
|
-
(
|
121
|
-
mediaTrack: MediaStreamTrack,
|
122
|
-
stream: MediaStream,
|
123
|
-
receiver?: RTCRtpReceiver,
|
124
|
-
) => {
|
125
|
-
this.onTrackAdded(mediaTrack, stream, receiver);
|
126
|
-
},
|
127
|
-
);
|
128
|
-
|
129
|
-
this.engine.on(EngineEvent.Disconnected, () => {
|
130
|
-
this.handleDisconnect();
|
131
|
-
});
|
132
121
|
|
122
|
+
this.engine.client.signalLatency = this.options.expSignalLatency;
|
133
123
|
this.engine.client.onParticipantUpdate = this.handleParticipantUpdates;
|
134
124
|
this.engine.client.onRoomUpdate = this.handleRoomUpdate;
|
135
125
|
this.engine.client.onSpeakersChanged = this.handleSpeakersChanged;
|
136
126
|
this.engine.client.onStreamStateUpdate = this.handleStreamStateUpdate;
|
137
127
|
this.engine.client.onSubscriptionPermissionUpdate = this.handleSubscriptionPermissionUpdate;
|
138
|
-
this.engine.on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate);
|
139
|
-
this.engine.on(EngineEvent.DataPacketReceived, this.handleDataPacket);
|
140
|
-
|
141
|
-
this.engine.on(EngineEvent.Reconnecting, () => {
|
142
|
-
this.state = RoomState.Reconnecting;
|
143
|
-
this.emit(RoomEvent.Reconnecting);
|
144
|
-
});
|
145
|
-
|
146
|
-
this.engine.on(EngineEvent.Reconnected, () => {
|
147
|
-
this.state = RoomState.Connected;
|
148
|
-
this.emit(RoomEvent.Reconnected);
|
149
|
-
});
|
150
|
-
|
151
|
-
this.engine.on(EngineEvent.SignalConnected, () => {
|
152
|
-
if (this.state === RoomState.Reconnecting) {
|
153
|
-
this.sendSyncState();
|
154
|
-
}
|
155
|
-
});
|
156
|
-
|
157
128
|
this.engine.client.onConnectionQuality = this.handleConnectionQualityUpdate;
|
129
|
+
|
130
|
+
this.engine
|
131
|
+
.on(
|
132
|
+
EngineEvent.MediaTrackAdded,
|
133
|
+
(
|
134
|
+
mediaTrack: MediaStreamTrack,
|
135
|
+
stream: MediaStream,
|
136
|
+
receiver?: RTCRtpReceiver,
|
137
|
+
) => {
|
138
|
+
this.onTrackAdded(mediaTrack, stream, receiver);
|
139
|
+
},
|
140
|
+
)
|
141
|
+
.on(EngineEvent.Disconnected, () => {
|
142
|
+
this.handleDisconnect();
|
143
|
+
})
|
144
|
+
.on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
|
145
|
+
.on(EngineEvent.DataPacketReceived, this.handleDataPacket)
|
146
|
+
.on(EngineEvent.Resuming, () => {
|
147
|
+
this.state = RoomState.Reconnecting;
|
148
|
+
this.emit(RoomEvent.Reconnecting);
|
149
|
+
this.emit(RoomEvent.StateChanged, this.state);
|
150
|
+
})
|
151
|
+
.on(EngineEvent.Resumed, () => {
|
152
|
+
this.state = RoomState.Connected;
|
153
|
+
this.emit(RoomEvent.Reconnected);
|
154
|
+
this.emit(RoomEvent.StateChanged, this.state);
|
155
|
+
this.updateSubscriptions();
|
156
|
+
})
|
157
|
+
.on(EngineEvent.SignalResumed, () => {
|
158
|
+
if (this.state === RoomState.Reconnecting) {
|
159
|
+
this.sendSyncState();
|
160
|
+
}
|
161
|
+
})
|
162
|
+
.on(EngineEvent.Restarting, this.handleRestarting)
|
163
|
+
.on(EngineEvent.Restarted, this.handleRestarted);
|
158
164
|
}
|
159
165
|
|
160
166
|
/**
|
@@ -202,6 +208,7 @@ class Room extends EventEmitter {
|
|
202
208
|
}
|
203
209
|
|
204
210
|
this.state = RoomState.Connected;
|
211
|
+
this.emit(RoomEvent.StateChanged, this.state);
|
205
212
|
const pi = joinResponse.participant!;
|
206
213
|
this.localParticipant = new LocalParticipant(
|
207
214
|
pi.sid,
|
@@ -213,10 +220,10 @@ class Room extends EventEmitter {
|
|
213
220
|
this.localParticipant.updateInfo(pi);
|
214
221
|
// forward metadata changed for the local participant
|
215
222
|
this.localParticipant
|
216
|
-
.on(ParticipantEvent.MetadataChanged, (metadata:
|
223
|
+
.on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
|
217
224
|
this.emit(RoomEvent.MetadataChanged, metadata, this.localParticipant);
|
218
225
|
})
|
219
|
-
.on(ParticipantEvent.ParticipantMetadataChanged, (metadata:
|
226
|
+
.on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
|
220
227
|
this.emit(RoomEvent.ParticipantMetadataChanged, metadata, this.localParticipant);
|
221
228
|
})
|
222
229
|
.on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
|
@@ -439,6 +446,45 @@ class Room extends EventEmitter {
|
|
439
446
|
);
|
440
447
|
}
|
441
448
|
|
449
|
+
private handleRestarting = () => {
|
450
|
+
this.state = RoomState.Reconnecting;
|
451
|
+
this.emit(RoomEvent.Reconnecting);
|
452
|
+
this.emit(RoomEvent.StateChanged, this.state);
|
453
|
+
|
454
|
+
// also unwind existing participants & existing subscriptions
|
455
|
+
for (const p of this.participants.values()) {
|
456
|
+
this.handleParticipantDisconnected(p.sid, p);
|
457
|
+
}
|
458
|
+
};
|
459
|
+
|
460
|
+
private handleRestarted = async (joinResponse: JoinResponse) => {
|
461
|
+
this.state = RoomState.Connected;
|
462
|
+
this.emit(RoomEvent.Reconnected);
|
463
|
+
this.emit(RoomEvent.StateChanged, this.state);
|
464
|
+
|
465
|
+
// rehydrate participants
|
466
|
+
if (joinResponse.participant) {
|
467
|
+
// with a restart, the sid will have changed, we'll map our understanding to it
|
468
|
+
this.localParticipant.sid = joinResponse.participant.sid;
|
469
|
+
this.handleParticipantUpdates([joinResponse.participant]);
|
470
|
+
}
|
471
|
+
this.handleParticipantUpdates(joinResponse.otherParticipants);
|
472
|
+
|
473
|
+
// unpublish & republish tracks
|
474
|
+
const localPubs: LocalTrackPublication[] = [];
|
475
|
+
this.localParticipant.tracks.forEach((pub) => {
|
476
|
+
if (pub.track) {
|
477
|
+
localPubs.push(pub);
|
478
|
+
}
|
479
|
+
});
|
480
|
+
|
481
|
+
await Promise.all(localPubs.map(async (pub) => {
|
482
|
+
const track = pub.track!;
|
483
|
+
this.localParticipant.unpublishTrack(track, false);
|
484
|
+
this.localParticipant.publishTrack(track, pub.options);
|
485
|
+
}));
|
486
|
+
};
|
487
|
+
|
442
488
|
private handleDisconnect(shouldStopTracks = true) {
|
443
489
|
if (this.state === RoomState.Disconnected) {
|
444
490
|
return;
|
@@ -469,12 +515,14 @@ class Room extends EventEmitter {
|
|
469
515
|
navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange);
|
470
516
|
this.state = RoomState.Disconnected;
|
471
517
|
this.emit(RoomEvent.Disconnected);
|
518
|
+
this.emit(RoomEvent.StateChanged, this.state);
|
472
519
|
}
|
473
520
|
|
474
521
|
private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
|
475
522
|
// handle changes to participant state, and send events
|
476
523
|
participantInfos.forEach((info) => {
|
477
|
-
if (info.sid === this.localParticipant.sid
|
524
|
+
if (info.sid === this.localParticipant.sid
|
525
|
+
|| info.identity === this.localParticipant.identity) {
|
478
526
|
this.localParticipant.updateInfo(info);
|
479
527
|
return;
|
480
528
|
}
|
@@ -676,66 +724,72 @@ class Room extends EventEmitter {
|
|
676
724
|
}
|
677
725
|
}
|
678
726
|
|
727
|
+
private createParticipant(id: string, info?: ParticipantInfo): RemoteParticipant {
|
728
|
+
let participant: RemoteParticipant;
|
729
|
+
if (info) {
|
730
|
+
participant = RemoteParticipant.fromParticipantInfo(
|
731
|
+
this.engine.client,
|
732
|
+
info,
|
733
|
+
);
|
734
|
+
} else {
|
735
|
+
participant = new RemoteParticipant(this.engine.client, id, '');
|
736
|
+
}
|
737
|
+
return participant;
|
738
|
+
}
|
739
|
+
|
679
740
|
private getOrCreateParticipant(
|
680
741
|
id: string,
|
681
742
|
info?: ParticipantInfo,
|
682
743
|
): RemoteParticipant {
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
(track
|
706
|
-
// monitor playback status
|
707
|
-
if (track.kind === Track.Kind.Audio) {
|
708
|
-
track.on(TrackEvent.AudioPlaybackStarted, this.handleAudioPlaybackStarted);
|
709
|
-
track.on(TrackEvent.AudioPlaybackFailed, this.handleAudioPlaybackFailed);
|
710
|
-
}
|
711
|
-
this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
|
712
|
-
})
|
713
|
-
.on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
|
714
|
-
this.emit(RoomEvent.TrackUnpublished, publication, participant);
|
715
|
-
})
|
716
|
-
.on(ParticipantEvent.TrackUnsubscribed,
|
717
|
-
(track: RemoteTrack, publication: RemoteTrackPublication) => {
|
718
|
-
this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
|
719
|
-
})
|
720
|
-
.on(ParticipantEvent.TrackSubscriptionFailed, (sid: string) => {
|
721
|
-
this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
|
722
|
-
})
|
723
|
-
.on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
|
724
|
-
this.emit(RoomEvent.TrackMuted, pub, participant);
|
725
|
-
})
|
726
|
-
.on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
|
727
|
-
this.emit(RoomEvent.TrackUnmuted, pub, participant);
|
728
|
-
})
|
729
|
-
.on(ParticipantEvent.MetadataChanged, (metadata: any) => {
|
730
|
-
this.emit(RoomEvent.MetadataChanged, metadata, participant);
|
744
|
+
if (this.participants.has(id)) {
|
745
|
+
return (this.participants.get(id) as RemoteParticipant);
|
746
|
+
}
|
747
|
+
// it's possible for the RTC track to arrive before signaling data
|
748
|
+
// when this happens, we'll create the participant and make the track work
|
749
|
+
const participant = this.createParticipant(id, info);
|
750
|
+
this.participants.set(id, participant);
|
751
|
+
|
752
|
+
// also forward events
|
753
|
+
// trackPublished is only fired for tracks added after both local participant
|
754
|
+
// and remote participant joined the room
|
755
|
+
participant
|
756
|
+
.on(ParticipantEvent.TrackPublished, (trackPublication: RemoteTrackPublication) => {
|
757
|
+
this.emit(RoomEvent.TrackPublished, trackPublication, participant);
|
758
|
+
})
|
759
|
+
.on(ParticipantEvent.TrackSubscribed,
|
760
|
+
(track: RemoteTrack, publication: RemoteTrackPublication) => {
|
761
|
+
// monitor playback status
|
762
|
+
if (track.kind === Track.Kind.Audio) {
|
763
|
+
track.on(TrackEvent.AudioPlaybackStarted, this.handleAudioPlaybackStarted);
|
764
|
+
track.on(TrackEvent.AudioPlaybackFailed, this.handleAudioPlaybackFailed);
|
765
|
+
}
|
766
|
+
this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
|
731
767
|
})
|
732
|
-
|
733
|
-
|
768
|
+
.on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
|
769
|
+
this.emit(RoomEvent.TrackUnpublished, publication, participant);
|
770
|
+
})
|
771
|
+
.on(ParticipantEvent.TrackUnsubscribed,
|
772
|
+
(track: RemoteTrack, publication: RemoteTrackPublication) => {
|
773
|
+
this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
|
734
774
|
})
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
775
|
+
.on(ParticipantEvent.TrackSubscriptionFailed, (sid: string) => {
|
776
|
+
this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
|
777
|
+
})
|
778
|
+
.on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
|
779
|
+
this.emit(RoomEvent.TrackMuted, pub, participant);
|
780
|
+
})
|
781
|
+
.on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
|
782
|
+
this.emit(RoomEvent.TrackUnmuted, pub, participant);
|
783
|
+
})
|
784
|
+
.on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
|
785
|
+
this.emit(RoomEvent.MetadataChanged, metadata, participant);
|
786
|
+
})
|
787
|
+
.on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
|
788
|
+
this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
|
789
|
+
})
|
790
|
+
.on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
|
791
|
+
this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
|
792
|
+
});
|
739
793
|
return participant;
|
740
794
|
}
|
741
795
|
|
@@ -775,11 +829,88 @@ class Room extends EventEmitter {
|
|
775
829
|
});
|
776
830
|
}
|
777
831
|
|
778
|
-
/**
|
779
|
-
|
832
|
+
/**
|
833
|
+
* After resuming, we'll need to notify the server of the current
|
834
|
+
* subscription settings.
|
835
|
+
*/
|
836
|
+
private updateSubscriptions() {
|
837
|
+
for (const p of this.participants.values()) {
|
838
|
+
for (const pub of p.videoTracks.values()) {
|
839
|
+
if (pub.isSubscribed && pub instanceof RemoteTrackPublication) {
|
840
|
+
pub.emitTrackUpdate();
|
841
|
+
}
|
842
|
+
}
|
843
|
+
}
|
844
|
+
}
|
845
|
+
|
846
|
+
// /** @internal */
|
847
|
+
emit<E extends keyof RoomEventCallbacks>(
|
848
|
+
event: E, ...args: Parameters<RoomEventCallbacks[E]>
|
849
|
+
): boolean {
|
780
850
|
log.debug('room event', event, ...args);
|
781
851
|
return super.emit(event, ...args);
|
782
852
|
}
|
783
853
|
}
|
784
854
|
|
785
855
|
export default Room;
|
856
|
+
|
857
|
+
export type RoomEventCallbacks = {
|
858
|
+
reconnecting: () => void,
|
859
|
+
reconnected: () => void,
|
860
|
+
disconnected: () => void,
|
861
|
+
stateChanged: (state: RoomState) => void,
|
862
|
+
mediaDevicesChanged: () => void,
|
863
|
+
participantConnected: (participant: RemoteParticipant) => void,
|
864
|
+
participantDisconnected: (participant: RemoteParticipant) => void,
|
865
|
+
trackPublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void,
|
866
|
+
trackSubscribed: (
|
867
|
+
track: RemoteTrack,
|
868
|
+
publication: RemoteTrackPublication,
|
869
|
+
participant: RemoteParticipant
|
870
|
+
) => void,
|
871
|
+
trackSubscriptionFailed: (trackSid: string, participant: RemoteParticipant) => void,
|
872
|
+
trackUnpublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void,
|
873
|
+
trackUnsubscribed: (
|
874
|
+
track: RemoteTrack,
|
875
|
+
publication: RemoteTrackPublication,
|
876
|
+
participant: RemoteParticipant,
|
877
|
+
) => void,
|
878
|
+
trackMuted: (publication: TrackPublication, participant: Participant) => void,
|
879
|
+
trackUnmuted: (publication: TrackPublication, participant: Participant) => void,
|
880
|
+
localTrackPublished: (publication: LocalTrackPublication, participant: LocalParticipant) => void,
|
881
|
+
localTrackUnpublished: (
|
882
|
+
publication: LocalTrackPublication,
|
883
|
+
participant: LocalParticipant
|
884
|
+
) => void,
|
885
|
+
/**
|
886
|
+
* @deprecated use [[participantMetadataChanged]] instead
|
887
|
+
*/
|
888
|
+
metadataChanged: (
|
889
|
+
metadata: string | undefined,
|
890
|
+
participant?: RemoteParticipant | LocalParticipant
|
891
|
+
) => void,
|
892
|
+
participantMetadataChanged: (
|
893
|
+
metadata: string | undefined,
|
894
|
+
participant: RemoteParticipant | LocalParticipant
|
895
|
+
) => void,
|
896
|
+
activeSpeakersChanged: (speakers: Array<Participant>) => void,
|
897
|
+
roomMetadataChanged: (metadata: string) => void,
|
898
|
+
dataReceived: (
|
899
|
+
payload: Uint8Array,
|
900
|
+
participant?: RemoteParticipant,
|
901
|
+
kind?: DataPacket_Kind
|
902
|
+
) => void,
|
903
|
+
connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void,
|
904
|
+
mediaDevicesError: (error: Error) => void,
|
905
|
+
trackStreamStateChanged: (
|
906
|
+
publication: RemoteTrackPublication,
|
907
|
+
streamState: Track.StreamState,
|
908
|
+
participant: RemoteParticipant,
|
909
|
+
) => void,
|
910
|
+
trackSubscriptionPermissionChanged: (
|
911
|
+
publication: RemoteTrackPublication,
|
912
|
+
status: TrackPublication.SubscriptionStatus,
|
913
|
+
participant: RemoteParticipant,
|
914
|
+
) => void,
|
915
|
+
audioPlaybackChanged: (playing: boolean) => void,
|
916
|
+
};
|
package/src/room/events.ts
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
* room.on(RoomEvent.TrackPublished, (track, publication, participant) => {})
|
8
8
|
* ```
|
9
9
|
*/
|
10
|
+
|
10
11
|
export enum RoomEvent {
|
11
12
|
/**
|
12
13
|
* When the connection to the server has been interrupted and it's attempting
|
@@ -25,6 +26,13 @@ export enum RoomEvent {
|
|
25
26
|
*/
|
26
27
|
Disconnected = 'disconnected',
|
27
28
|
|
29
|
+
/**
|
30
|
+
* Whenever the connection state of the room changes
|
31
|
+
*
|
32
|
+
* args: ([[RoomState]])
|
33
|
+
*/
|
34
|
+
StateChanged = 'stateChanged',
|
35
|
+
|
28
36
|
/**
|
29
37
|
* When input or output devices on the machine have changed.
|
30
38
|
*/
|
@@ -146,7 +154,7 @@ export enum RoomEvent {
|
|
146
154
|
* args: (prevMetadata: string, [[Participant]])
|
147
155
|
*
|
148
156
|
*/
|
149
|
-
ParticipantMetadataChanged = '
|
157
|
+
ParticipantMetadataChanged = 'participantMetadataChanged',
|
150
158
|
|
151
159
|
/**
|
152
160
|
* Room metadata is a simple way for app-specific state to be pushed to
|
@@ -366,11 +374,14 @@ export enum ParticipantEvent {
|
|
366
374
|
|
367
375
|
/** @internal */
|
368
376
|
export enum EngineEvent {
|
377
|
+
TransportsCreated = 'transportsCreated',
|
369
378
|
Connected = 'connected',
|
370
379
|
Disconnected = 'disconnected',
|
371
|
-
|
372
|
-
|
373
|
-
|
380
|
+
Resuming = 'resuming',
|
381
|
+
Resumed = 'resumed',
|
382
|
+
Restarting = 'restarting',
|
383
|
+
Restarted = 'restarted',
|
384
|
+
SignalResumed = 'signalResumed',
|
374
385
|
MediaTrackAdded = 'mediaTrackAdded',
|
375
386
|
ActiveSpeakersUpdate = 'activeSpeakersUpdate',
|
376
387
|
DataPacketReceived = 'dataPacketReceived',
|
@@ -416,8 +416,7 @@ export default class LocalParticipant extends Participant {
|
|
416
416
|
// store RTPSender
|
417
417
|
track.sender = transceiver.sender;
|
418
418
|
if (track instanceof LocalVideoTrack) {
|
419
|
-
|
420
|
-
track.startMonitor(this.engine.client, disableLayerPause);
|
419
|
+
track.startMonitor(this.engine.client);
|
421
420
|
} else if (track instanceof LocalAudioTrack) {
|
422
421
|
track.startMonitor();
|
423
422
|
}
|
@@ -434,6 +433,7 @@ export default class LocalParticipant extends Participant {
|
|
434
433
|
|
435
434
|
unpublishTrack(
|
436
435
|
track: LocalTrack | MediaStreamTrack,
|
436
|
+
stopOnUnpublish?: boolean,
|
437
437
|
): LocalTrackPublication | null {
|
438
438
|
// look through all published tracks to find the right ones
|
439
439
|
const publication = this.getPublicationForTrack(track);
|
@@ -455,7 +455,10 @@ export default class LocalParticipant extends Participant {
|
|
455
455
|
track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
|
456
456
|
track.off(TrackEvent.Ended, this.onTrackUnpublish);
|
457
457
|
|
458
|
-
if (
|
458
|
+
if (stopOnUnpublish === undefined) {
|
459
|
+
stopOnUnpublish = this.roomOptions?.stopLocalTrackOnUnpublish ?? true;
|
460
|
+
}
|
461
|
+
if (stopOnUnpublish) {
|
459
462
|
track.stop();
|
460
463
|
}
|
461
464
|
|
@@ -507,10 +510,6 @@ export default class LocalParticipant extends Participant {
|
|
507
510
|
return publications;
|
508
511
|
}
|
509
512
|
|
510
|
-
get publisherMetrics(): any {
|
511
|
-
return null;
|
512
|
-
}
|
513
|
-
|
514
513
|
/**
|
515
514
|
* Publish a new data payload to the room. Data will be forwarded to each
|
516
515
|
* participant in the room if the destination argument is empty
|
@@ -1,8 +1,12 @@
|
|
1
1
|
import { EventEmitter } from 'events';
|
2
|
-
import
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
3
|
+
import { ConnectionQuality as ProtoQuality, DataPacket_Kind, ParticipantInfo } from '../../proto/livekit_models';
|
3
4
|
import { ParticipantEvent, TrackEvent } from '../events';
|
5
|
+
import LocalTrackPublication from '../track/LocalTrackPublication';
|
6
|
+
import RemoteTrackPublication from '../track/RemoteTrackPublication';
|
4
7
|
import { Track } from '../track/Track';
|
5
8
|
import { TrackPublication } from '../track/TrackPublication';
|
9
|
+
import { RemoteTrack } from '../track/types';
|
6
10
|
|
7
11
|
export enum ConnectionQuality {
|
8
12
|
Excellent = 'excellent',
|
@@ -24,7 +28,9 @@ function qualityFromProto(q: ProtoQuality): ConnectionQuality {
|
|
24
28
|
}
|
25
29
|
}
|
26
30
|
|
27
|
-
export default class Participant extends
|
31
|
+
export default class Participant extends (
|
32
|
+
EventEmitter as new () => TypedEmitter<ParticipantEventCallbacks>
|
33
|
+
) {
|
28
34
|
protected participantInfo?: ParticipantInfo;
|
29
35
|
|
30
36
|
audioTracks: Map<string, TrackPublication>;
|
@@ -158,8 +164,8 @@ export default class Participant extends EventEmitter {
|
|
158
164
|
this.metadata = md;
|
159
165
|
|
160
166
|
if (changed) {
|
161
|
-
this.emit(ParticipantEvent.MetadataChanged, prevMetadata
|
162
|
-
this.emit(ParticipantEvent.ParticipantMetadataChanged, prevMetadata
|
167
|
+
this.emit(ParticipantEvent.MetadataChanged, prevMetadata);
|
168
|
+
this.emit(ParticipantEvent.ParticipantMetadataChanged, prevMetadata);
|
163
169
|
}
|
164
170
|
}
|
165
171
|
|
@@ -212,3 +218,32 @@ export default class Participant extends EventEmitter {
|
|
212
218
|
}
|
213
219
|
}
|
214
220
|
}
|
221
|
+
|
222
|
+
export type ParticipantEventCallbacks = {
|
223
|
+
trackPublished: (publication: RemoteTrackPublication) => void,
|
224
|
+
trackSubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void,
|
225
|
+
trackSubscriptionFailed: (trackSid: string) => void,
|
226
|
+
trackUnpublished: (publication: RemoteTrackPublication) => void,
|
227
|
+
trackUnsubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void,
|
228
|
+
trackMuted: (publication: TrackPublication) => void,
|
229
|
+
trackUnmuted: (publication: TrackPublication) => void,
|
230
|
+
localTrackPublished: (publication: LocalTrackPublication) => void,
|
231
|
+
localTrackUnpublished: (publication: LocalTrackPublication) => void,
|
232
|
+
/**
|
233
|
+
* @deprecated use [[participantMetadataChanged]] instead
|
234
|
+
*/
|
235
|
+
metadataChanged: (prevMetadata: string | undefined, participant?: any) => void,
|
236
|
+
participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void,
|
237
|
+
dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void,
|
238
|
+
isSpeakingChanged: (speaking: boolean) => void,
|
239
|
+
connectionQualityChanged: (connectionQuality: ConnectionQuality) => void,
|
240
|
+
trackStreamStateChanged: (
|
241
|
+
publication: RemoteTrackPublication,
|
242
|
+
streamState: Track.StreamState
|
243
|
+
) => void,
|
244
|
+
trackSubscriptionPermissionChanged: (
|
245
|
+
publication: RemoteTrackPublication,
|
246
|
+
status: TrackPublication.SubscriptionStatus
|
247
|
+
) => void,
|
248
|
+
mediaDevicesError: (error: Error) => void,
|
249
|
+
};
|
@@ -10,9 +10,8 @@ import RemoteAudioTrack from '../track/RemoteAudioTrack';
|
|
10
10
|
import RemoteTrackPublication from '../track/RemoteTrackPublication';
|
11
11
|
import RemoteVideoTrack from '../track/RemoteVideoTrack';
|
12
12
|
import { Track } from '../track/Track';
|
13
|
-
import { TrackPublication } from '../track/TrackPublication';
|
14
13
|
import { RemoteTrack } from '../track/types';
|
15
|
-
import Participant from './Participant';
|
14
|
+
import Participant, { ParticipantEventCallbacks } from './Participant';
|
16
15
|
|
17
16
|
export default class RemoteParticipant extends Participant {
|
18
17
|
audioTracks: Map<string, RemoteTrackPublication>;
|
@@ -42,7 +41,7 @@ export default class RemoteParticipant extends Participant {
|
|
42
41
|
this.videoTracks = new Map();
|
43
42
|
}
|
44
43
|
|
45
|
-
protected addTrackPublication(publication:
|
44
|
+
protected addTrackPublication(publication: RemoteTrackPublication) {
|
46
45
|
super.addTrackPublication(publication);
|
47
46
|
|
48
47
|
// register action events
|
@@ -53,6 +52,9 @@ export default class RemoteParticipant extends Participant {
|
|
53
52
|
},
|
54
53
|
);
|
55
54
|
publication.on(TrackEvent.UpdateSubscription, (sub: UpdateSubscription) => {
|
55
|
+
sub.participantTracks.forEach((pt) => {
|
56
|
+
pt.participantSid = this.sid;
|
57
|
+
});
|
56
58
|
this.signalClient.sendUpdateSubscription(sub);
|
57
59
|
});
|
58
60
|
publication.on(TrackEvent.Ended, (track: RemoteTrack) => {
|
@@ -231,7 +233,10 @@ export default class RemoteParticipant extends Participant {
|
|
231
233
|
}
|
232
234
|
|
233
235
|
/** @internal */
|
234
|
-
emit
|
236
|
+
emit<E extends keyof ParticipantEventCallbacks>(
|
237
|
+
event: E,
|
238
|
+
...args: Parameters<ParticipantEventCallbacks[E]>
|
239
|
+
): boolean {
|
235
240
|
log.trace('participant event', this.sid, event, ...args);
|
236
241
|
return super.emit(event, ...args);
|
237
242
|
}
|
@@ -73,7 +73,14 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
73
73
|
this._currentBitrate = 0;
|
74
74
|
return;
|
75
75
|
}
|
76
|
-
|
76
|
+
|
77
|
+
let stats: AudioSenderStats | undefined;
|
78
|
+
try {
|
79
|
+
stats = await this.getSenderStats();
|
80
|
+
} catch (e) {
|
81
|
+
log.error('could not get audio sender stats', e);
|
82
|
+
return;
|
83
|
+
}
|
77
84
|
|
78
85
|
if (stats && this.prevStats) {
|
79
86
|
this._currentBitrate = computeBitrate(stats, this.prevStats);
|
@@ -3,12 +3,15 @@ import { TrackEvent } from '../events';
|
|
3
3
|
import LocalAudioTrack from './LocalAudioTrack';
|
4
4
|
import LocalTrack from './LocalTrack';
|
5
5
|
import LocalVideoTrack from './LocalVideoTrack';
|
6
|
+
import { TrackPublishOptions } from './options';
|
6
7
|
import { Track } from './Track';
|
7
8
|
import { TrackPublication } from './TrackPublication';
|
8
9
|
|
9
10
|
export default class LocalTrackPublication extends TrackPublication {
|
10
11
|
track?: LocalTrack;
|
11
12
|
|
13
|
+
options?: TrackPublishOptions;
|
14
|
+
|
12
15
|
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack) {
|
13
16
|
super(kind, ti.sid, ti.name);
|
14
17
|
|