feat(voice): drive screen share quality from viewer demand (#2421)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-03 04:49:29 +02:00
1 parent 66517c925b
commit 2426a5769d
5 files changed
+316 -40

No files matched your search

@@ -18,6 +18,10 @@ import {
type ScreenSharePublicationOperation,
ScreenShareWatchErrorCode,
} from '@app/features/voice/state/ScreenShareWatchFailures';
import {
clearScreenShareViewerDemand,
resolveScreenSharePublicationVideoRequest,
} from '@app/features/voice/utils/ScreenShareSubscriptionPolicy';
import type {RemoteParticipant, RemoteTrackPublication, Room} from 'livekit-client';
import {VideoQuality} from 'livekit-client';
@@ -30,6 +34,13 @@ const qualityMap: Record<VoiceMediaGraphVideoQuality, VideoQuality> = {
high: VideoQuality.HIGH,
};
const graphQualityMap = new Map<VideoQuality, VoiceMediaGraphVideoQuality>(
Object.entries(qualityMap).map(([graphQuality, videoQuality]): [VideoQuality, VoiceMediaGraphVideoQuality] => [
videoQuality,
graphQuality as VoiceMediaGraphVideoQuality,
]),
);
export class ScreenShareSubscriptionManager extends Store {
private room: Room | null = null;
private observers = new Map<string, IntersectionObserver>();
@@ -44,6 +55,7 @@ export class ScreenShareSubscriptionManager extends Store {
};
setRoom(room: Room | null): void {
clearScreenShareViewerDemand();
this.update(() => {
this.room = room;
});
@@ -292,10 +304,17 @@ export class ScreenShareSubscriptionManager extends Store {
participantIdentity: string,
publication: RemoteTrackPublication,
quality: VoiceMediaGraphVideoQuality,
): boolean {
return this.runPublicationOperation(participantIdentity, publication, 'setVideoQuality', () => {
publication.setVideoQuality(qualityMap[quality]);
): VoiceMediaGraphVideoQuality | null {
const request = resolveScreenSharePublicationVideoRequest(publication, qualityMap[quality]);
const applied = this.runPublicationOperation(participantIdentity, publication, request.operation, () => {
if (request.operation === 'setVideoDimensions') {
publication.setVideoDimensions(request.dimensions);
return;
}
publication.setVideoQuality(request.quality);
});
if (!applied) return null;
return graphQualityMap.get(request.quality) ?? null;
}
private createObserver(participantIdentity: string, element: HTMLElement): IntersectionObserver {
@@ -400,15 +419,16 @@ export class ScreenShareSubscriptionManager extends Store {
publication: RemoteTrackPublication,
enabled: boolean,
quality: VoiceMediaGraphVideoQuality,
): boolean {
): VoiceMediaGraphVideoQuality | null {
const subscribedApplied = this.runPublicationOperation(participantIdentity, publication, 'setSubscribed', () => {
publication.setSubscribed(true);
});
const enabledApplied = this.runPublicationOperation(participantIdentity, publication, 'setEnabled', () => {
publication.setEnabled(enabled);
});
const qualityApplied = this.applyQuality(participantIdentity, publication, quality);
return subscribedApplied && enabledApplied && qualityApplied;
const appliedQuality = this.applyQuality(participantIdentity, publication, quality);
if (!subscribedApplied || !enabledApplied) return null;
return appliedQuality;
}
private subscribePublication(
@@ -421,9 +441,12 @@ export class ScreenShareSubscriptionManager extends Store {
return;
}
this.withActiveScreenSharePublications(participantIdentity, (publications, participant) => {
let appliedQuality: VoiceMediaGraphVideoQuality | null = null;
let applied = true;
for (const publication of publications) {
applied = this.applySubscribeOperations(participantIdentity, publication, enabled, quality) && applied;
const publicationQuality = this.applySubscribeOperations(participantIdentity, publication, enabled, quality);
if (publicationQuality === null) applied = false;
else appliedQuality = publicationQuality;
}
for (const publication of ScreenSharePublicationMigration.getScreenSharePublicationsToDisable(participant)) {
this.runPublicationOperation(participantIdentity, publication, 'setSubscribed', () => {
@@ -433,7 +456,7 @@ export class ScreenShareSubscriptionManager extends Store {
if (!applied) return;
this.reportActualChanged(participantIdentity, {
enabled,
quality,
quality: appliedQuality,
trackSid: publications[0]?.trackSid ?? null,
});
logger.debug('Screen share subscribe command applied', {participantIdentity});
@@ -510,10 +533,11 @@ export class ScreenShareSubscriptionManager extends Store {
}
this.pendingResubscribePulses.delete(participantIdentity);
if (!this.isSubscriptionStillWanted(participantIdentity)) return;
if (!this.applySubscribeOperations(participantIdentity, publication, enabled, quality)) return;
const appliedQuality = this.applySubscribeOperations(participantIdentity, publication, enabled, quality);
if (appliedQuality === null) return;
this.reportActualChanged(participantIdentity, {
enabled,
quality,
quality: appliedQuality,
trackSid: publication.trackSid ?? null,
});
logger.debug('Screen share resubscribe pulse command applied after republish', {
@@ -555,13 +579,16 @@ export class ScreenShareSubscriptionManager extends Store {
private setPublicationQuality(participantIdentity: string, quality: VoiceMediaGraphVideoQuality): void {
this.withActiveScreenSharePublications(participantIdentity, (publications) => {
let appliedQuality: VoiceMediaGraphVideoQuality | null = null;
let applied = true;
for (const publication of publications) {
applied = this.applyQuality(participantIdentity, publication, quality) && applied;
const publicationQuality = this.applyQuality(participantIdentity, publication, quality);
if (publicationQuality === null) applied = false;
else appliedQuality = publicationQuality;
}
if (!applied) return;
this.reportActualChanged(participantIdentity, {quality});
logger.debug('Quality updated', {participantIdentity, quality});
this.reportActualChanged(participantIdentity, {quality: appliedQuality});
logger.debug('Quality updated', {participantIdentity, quality: appliedQuality});
});
}
}