From 8487e723d4aadb4c25083215a2562830022df23d Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Tue, 17 Feb 2026 23:29:25 +0100 Subject: [PATCH] fix: disable camera and remove Y4M fake video to prevent browser crash when audio flows Co-authored-by: Cursor --- src/bot/orchestrator.ts | 65 ++++++++++++----------------------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts index 828ae4f..8b0f9db 100644 --- a/src/bot/orchestrator.ts +++ b/src/bot/orchestrator.ts @@ -3,7 +3,6 @@ import { Logger } from 'winston'; import { v4 as uuidv4 } from 'uuid'; import path from 'path'; import fs from 'fs'; -import os from 'os'; import WebSocket from 'ws'; import { config } from '../config'; @@ -17,37 +16,8 @@ import { ChatProcedure, ChatMessageEntry } from './chatProcedure'; import { AuthProcedure } from './authProcedure'; import { isValidMeetingUrl } from './meetingUrlParser'; -/** - * Generate a solid-white Y4M video file for use as fake camera input. - * Chromium loops this single frame at 30fps, so participants see a static white image. - * Later this can be replaced with a custom image (avatar/background). - */ -function _generateFakeVideoFile(): string { - const width = 1280; - const height = 720; - const filePath = path.join(os.tmpdir(), 'bot-video-white.y4m'); - - if (fs.existsSync(filePath)) return filePath; - - const header = `YUV4MPEG2 W${width} H${height} F30:1 Ip A0:0 C420jpeg\n`; - const frameHeader = 'FRAME\n'; - - // White in YUV: Y=235, U=128, V=128 - const yPlane = Buffer.alloc(width * height, 235); - const uvSize = (width / 2) * (height / 2); - const uPlane = Buffer.alloc(uvSize, 128); - const vPlane = Buffer.alloc(uvSize, 128); - - const fd = fs.openSync(filePath, 'w'); - fs.writeSync(fd, header); - fs.writeSync(fd, frameHeader); - fs.writeSync(fd, yPlane); - fs.writeSync(fd, uPlane); - fs.writeSync(fd, vPlane); - fs.closeSync(fd); - - return filePath; -} +// Camera / fake video injection is disabled for now to focus on stability. +// The Y4M fake video file was causing browser crashes when audio started flowing. export interface OrchestratorCallbacks { onStateChange: (state: BotState, message?: string) => void; @@ -189,9 +159,6 @@ export class BotOrchestrator { // Dismiss any post-join permission modals (e.g. "Manage windows on all displays") await this._joinProcedure!.dismissBrowserPermissionModals(); - // Verify camera is on in the meeting - await this._ensureCameraOnInMeeting(); - // Initialize audio playback await this._audioProcedure!.initialize(); @@ -309,8 +276,8 @@ export class BotOrchestrator { await this._takeScreenshot('auth-no-join-now'); } - // Activate camera toggle if it's off (so background image is visible) - await this._ensureCameraOn(); + // Camera stays OFF — no video injection, focus on communication stability + this._logger.info('Camera left OFF (video disabled for stability)'); await this._page!.waitForTimeout(2000); @@ -347,9 +314,6 @@ export class BotOrchestrator { // Start keepalive to prevent idle disconnect this._startKeepAlive(); - // Verify camera is on in the meeting - await this._ensureCameraOnInMeeting(); - // Initialize audio playback await this._audioProcedure!.initialize(); @@ -803,22 +767,15 @@ export class BotOrchestrator { private async _launchBrowser(authMode: boolean = false): Promise { this._logger.info(`Launching browser (authMode=${authMode})...`); - // Generate a solid white Y4M video file so participants see a clean image - const fakeVideoPath = _generateFakeVideoFile(); - this._logger.info(`Fake video file: ${fakeVideoPath}`); - const args = authMode ? [ - // Chromium Minimal: only --no-sandbox + fake media (proven to work for authenticated join) '--no-sandbox', '--use-fake-ui-for-media-stream', '--use-fake-device-for-media-stream', - `--use-file-for-fake-video-capture=${fakeVideoPath}`, ] : [ '--use-fake-ui-for-media-stream', '--use-fake-device-for-media-stream', - `--use-file-for-fake-video-capture=${fakeVideoPath}`, '--disable-web-security', '--disable-features=IsolateOrigins,site-per-process', '--autoplay-policy=no-user-gesture-required', @@ -933,6 +890,20 @@ export class BotOrchestrator { } }); + // Handle browser renderer crash (Chromium process segfault) + this._page.on('crash', () => { + this._logger.error('BROWSER CRASH: Chromium renderer process crashed!'); + this._setState('error', 'Browser crashed'); + }); + + // Handle browser disconnection (entire browser process dies) + this._browser.on('disconnected', () => { + if (!this._isShuttingDown) { + this._logger.error('BROWSER DISCONNECTED: Browser process died unexpectedly'); + this._setState('error', 'Browser process died'); + } + }); + this._logger.info('Browser launched'); }