fix: screenshare codec fallback, native FPS, and connection info
- Gaming mode uses H.264 primary (hardware NVENC encoding, zero CPU impact on games). Text mode uses VP9 primary with H.264 backup and SIMULCAST policy for Safari compatibility. - Fix native mode starting at 30fps by decoupling frameRate constraint from resolution constraint in both screenShare.ts overdrive and useLiveKit.ts updateActiveTracks. - Filter paused backup codec tracks in Connection Info stats so dead 0kbps entries don't show alongside active codec tracks.
This commit is contained in:
@@ -612,9 +612,12 @@ export function useLiveKit() {
|
||||
if (screenPub?.videoTrack) {
|
||||
const mediaTrack = getMediaStreamTrack(screenPub.videoTrack);
|
||||
if (mediaTrack) {
|
||||
// Skip resolution constraints for native mode — capture is already at native dims
|
||||
if (opts.capture.width > 0 && opts.capture.height > 0) {
|
||||
await mediaTrack.applyConstraints({ width: { ideal: opts.capture.width }, height: { ideal: opts.capture.height }, frameRate: { ideal: opts.capture.frameRate } });
|
||||
// Standard mode: apply resolution + frameRate together
|
||||
await mediaTrack.applyConstraints({ width: { ideal: opts.capture.width }, height: { ideal: opts.capture.height }, frameRate: { ideal: opts.capture.frameRate, min: 15 } });
|
||||
} else {
|
||||
// Native mode: apply frameRate only — never pass 0 to width/height
|
||||
await mediaTrack.applyConstraints({ frameRate: { ideal: opts.capture.frameRate, min: 15 } });
|
||||
}
|
||||
mediaTrack.contentHint = opts.contentHint;
|
||||
}
|
||||
|
||||
@@ -511,14 +511,30 @@ export function useTrackStats(enabled: boolean): TrackStatsSnapshot | null {
|
||||
network.packetLoss = (totalPacketsLost / totalPackets) * 100;
|
||||
}
|
||||
|
||||
// ── Step F: Cleanup stale prevSample entries ──
|
||||
// ── Step F: Filter paused backup codec tracks ──
|
||||
// With backupCodec enabled, the publisher may have two concurrent outbound
|
||||
// video tracks for the same source (e.g. VP9 + H.264). When one is paused
|
||||
// by dynacast (0 bitrate), hide it if an active sibling exists.
|
||||
const filteredVideoTracks = videoTracks.filter((track) => {
|
||||
if (track.direction !== 'send' || track.bitrate > 0) return true;
|
||||
const hasActiveSibling = videoTracks.some(
|
||||
(other) =>
|
||||
other !== track &&
|
||||
other.direction === 'send' &&
|
||||
other.source === track.source &&
|
||||
other.bitrate > 0,
|
||||
);
|
||||
return !hasActiveSibling;
|
||||
});
|
||||
|
||||
// ── Step G: Cleanup stale prevSample entries ──
|
||||
for (const key of prev.keys()) {
|
||||
if (!seenKeys.has(key)) {
|
||||
prev.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
setSnapshot({ network, audioTracks, videoTracks });
|
||||
setSnapshot({ network, audioTracks, videoTracks: filteredVideoTracks });
|
||||
};
|
||||
|
||||
poll();
|
||||
|
||||
Reference in New Issue
Block a user