Skip to content

Commit 591c218

Browse files
authored
Fix missed TrackPublished events. (#275)
* Fix missed TrackPublished events. When initial participant info is sent down along with tracks, we would miss emitting TrackPublished events for them. This is due an incorrect assumption that participants would first join without tracks. Instead of relying on if it's the first update, we now use Room connection state to determine if those TrackPublished events should be emitted or not. * defer TrackSubscribed event until after connected to room. * emit certain events only when connected
1 parent 99fba24 commit 591c218

File tree

4 files changed

+68
-40
lines changed

4 files changed

+68
-40
lines changed

.changeset/famous-suits-matter.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'livekit-client': patch
3+
---
4+
5+
Fixed TrackPublished events not firing correctly in some cases

example/sample.ts

+18-22
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import {
2020
VideoCaptureOptions,
2121
VideoCodec,
2222
VideoPresets,
23-
VideoQuality,
24-
} from '../src/index';
23+
VideoQuality
24+
} from '../src/index'
2525

2626
const $ = (id: string) => document.getElementById(id);
2727

@@ -110,12 +110,12 @@ const appActions = {
110110
.on(RoomEvent.LocalTrackPublished, () => {
111111
renderParticipant(room.localParticipant);
112112
updateButtonsForPublishState();
113-
renderScreenShare();
113+
renderScreenShare(room);
114114
})
115115
.on(RoomEvent.LocalTrackUnpublished, () => {
116116
renderParticipant(room.localParticipant);
117117
updateButtonsForPublishState();
118-
renderScreenShare();
118+
renderScreenShare(room);
119119
})
120120
.on(RoomEvent.RoomMetadataChanged, (metadata) => {
121121
appendLog('new metadata for room', metadata);
@@ -138,9 +138,15 @@ const appActions = {
138138
appendLog('connection quality changed', participant?.identity, quality);
139139
},
140140
)
141-
.on(RoomEvent.TrackSubscribed, (_1, _2, participant: RemoteParticipant) => {
141+
.on(RoomEvent.TrackSubscribed, (_1, pub, participant) => {
142+
appendLog('subscribed to track', pub.trackSid, participant.identity);
142143
renderParticipant(participant);
143-
renderScreenShare();
144+
renderScreenShare(room);
145+
})
146+
.on(RoomEvent.TrackUnsubscribed, (_, pub, participant) => {
147+
appendLog('unsubscribed from track', pub.trackSid);
148+
renderParticipant(participant);
149+
renderScreenShare(room);
144150
})
145151
.on(RoomEvent.SignalConnected, async () => {
146152
if (shouldPublish) {
@@ -351,16 +357,6 @@ function handleData(msg: Uint8Array, participant?: RemoteParticipant) {
351357
function participantConnected(participant: Participant) {
352358
appendLog('participant', participant.identity, 'connected', participant.metadata);
353359
participant
354-
.on(ParticipantEvent.TrackSubscribed, (_, pub: TrackPublication) => {
355-
appendLog('subscribed to track', pub.trackSid, participant.identity);
356-
renderParticipant(participant);
357-
renderScreenShare();
358-
})
359-
.on(ParticipantEvent.TrackUnsubscribed, (_, pub: TrackPublication) => {
360-
appendLog('unsubscribed from track', pub.trackSid);
361-
renderParticipant(participant);
362-
renderScreenShare();
363-
})
364360
.on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
365361
appendLog('track was muted', pub.trackSid, participant.identity);
366362
renderParticipant(participant);
@@ -391,7 +387,7 @@ function handleRoomDisconnect() {
391387
currentRoom.participants.forEach((p) => {
392388
renderParticipant(p, true);
393389
});
394-
renderScreenShare();
390+
renderScreenShare(currentRoom);
395391

396392
const container = $('participants-area');
397393
if (container) {
@@ -572,19 +568,19 @@ function renderParticipant(participant: Participant, remove: boolean = false) {
572568
}
573569
}
574570

575-
function renderScreenShare() {
571+
function renderScreenShare(room: Room) {
576572
const div = $('screenshare-area')!;
577-
if (!currentRoom || currentRoom.state !== ConnectionState.Connected) {
573+
if (room.state !== ConnectionState.Connected) {
578574
div.style.display = 'none';
579575
return;
580576
}
581577
let participant: Participant | undefined;
582-
let screenSharePub: TrackPublication | undefined = currentRoom.localParticipant.getTrack(
578+
let screenSharePub: TrackPublication | undefined = room.localParticipant.getTrack(
583579
Track.Source.ScreenShare,
584580
);
585581
let screenShareAudioPub: RemoteTrackPublication | undefined;
586582
if (!screenSharePub) {
587-
currentRoom.participants.forEach((p) => {
583+
room.participants.forEach((p) => {
588584
if (screenSharePub) {
589585
return;
590586
}
@@ -599,7 +595,7 @@ function renderScreenShare() {
599595
}
600596
});
601597
} else {
602-
participant = currentRoom.localParticipant;
598+
participant = room.localParticipant;
603599
}
604600

605601
if (screenSharePub && participant) {

src/room/Room.ts

+40-7
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,20 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
500500
stream: MediaStream,
501501
receiver?: RTCRtpReceiver,
502502
) {
503+
// don't fire onSubscribed when connecting
504+
// WebRTC fires onTrack as soon as setRemoteDescription is called on the offer
505+
// at that time, ICE connectivity has not been established so the track is not
506+
// technically subscribed.
507+
// We'll defer these events until when the room is connected or eventually disconnected.
508+
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
509+
setTimeout(() => {
510+
this.onTrackAdded(mediaTrack, stream, receiver);
511+
}, 10);
512+
return;
513+
}
514+
if (this.state === ConnectionState.Disconnected) {
515+
log.warn('skipping incoming track after Room disconnected');
516+
}
503517
const parts = unpackStreamId(stream.id);
504518
const participantId = parts[0];
505519
let trackId = parts[1];
@@ -850,7 +864,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
850864
// and remote participant joined the room
851865
participant
852866
.on(ParticipantEvent.TrackPublished, (trackPublication: RemoteTrackPublication) => {
853-
this.emit(RoomEvent.TrackPublished, trackPublication, participant);
867+
this.emitWhenConnected(RoomEvent.TrackPublished, trackPublication, participant);
854868
})
855869
.on(
856870
ParticipantEvent.TrackSubscribed,
@@ -864,7 +878,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
864878
},
865879
)
866880
.on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
867-
this.emit(RoomEvent.TrackUnpublished, publication, participant);
881+
this.emitWhenConnected(RoomEvent.TrackUnpublished, publication, participant);
868882
})
869883
.on(
870884
ParticipantEvent.TrackUnsubscribed,
@@ -876,23 +890,32 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
876890
this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
877891
})
878892
.on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
879-
this.emit(RoomEvent.TrackMuted, pub, participant);
893+
this.emitWhenConnected(RoomEvent.TrackMuted, pub, participant);
880894
})
881895
.on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
882-
this.emit(RoomEvent.TrackUnmuted, pub, participant);
896+
this.emitWhenConnected(RoomEvent.TrackUnmuted, pub, participant);
883897
})
884898
.on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
885-
this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
899+
this.emitWhenConnected(RoomEvent.ParticipantMetadataChanged, metadata, participant);
886900
})
887901
.on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
888-
this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
902+
this.emitWhenConnected(RoomEvent.ConnectionQualityChanged, quality, participant);
889903
})
890904
.on(
891905
ParticipantEvent.ParticipantPermissionsChanged,
892906
(prevPermissions: ParticipantPermission) => {
893-
this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, participant);
907+
this.emitWhenConnected(
908+
RoomEvent.ParticipantPermissionsChanged,
909+
prevPermissions,
910+
participant,
911+
);
894912
},
895913
);
914+
915+
// update info at the end after callbacks have been set up
916+
if (info) {
917+
participant.updateInfo(info);
918+
}
896919
return participant;
897920
}
898921

@@ -959,6 +982,16 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
959982
return true;
960983
}
961984

985+
private emitWhenConnected<E extends keyof RoomEventCallbacks>(
986+
event: E,
987+
...args: Parameters<RoomEventCallbacks[E]>
988+
): boolean {
989+
if (this.state === ConnectionState.Connected) {
990+
return this.emit(event, ...args);
991+
}
992+
return false;
993+
}
994+
962995
// /** @internal */
963996
emit<E extends keyof RoomEventCallbacks>(
964997
event: E,

src/room/participant/RemoteParticipant.ts

+5-11
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ export default class RemoteParticipant extends Participant {
2323

2424
/** @internal */
2525
static fromParticipantInfo(signalClient: SignalClient, pi: ParticipantInfo): RemoteParticipant {
26-
const rp = new RemoteParticipant(signalClient, pi.sid, pi.identity);
27-
rp.updateInfo(pi);
28-
return rp;
26+
return new RemoteParticipant(signalClient, pi.sid, pi.identity);
2927
}
3028

3129
/** @internal */
@@ -182,8 +180,6 @@ export default class RemoteParticipant extends Participant {
182180

183181
/** @internal */
184182
updateInfo(info: ParticipantInfo) {
185-
const alreadyHasMetadata = this.hasMetadata;
186-
187183
super.updateInfo(info);
188184

189185
// we are getting a list of all available tracks, reconcile in here
@@ -212,12 +208,10 @@ export default class RemoteParticipant extends Participant {
212208
validTracks.set(ti.sid, publication);
213209
});
214210

215-
// send new tracks
216-
if (alreadyHasMetadata) {
217-
newTracks.forEach((publication) => {
218-
this.emit(ParticipantEvent.TrackPublished, publication);
219-
});
220-
}
211+
// always emit events for new publications, Room will not forward them unless it's ready
212+
newTracks.forEach((publication) => {
213+
this.emit(ParticipantEvent.TrackPublished, publication);
214+
});
221215

222216
// detect removed tracks
223217
this.tracks.forEach((publication) => {

0 commit comments

Comments
 (0)