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');
}