mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-17 20:39:19 +00:00
Fix tracks not being resumed in case of a startPublishing vs track
creating race.
This commit is contained in:
@@ -286,6 +286,53 @@ describe("Publisher", () => {
|
||||
expect(track!.isUpstreamPaused).toBe(true);
|
||||
});
|
||||
|
||||
it("Resumes upstream for tracks published after startPublishing was called (slow camera)", async () => {
|
||||
videoEnabled$.next(true);
|
||||
|
||||
// Simulate that the upstream ended up paused by the time the track gets
|
||||
// published (e.g. paused while it was still unpublished).
|
||||
const originalPublishTrack = localParticipant.publishTrack;
|
||||
vi.mocked(localParticipant).publishTrack = vi
|
||||
.fn()
|
||||
.mockImplementation(async (track: LocalTrack) => {
|
||||
await track.pauseUpstream();
|
||||
return originalPublishTrack(track);
|
||||
});
|
||||
|
||||
const resolvers = Promise.withResolvers<void>();
|
||||
createTrackLock = resolvers.promise;
|
||||
|
||||
// Track creation is slow (e.g. camera hardware takes time to open)
|
||||
await publisher.createAndSetupTracks();
|
||||
// startPublishing runs before the camera track exists, so its
|
||||
// resumeUpstreams call finds nothing to resume
|
||||
await publisher.startPublishing();
|
||||
expect(
|
||||
localParticipant.getTrackPublication(Track.Source.Camera),
|
||||
).toBeUndefined();
|
||||
|
||||
// The camera opens and the track gets published
|
||||
resolvers.resolve();
|
||||
await flushPromises();
|
||||
|
||||
const track = localParticipant.getTrackPublication(
|
||||
Track.Source.Camera,
|
||||
)?.track;
|
||||
expect(track).toBeDefined();
|
||||
expect(track!.resumeUpstream).toHaveBeenCalled();
|
||||
expect(track!.isUpstreamPaused).toBe(false);
|
||||
});
|
||||
|
||||
it("Does not pause tracks published after the publisher was destroyed", async () => {
|
||||
await publisher.destroy();
|
||||
|
||||
const track = createMockLocalTrack(Track.Source.Camera);
|
||||
await localParticipant.publishTrack(track);
|
||||
await flushPromises();
|
||||
|
||||
expect(track.pauseUpstream).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Ensure resume upstream when published is called", async () => {
|
||||
videoEnabled$.next(true);
|
||||
await publisher.createAndSetupTracks();
|
||||
|
||||
@@ -80,11 +80,15 @@ export class Publisher {
|
||||
|
||||
this.connection.livekitRoom.localParticipant.on(
|
||||
ParticipantEvent.LocalTrackPublished,
|
||||
this.onLocalTrackPublished.bind(this),
|
||||
this.onLocalTrackPublished,
|
||||
);
|
||||
}
|
||||
|
||||
public async destroy(): Promise<void> {
|
||||
this.connection.livekitRoom.localParticipant.off(
|
||||
ParticipantEvent.LocalTrackPublished,
|
||||
this.onLocalTrackPublished,
|
||||
);
|
||||
this.scope.end();
|
||||
this.logger.info("Scope ended -> unset handler");
|
||||
this.muteStates.audio.unsetHandler();
|
||||
@@ -106,9 +110,9 @@ export class Publisher {
|
||||
// So for that we use pauseUpStream(): Stops sending media to the server by replacing
|
||||
// the sender track with null, but keeps the local MediaStreamTrack active.
|
||||
// The user can still see/hear themselves locally, but remote participants see nothing.
|
||||
private onLocalTrackPublished(
|
||||
private onLocalTrackPublished = (
|
||||
localTrackPublication: LocalTrackPublication,
|
||||
): void {
|
||||
): void => {
|
||||
this.logger.info("Local track published", localTrackPublication);
|
||||
const lkRoom = this.connection.livekitRoom;
|
||||
if (!this.shouldPublish) {
|
||||
@@ -116,6 +120,17 @@ export class Publisher {
|
||||
this.pauseUpstreams(lkRoom, [localTrackPublication.source]).catch((e) => {
|
||||
this.logger.error(`Failed to pause upstreams`, e);
|
||||
});
|
||||
} else {
|
||||
// If startPublishing() ran before this track existed (track creation is
|
||||
// not awaited and e.g. camera hardware can take a while to open), its
|
||||
// resumeUpstreams() call found no track and did nothing. Resume here so
|
||||
// that a track published after startPublishing() actually sends media.
|
||||
// This is a no-op if the upstream is not paused.
|
||||
this.resumeUpstreams(lkRoom, [localTrackPublication.source]).catch(
|
||||
(e) => {
|
||||
this.logger.error(`Failed to resume upstreams`, e);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (localTrackPublication.source === Track.Source.Microphone) {
|
||||
const muteState = this.muteStates.audio;
|
||||
|
||||
Reference in New Issue
Block a user