fix: centralize screen share pipeline to fix 270p resolution ramp-up failure

Screen sharing was publishing at h360 (640x360) and never ramping to target
resolution due to stale closure in setTimeout, wrong initial quality anchor,
and 5 competing code paths with inconsistent bitrates.

- Create utils/screenShare.ts as single source of truth for all screen share ops
- Publish at target resolution from the start (not h360 → ramp)
- Read store at call time in timers (eliminates stale closure bug)
- Use maintain-resolution for screen content, maintain-framerate for camera
- Fix OS-level "Stop sharing" not resetting store or restoring AEC
- Enable dynacast for SFU quality signaling
- Reconcile QUALITY_MAP to canonical bitrates across all 7 files
This commit is contained in:
Jannis Braun
2026-02-23 03:25:52 +01:00
parent 82eee04946
commit 7e7de41718
7 changed files with 211 additions and 161 deletions
@@ -2,8 +2,8 @@ import React, { useRef, useEffect, useState, useCallback } from 'react';
import { Avatar } from '../ui/Avatar';
import { useVoiceStore } from '../../stores/voiceStore';
import { getActiveRoom, setStreamSubscription } from '../../hooks/useLiveKit';
import { AudioManager } from '../../audio/AudioManager';
import { VideoQualityPopover } from './VideoQualityPopover';
import { stopScreenShare, changeScreenShare } from '../../utils/screenShare';
import type { StreamTile as StreamTileType } from '../../hooks/useLiveKit';
interface StreamTileProps {
@@ -107,22 +107,14 @@ export function StreamTile({ tile, large }: StreamTileProps) {
const handleStopStreaming = useCallback(async () => {
const room = getActiveRoom();
if (room) {
await room.localParticipant.setScreenShareEnabled(false);
AudioManager.getInstance().setScreenShareActive(false);
useVoiceStore.getState().toggleScreenShare();
await stopScreenShare(room);
}
}, []);
const handleChangeStream = useCallback(async () => {
const room = getActiveRoom();
if (room) {
await room.localParticipant.setScreenShareEnabled(false);
// Small delay then re-start to re-trigger the source picker
setTimeout(async () => {
await room.localParticipant.setScreenShareEnabled(true, {
audio: true,
});
}, 200);
await changeScreenShare(room);
}
}, []);