Implement 1080p 60fps capture and WebSocket backpressure

This commit is contained in:
srtk 2026-02-13 22:29:20 +05:30
parent b3f5902c44
commit 0167aeb7b9
3 changed files with 24 additions and 16 deletions

View file

@ -17,7 +17,6 @@ export enum MediaType {
export class NetworkManager extends EventEmitter {
private ws: WebSocket | null = null;
private userId: number = 0;
private roomCode: string = '';
private videoSeq: number = 0;
private audioSeq: number = 0;
private screenSeq = 0;
@ -25,6 +24,7 @@ export class NetworkManager extends EventEmitter {
private pingInterval: NodeJS.Timeout | null = null;
private lastBinaryLog: number = 0;
private binaryCount: number = 0;
private dropCount: number = 0;
constructor(mainWindow: BrowserWindow) {
super();
@ -32,7 +32,6 @@ export class NetworkManager extends EventEmitter {
}
async connect(serverUrl: string, roomCode: string, displayName: string): Promise<any> {
this.roomCode = roomCode;
return new Promise((resolve, reject) => {
// Determine Host and Protocol
let host = serverUrl.trim().replace(/^wss?:\/\//, '').replace(/\/$/, '');
@ -127,11 +126,7 @@ export class NetworkManager extends EventEmitter {
this.safeSend('chat-message', msg.data);
break;
case 'UpdateStream':
// Ignore stream updates for self (we manage local state directly)
if (msg.data.user_id !== this.userId) {
console.log(`[Network] Peer Stream Update: User=${msg.data.user_id} Type=${msg.data.media_type} Active=${msg.data.active}`);
this.safeSend('peer-stream-update', msg.data);
}
this.safeSend('peer-stream-update', msg.data);
break;
case 'Error':
console.error('WS Error Msg:', msg.data);
@ -226,7 +221,19 @@ export class NetworkManager extends EventEmitter {
// --- New Encode Methods ---
sendEncodedVideoChunk(chunk: any, isKeyFrame: boolean, timestamp: number, streamType: 'video' | 'screen' = 'video') {
const MAX_PAYLOAD = 1400; // WS can handle larger but keeping small for consistency
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
// Backpressure check: If we have > 2.5MB buffered, drop this frame.
// For video, it's better to drop than to lag.
if (this.ws.bufferedAmount > 2.5 * 1024 * 1024) {
this.dropCount++;
if (this.dropCount % 60 === 0) {
console.warn(`[Network] Backpressure! Dropped ${this.dropCount} video frames. Buffered: ${this.ws.bufferedAmount} bytes`);
}
return;
}
const MAX_PAYLOAD = 16384;
const totalSize = chunk.length;
const seq = streamType === 'screen' ? this.screenSeq++ : this.videoSeq++;
@ -237,7 +244,7 @@ export class NetworkManager extends EventEmitter {
const end = Math.min(start + MAX_PAYLOAD, totalSize);
const slice = chunk.slice(start, end);
// Header (22 bytes)
// Header (24 bytes)
const header = Buffer.alloc(HEADER_SIZE);
header.writeUInt8(1, 0); // Version
const mType = streamType === 'screen' ? MediaType.Screen : MediaType.Video;

View file

@ -482,7 +482,7 @@ function App() {
video: {
width: 1280,
height: 720,
frameRate: 30,
frameRate: 60,
deviceId: selectedVideoDevice ? { exact: selectedVideoDevice } : undefined
}
});
@ -566,7 +566,8 @@ function App() {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId,
maxWidth: 1920,
maxHeight: 1080
maxHeight: 1080,
maxFrameRate: 60
}
}
} as any);

View file

@ -51,8 +51,8 @@ export class MediaEngine extends SimpleEventEmitter {
codec: 'avc1.42001f', // H.264 Baseline Profile Level 3.1 (720p safe)
width: 1280,
height: 720,
bitrate: 2_000_000,
framerate: 30,
bitrate: 4_000_000,
framerate: 60,
latencyMode: 'realtime',
avc: { format: 'annexb' }
};
@ -62,8 +62,8 @@ export class MediaEngine extends SimpleEventEmitter {
codec: 'avc1.64002a',
width: 1920,
height: 1080,
bitrate: 2_000_000, // Reduced to 2 Mbps for better stability/FPS
framerate: 30,
bitrate: 8_000_000, // Reduced to 2 Mbps for better stability/FPS
framerate: 60,
latencyMode: 'realtime', // Changed from 'quality' to 'realtime' for lower latency
avc: { format: 'annexb' }
};
@ -193,7 +193,7 @@ export class MediaEngine extends SimpleEventEmitter {
// Note: Decoders are usually more flexible, but giving a hint helps.
// Screen share uses High Profile, Video uses Baseline.
const config: VideoDecoderConfig = streamType === 'screen'
? { codec: 'avc1.64002a', optimizeForLatency: false }
? { codec: 'avc1.64002a', optimizeForLatency: true }
: { codec: 'avc1.42001f', optimizeForLatency: true };
decoder.configure(config);