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).
This commit is contained in:
Jannis Braun
2026-02-23 03:46:06 +01:00
parent 7e7de41718
commit fe97212c64
+11 -3
View File
@@ -21,8 +21,8 @@ export const AUTO_PRESET = SCREEN_QUALITY_MAP['720p60']!;
* Apply overdrive hammer to a published track — forces bitrate/resolution/framerate * Apply overdrive hammer to a published track — forces bitrate/resolution/framerate
* directly on the RTCRtpSender, bypassing LiveKit's conservative defaults. * directly on the RTCRtpSender, bypassing LiveKit's conservative defaults.
* *
* Screen share: uses 'maintain-resolution' (text/code readability matters more than fps) * Screen share: uses 'maintain-framerate' (gaming: hold 60fps, allow temporary quality drops)
* Camera: uses 'maintain-framerate' (smooth motion matters more than sharpness) * Camera: uses 'maintain-framerate' (smooth face motion matters more than sharpness)
*/ */
export async function applyOverdrive( export async function applyOverdrive(
room: Room, room: Room,
@@ -50,7 +50,7 @@ export async function applyOverdrive(
const isScreenShare = source === Track.Source.ScreenShare; const isScreenShare = source === Track.Source.ScreenShare;
if (isScreenShare) { if (isScreenShare) {
(params as any).degradationPreference = 'maintain-resolution'; (params as any).degradationPreference = 'maintain-framerate';
(params.encodings[0] as any).minBitrate = 2_000_000; (params.encodings[0] as any).minBitrate = 2_000_000;
} else { } else {
(params as any).degradationPreference = 'maintain-framerate'; (params as any).degradationPreference = 'maintain-framerate';
@@ -92,6 +92,14 @@ export async function startScreenShare(room: Room): Promise<boolean> {
return false; 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 // Update store — screen share is now active
useVoiceStore.setState({ isScreenSharing: true }); useVoiceStore.setState({ isScreenSharing: true });