From fe97212c64abb6f941ad0d660ffc0a2b0ebc9104 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Mon, 23 Feb 2026 03:46:06 +0100 Subject: [PATCH] fix: screen share gaming stutter by switching to maintain-framerate + contentHint motion Root cause: degradationPreference 'maintain-resolution' forced FPS drops under CPU pressure during fast-motion gaming. Changed to 'maintain-framerate' so the encoder drops resolution temporarily instead of stuttering. Added contentHint='motion' to optimize for temporal prediction (more P-frames, fewer I-frames). --- packages/web/src/utils/screenShare.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/web/src/utils/screenShare.ts b/packages/web/src/utils/screenShare.ts index 16ac398e..8033de9d 100644 --- a/packages/web/src/utils/screenShare.ts +++ b/packages/web/src/utils/screenShare.ts @@ -21,8 +21,8 @@ export const AUTO_PRESET = SCREEN_QUALITY_MAP['720p60']!; * Apply overdrive hammer to a published track — forces bitrate/resolution/framerate * directly on the RTCRtpSender, bypassing LiveKit's conservative defaults. * - * Screen share: uses 'maintain-resolution' (text/code readability matters more than fps) - * Camera: uses 'maintain-framerate' (smooth motion matters more than sharpness) + * Screen share: uses 'maintain-framerate' (gaming: hold 60fps, allow temporary quality drops) + * Camera: uses 'maintain-framerate' (smooth face motion matters more than sharpness) */ export async function applyOverdrive( room: Room, @@ -50,7 +50,7 @@ export async function applyOverdrive( const isScreenShare = source === Track.Source.ScreenShare; if (isScreenShare) { - (params as any).degradationPreference = 'maintain-resolution'; + (params as any).degradationPreference = 'maintain-framerate'; (params.encodings[0] as any).minBitrate = 2_000_000; } else { (params as any).degradationPreference = 'maintain-framerate'; @@ -92,6 +92,14 @@ export async function startScreenShare(room: Room): Promise { return false; } + // Tell the encoder to optimize for motion (more P-frames, fewer I-frames) + // Must be set BEFORE the overdrive timer so the encoder knows from frame 1 + const screenPub = room.localParticipant.getTrackPublications() + .find(p => p.source === Track.Source.ScreenShare); + if (screenPub?.track?.mediaStreamTrack) { + screenPub.track.mediaStreamTrack.contentHint = 'motion'; + } + // Update store — screen share is now active useVoiceStore.setState({ isScreenSharing: true });