feat: smart codec defaults + mid-stream codec switching
- Gaming mode defaults to H.264 (NVENC hardware encoding, zero CPU) - Text mode defaults to VP9 (better compression, CPU is free) - Changing mode auto-updates codec to smart default - User can manually override codec via UI pills - Changing codec mid-stream auto-restarts the stream (~1s interruption)
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
import { getMediaStreamTrack } from '../utils/livekitInternals';
|
||||
|
||||
let _activeRoom: Room | null = null;
|
||||
let _publishedScreenShareCodec: 'vp9' | 'h264' | null = null;
|
||||
|
||||
export function getActiveRoom(): Room | null {
|
||||
return _activeRoom;
|
||||
@@ -608,8 +609,21 @@ export function useLiveKit() {
|
||||
const updateActiveTracks = async () => {
|
||||
if (isScreenSharing) {
|
||||
const opts = buildScreenShareOptions(screenShareConfig);
|
||||
|
||||
// Codec changed mid-stream — must restart (codec is baked into SDP negotiation)
|
||||
if (_publishedScreenShareCodec && _publishedScreenShareCodec !== opts.publish.videoCodec) {
|
||||
_publishedScreenShareCodec = null;
|
||||
await stopScreenShare(room);
|
||||
setTimeout(() => startScreenShare(room).catch(() => {}), 200);
|
||||
return;
|
||||
}
|
||||
|
||||
const screenPub = room.localParticipant.getTrackPublications().find(p => p.source === Track.Source.ScreenShare);
|
||||
if (screenPub?.videoTrack) {
|
||||
// Track the published codec for mid-stream change detection
|
||||
if (!_publishedScreenShareCodec) {
|
||||
_publishedScreenShareCodec = opts.publish.videoCodec;
|
||||
}
|
||||
const mediaTrack = getMediaStreamTrack(screenPub.videoTrack);
|
||||
if (mediaTrack) {
|
||||
if (opts.capture.width > 0 && opts.capture.height > 0) {
|
||||
@@ -625,6 +639,9 @@ export function useLiveKit() {
|
||||
resolveNativeOverdrive(mediaTrack ?? null, screenShareConfig, opts);
|
||||
await applyOverdrive(room, Track.Source.ScreenShare, opts.overdrive);
|
||||
}
|
||||
} else {
|
||||
// Screen share stopped — clear published codec tracker
|
||||
_publishedScreenShareCodec = null;
|
||||
}
|
||||
if (isCameraOn) { await applyOverdrive(room, Track.Source.Camera, CAMERA_OVERDRIVE); }
|
||||
};
|
||||
|
||||
@@ -146,7 +146,7 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
inputDeviceId: 'default',
|
||||
outputDeviceId: 'default',
|
||||
focusedParticipantId: null,
|
||||
screenShareConfig: { height: 720, fps: 60, mode: 'gaming', codec: 'vp9', customBitrateKbps: null, shareAudio: !isElectron() },
|
||||
screenShareConfig: { height: 720, fps: 60, mode: 'gaming', codec: 'h264', customBitrateKbps: null, shareAudio: !isElectron() },
|
||||
participantVolumes: new Map(),
|
||||
setParticipantVolume: (userId, volume) => {
|
||||
set((state) => {
|
||||
@@ -326,9 +326,14 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
toggleScreenShare: () => set((state) => ({ isScreenSharing: !state.isScreenSharing })),
|
||||
|
||||
setFocusedParticipant: (id) => set({ focusedParticipantId: id }),
|
||||
setScreenShareConfig: (config) => set((state) => ({
|
||||
screenShareConfig: { ...state.screenShareConfig, ...config },
|
||||
})),
|
||||
setScreenShareConfig: (config) => set((state) => {
|
||||
const merged = { ...state.screenShareConfig, ...config };
|
||||
// When mode changes (without an explicit codec override), auto-set the smart default
|
||||
if (config.mode && !config.codec) {
|
||||
merged.codec = config.mode === 'gaming' ? 'h264' : 'vp9';
|
||||
}
|
||||
return { screenShareConfig: merged };
|
||||
}),
|
||||
noiseSuppression: true,
|
||||
echoCancellation: true,
|
||||
autoGainControl: true,
|
||||
|
||||
Reference in New Issue
Block a user