mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
fix(engine): scale Chrome memory budget and frame cache to available RAM
On low-memory systems (<4GB free), Chrome's --force-gpu-mem-available-mb=4096
causes the renderer to allocate more GPU texture memory than the system can
provide, leading to OOM crashes during frame capture ("Target closed").
Changes:
- Scale --force-gpu-mem-available-mb to match available system RAM
(512MB when <2GB free, 1024MB when <4GB, 4096MB otherwise)
- Add --js-flags=--max-old-space-size=N on low-memory systems to cap
Chrome's V8 heap (256MB when <2GB free, 512MB when <4GB)
- Scale frame data URI cache defaults: 32 entries/128MB when <2GB free,
64 entries/256MB when <4GB, unchanged otherwise
Closes #1072
This commit is contained in:
@@ -208,6 +208,29 @@ export const DEFAULT_CONFIG: EngineConfig = {
|
|||||||
debug: false,
|
debug: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getSystemFreeMb(): number {
|
||||||
|
try {
|
||||||
|
const { freemem } = require("os") as typeof import("os");
|
||||||
|
return Math.floor(freemem() / (1024 * 1024));
|
||||||
|
} catch {
|
||||||
|
return 8192;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function memoryAdaptiveCacheLimit(): number {
|
||||||
|
const free = getSystemFreeMb();
|
||||||
|
if (free < 2048) return 32;
|
||||||
|
if (free < 4096) return 64;
|
||||||
|
return DEFAULT_CONFIG.frameDataUriCacheLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function memoryAdaptiveCacheBytesMb(): number {
|
||||||
|
const free = getSystemFreeMb();
|
||||||
|
if (free < 2048) return 128;
|
||||||
|
if (free < 4096) return 256;
|
||||||
|
return DEFAULT_CONFIG.frameDataUriCacheBytesLimitMb;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve configuration by merging: defaults ← env vars ← explicit overrides.
|
* Resolve configuration by merging: defaults ← env vars ← explicit overrides.
|
||||||
* Env vars provide backward compatibility during migration; explicit config
|
* Env vars provide backward compatibility during migration; explicit config
|
||||||
@@ -298,14 +321,11 @@ export function resolveConfig(overrides?: Partial<EngineConfig>): EngineConfig {
|
|||||||
audioGain: envNum("PRODUCER_AUDIO_GAIN", DEFAULT_CONFIG.audioGain),
|
audioGain: envNum("PRODUCER_AUDIO_GAIN", DEFAULT_CONFIG.audioGain),
|
||||||
frameDataUriCacheLimit: Math.max(
|
frameDataUriCacheLimit: Math.max(
|
||||||
32,
|
32,
|
||||||
envNum("PRODUCER_FRAME_DATA_URI_CACHE_LIMIT", DEFAULT_CONFIG.frameDataUriCacheLimit),
|
envNum("PRODUCER_FRAME_DATA_URI_CACHE_LIMIT", memoryAdaptiveCacheLimit()),
|
||||||
),
|
),
|
||||||
frameDataUriCacheBytesLimitMb: Math.max(
|
frameDataUriCacheBytesLimitMb: Math.max(
|
||||||
64,
|
64,
|
||||||
envNum(
|
envNum("PRODUCER_FRAME_DATA_URI_CACHE_BYTES_MB", memoryAdaptiveCacheBytesMb()),
|
||||||
"PRODUCER_FRAME_DATA_URI_CACHE_BYTES_MB",
|
|
||||||
DEFAULT_CONFIG.frameDataUriCacheBytesLimitMb,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
playerReadyTimeout: envNum(
|
playerReadyTimeout: envNum(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import type { Browser, PuppeteerNode } from "puppeteer-core";
|
import type { Browser, PuppeteerNode } from "puppeteer-core";
|
||||||
import { existsSync, readdirSync } from "fs";
|
import { existsSync, readdirSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { homedir } from "os";
|
import { freemem, homedir } from "os";
|
||||||
import { DEFAULT_CONFIG, type EngineConfig } from "../config.js";
|
import { DEFAULT_CONFIG, type EngineConfig } from "../config.js";
|
||||||
|
|
||||||
let _puppeteer: PuppeteerNode | undefined;
|
let _puppeteer: PuppeteerNode | undefined;
|
||||||
@@ -476,6 +476,24 @@ export function _setPuppeteerForTests(mock: PuppeteerNode | undefined): void {
|
|||||||
_puppeteer = mock;
|
_puppeteer = mock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFreeMb(): number {
|
||||||
|
return Math.floor(freemem() / (1024 * 1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGpuMemBudgetMb(): number {
|
||||||
|
const free = getFreeMb();
|
||||||
|
if (free < 2048) return 512;
|
||||||
|
if (free < 4096) return 1024;
|
||||||
|
return 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLowMemoryFlags(): string[] {
|
||||||
|
const free = getFreeMb();
|
||||||
|
if (free >= 4096) return [];
|
||||||
|
const heapMb = free < 2048 ? 256 : 512;
|
||||||
|
return [`--js-flags=--max-old-space-size=${heapMb}`];
|
||||||
|
}
|
||||||
|
|
||||||
export interface BuildChromeArgsOptions {
|
export interface BuildChromeArgsOptions {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -530,9 +548,10 @@ export function buildChromeArgs(
|
|||||||
"--disable-print-preview",
|
"--disable-print-preview",
|
||||||
"--no-pings",
|
"--no-pings",
|
||||||
"--no-zygote",
|
"--no-zygote",
|
||||||
// Memory
|
// Memory — scale GPU budget to available system RAM
|
||||||
"--force-gpu-mem-available-mb=4096",
|
`--force-gpu-mem-available-mb=${getGpuMemBudgetMb()}`,
|
||||||
"--disk-cache-size=268435456",
|
"--disk-cache-size=268435456",
|
||||||
|
...getLowMemoryFlags(),
|
||||||
// Disable features that add overhead
|
// Disable features that add overhead
|
||||||
"--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process,Translate,BackForwardCache,IntensiveWakeUpThrottling",
|
"--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process,Translate,BackForwardCache,IntensiveWakeUpThrottling",
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user