Fix screenshare glitches

This commit is contained in:
2025-10-11 18:33:03 +03:00
Unverified
parent e1960d9e58
commit 174ab8f1fa
3 changed files with 180 additions and 69 deletions
+77 -38
View File
@@ -238,20 +238,27 @@ async function createPeerConnection(userId: number): Promise<RTCPeerConnection>
if (call && remoteStream) {
const track = event.track;
// Apply E2EE transform to the receiver for this new track if session key is available
// Apply E2EE transform to all tracks - video now uses header-preserving encryption
if (call.sessionKey && window.RTCRtpScriptTransform) {
try {
const receiver = call.peerConnection.getReceivers().find(r => r.track === track);
if (receiver && !call.transformedReceivers.has(receiver)) {
const key = await importAesGcmKey(call.sessionKey);
console.log(`Applying decrypt transform to newly received ${track.kind} track`);
console.log(`Applying decrypt transform to newly received ${track.kind} track:`);
console.log("- sessionId:", call.sessionId);
console.log("- sessionKey (first 8 bytes):", Array.from(new Uint8Array(call.sessionKey).slice(0, 8)));
// @ts-ignore
receiver.transform = new RTCRtpScriptTransform(new E2EEWorker(), { key, mode: 'decrypt', sessionId: call.sessionId });
call.transformedReceivers.add(receiver);
console.log(`Decrypt transform applied successfully to ${track.kind} track`);
}
} catch (error) {
console.error("Failed to apply E2EE to received track:", error);
}
} else {
console.log(`Skipping decrypt transform for ${track.kind} track - session key not available or RTCRtpScriptTransform not supported`);
console.log("Session key exists:", !!call.sessionKey);
console.log("RTCRtpScriptTransform available:", !!window.RTCRtpScriptTransform);
}
// Determine stream type based on track kind and signaling state
@@ -897,7 +904,7 @@ export async function toggleVideo(userId: number): Promise<boolean> {
// Add video track to peer connection
const videoTrack = videoStream.getVideoTracks()[0];
const sender = call.peerConnection.addTrack(videoTrack, videoStream);
call.peerConnection.addTrack(videoTrack, videoStream);
console.log("Video track added successfully");
console.log("Current senders:", call.peerConnection.getSenders().map(s => s.track?.kind));
@@ -907,19 +914,21 @@ export async function toggleVideo(userId: number): Promise<boolean> {
direction: t.direction
})));
// Apply E2EE transform if session key exists and not already transformed
if (call.sessionKey && window.RTCRtpScriptTransform && !call.transformedSenders.has(sender)) {
// Apply E2EE transform with header-preserving encryption for video
if (call.sessionKey && window.RTCRtpScriptTransform) {
try {
const key = await importAesGcmKey(call.sessionKey);
console.log("Applying E2EE to video sender");
sender.transform = new RTCRtpScriptTransform(new E2EEWorker(), { key, mode: "encrypt", sessionId: call.sessionId });
call.transformedSenders.add(sender);
console.log("Applying E2EE to video sender with header preservation");
const sender = call.peerConnection.getSenders().find(s => s.track === videoTrack);
if (sender && !call.transformedSenders.has(sender)) {
sender.transform = new RTCRtpScriptTransform(new E2EEWorker(), { key, mode: "encrypt", sessionId: call.sessionId });
call.transformedSenders.add(sender);
console.log("E2EE applied to video sender successfully");
}
} catch (error) {
console.error("Failed to apply E2EE to video:", error);
throw error; // Fail securely
}
} else {
console.log("Skipping E2EE for video - session key not available yet or already transformed");
}
// Notify local video stream handler
@@ -988,9 +997,12 @@ export async function toggleScreenShare(userId: number): Promise<boolean> {
if (!call.isScreenSharing) {
// Enable screen sharing
try {
// @ts-ignore - getDisplayMedia might not be in all TypeScript versions
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
video: {
width: { ideal: 1920, max: 3840 },
height: { ideal: 1080, max: 2160 },
frameRate: { ideal: 60, max: 60 }
},
audio: false
});
@@ -1017,32 +1029,9 @@ export async function toggleScreenShare(userId: number): Promise<boolean> {
toggleScreenShare(userId);
});
const sender = call.peerConnection.addTrack(videoTrack, screenStream);
console.log("Screen share track added successfully");
// Apply E2EE transform if session key exists and not already transformed
if (call.sessionKey && window.RTCRtpScriptTransform && !call.transformedSenders.has(sender)) {
try {
const key = await importAesGcmKey(call.sessionKey);
console.log("Applying E2EE to screen share sender");
// @ts-ignore
sender.transform = new RTCRtpScriptTransform(new E2EEWorker(), { key, mode: "encrypt", sessionId: call.sessionId });
call.transformedSenders.add(sender);
} catch (error) {
console.error("Failed to apply E2EE to screen share:", error);
throw error; // Fail securely
}
} else {
console.log("Skipping E2EE for screen share - session key not available yet or already transformed");
}
// Notify local screen share handler
if (onLocalScreenShare) {
onLocalScreenShare(userId, screenStream);
}
// Send signaling message to notify remote peer
// Send signaling message FIRST to notify remote peer before adding track
// This ensures the receiver knows it's screen share before the track arrives
console.log("Sending screen share toggle BEFORE adding track");
await sendSignalingMessage({
type: "call_screen_share_toggle",
fromUserId: 0,
@@ -1050,6 +1039,56 @@ export async function toggleScreenShare(userId: number): Promise<boolean> {
data: { enabled: true }
});
// Small delay to ensure signaling message is processed before track arrives
await new Promise(resolve => setTimeout(resolve, 100));
call.peerConnection.addTrack(videoTrack, screenStream);
console.log("Screen share track added, immediately applying E2EE transform");
// CRITICAL: Apply E2EE transform IMMEDIATELY after track is added
if (call.sessionKey && window.RTCRtpScriptTransform) {
try {
const key = await importAesGcmKey(call.sessionKey);
console.log("Applying E2EE to screen share sender:");
console.log("- sessionId:", call.sessionId);
console.log("- sessionKey (first 8 bytes):", Array.from(new Uint8Array(call.sessionKey).slice(0, 8)));
console.log("Available senders:", call.peerConnection.getSenders().map(s => ({
track: s.track?.kind,
id: s.track?.id
})));
console.log("Looking for screen share track:", videoTrack.id);
const sender = call.peerConnection.getSenders().find(s => s.track === videoTrack);
console.log("Found screen share sender:", !!sender);
if (sender && !call.transformedSenders.has(sender)) {
sender.transform = new RTCRtpScriptTransform(new E2EEWorker(), { key, mode: "encrypt", sessionId: call.sessionId });
call.transformedSenders.add(sender);
console.log("E2EE applied to screen share sender successfully");
} else {
console.log("Screen share sender not found or already transformed");
}
} catch (error) {
console.error("Failed to apply E2EE to screen share:", error);
throw error; // Fail securely
}
} else {
console.log("Skipping E2EE for screen share - session key not available yet");
console.log("Session key exists:", !!call.sessionKey);
console.log("RTCRtpScriptTransform available:", !!window.RTCRtpScriptTransform);
}
// Let browser handle screen share settings naturally
// Avoid applying constraints that might cause glitches
// Notify local screen share handler
if (onLocalScreenShare) {
onLocalScreenShare(userId, screenStream);
}
console.log("Screen share setup complete - using existing session key:", call.sessionId);
return true;
} catch (error) {
console.error("Failed to enable screen sharing:", error);