perf(call): H.264 + contentHint + 30fps default for screen-share

The real cost in the "frame by frame" stutter wasn't the custom capture
path — it was LiveKit re-encoding via VP9 software with L3T3_KEY SVC
(three spatial × three temporal layers, all CPU). Switching the
per-publish codec to H.264 lets Chromium's hardware encoder take over
on Windows and sidesteps the SVC mode entirely (H.264 has no SVC).
Also pushes `contentHint = 'detail'` on the track — setScreenShareEnabled
does this internally, the manual publishTrack paths had been missing it,
which changes how the encoder allocates its frame budget for static UI
content.

Auto preset default framerate 60 → 30. 60fps desktop share burns three
full-res encodes per frame at sizes up to 4K; 30 is what getDisplayMedia
practically delivers anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-04-22 23:37:22 +02:00
parent ccbef6d959
commit f9bbcdee47
2 changed files with 12 additions and 3 deletions
+11 -2
View File
@@ -1310,9 +1310,17 @@ export function CallProvider({ children }: { children: ReactNode }) {
stream.getTracks().forEach((t) => t.stop());
throw new Error('no video track from chromeMediaSource');
}
// H.264 lets Chromium's hardware encoder take over on Windows
// (Intel QuickSync / NVENC / AMD VCE) — VP9 is software-only in
// Chromium's WebRTC path and collapses under the L3T3_KEY
// scalability mode we set globally for camera. `contentHint` is
// what setScreenShareEnabled sets internally; pushing it by hand
// here tells the encoder to weight sharpness over smoothness,
// which is right for UI/text-heavy screen content.
videoMst.contentHint = 'detail';
const videoPub = await lp.publishTrack(videoMst, {
source: LkTrack.Source.ScreenShare,
videoCodec: 'vp9',
videoCodec: 'h264',
});
const audioRes = await startWasapiAudio(LkTrack);
videoMst.addEventListener('ended', () => {
@@ -1358,9 +1366,10 @@ export function CallProvider({ children }: { children: ReactNode }) {
nativeCaptureRef.current = null;
throw new NativeCaptureUnavailable('canvas stream produced no video track');
}
videoMst.contentHint = 'detail';
const videoPub = await lp.publishTrack(videoMst, {
source: LkTrack.Source.ScreenShare,
videoCodec: 'vp9',
videoCodec: 'h264',
});
const audioRes = await startWasapiAudio(LkTrack);
videoMst.addEventListener('ended', () => {
+1 -1
View File
@@ -46,7 +46,7 @@ export interface PresetParams {
}
const PRESET_PARAMS: Record<ScreenSharePreset, PresetParams> = {
auto: { dims: null, framerate: 60, bitrateKbps: 8000, label: 'Auto (Original)' },
auto: { dims: null, framerate: 30, bitrateKbps: 8000, label: 'Auto (Original)' },
'720p30': {
dims: { width: 1280, height: 720 },
framerate: 30,