diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea8e878d..337dafc8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -132,7 +132,7 @@ jobs: run: npm ci --ignore-scripts - name: Install bundled FFmpeg binary - run: node node_modules/ffmpeg-static/install.js + run: node scripts/install-ffmpeg-static.mjs - name: Verify bundled FFmpeg binary run: | @@ -233,7 +233,7 @@ jobs: run: npm ci --ignore-scripts - name: Install bundled FFmpeg binary - run: node node_modules/ffmpeg-static/install.js + run: node scripts/install-ffmpeg-static.mjs - name: Verify bundled FFmpeg binary run: | @@ -392,7 +392,7 @@ jobs: run: npm ci --ignore-scripts - name: Install bundled FFmpeg binary - run: node node_modules/ffmpeg-static/install.js + run: node scripts/install-ffmpeg-static.mjs - name: Verify bundled FFmpeg binary run: | @@ -470,7 +470,7 @@ jobs: run: npm ci --ignore-scripts - name: Install bundled FFmpeg binary - run: node node_modules/ffmpeg-static/install.js + run: node scripts/install-ffmpeg-static.mjs - name: Verify bundled FFmpeg binary run: | diff --git a/scripts/install-ffmpeg-static.mjs b/scripts/install-ffmpeg-static.mjs new file mode 100644 index 00000000..80fa3773 --- /dev/null +++ b/scripts/install-ffmpeg-static.mjs @@ -0,0 +1,32 @@ +import { spawnSync } from "node:child_process"; + +const maxAttempts = Number.parseInt(process.env.FFMPEG_STATIC_INSTALL_ATTEMPTS ?? "3", 10); +const baseDelayMs = Number.parseInt(process.env.FFMPEG_STATIC_INSTALL_DELAY_MS ?? "2000", 10); + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function main() { + for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { + const result = spawnSync(process.execPath, ["node_modules/ffmpeg-static/install.js"], { + stdio: "inherit", + }); + + if (result.status === 0) { + return; + } + + if (attempt === maxAttempts) { + process.exit(result.status ?? 1); + } + + const delayMs = baseDelayMs * attempt; + console.warn( + `[ffmpeg-static] Install attempt ${attempt}/${maxAttempts} failed; retrying in ${delayMs}ms...`, + ); + await sleep(delayMs); + } +} + +await main(); diff --git a/src/lib/exporter/modernFrameRenderer.ts b/src/lib/exporter/modernFrameRenderer.ts index a6819f6c..5372937f 100644 --- a/src/lib/exporter/modernFrameRenderer.ts +++ b/src/lib/exporter/modernFrameRenderer.ts @@ -477,7 +477,6 @@ export class FrameRenderer { private currentVideoTime = 0; private cursorOverlay: PixiCursorOverlay | null = null; private lastSyncedWebcamTime: number | null = null; - private lastWebcamCacheRefreshTime: number | null = null; private webcamRenderMode: "hidden" | "live" | "cached" = "hidden"; private webcamLayoutCache: WebcamLayoutCache | null = null; private videoTextureUsesStartupStaging = false; @@ -2367,7 +2366,6 @@ export class FrameRenderer { this.webcamFrameCacheCanvas = null; this.webcamFrameCacheCtx = null; this.lastSyncedWebcamTime = null; - this.lastWebcamCacheRefreshTime = null; this.webcamLayoutCache = null; this.webcamRenderMode = "hidden"; return; @@ -2380,7 +2378,6 @@ export class FrameRenderer { this.clearWebcamMediaElement(); this.webcamFrameCacheCanvas = null; this.webcamFrameCacheCtx = null; - this.lastWebcamCacheRefreshTime = null; this.webcamLayoutCache = null; this.webcamRenderMode = "hidden"; @@ -2391,7 +2388,6 @@ export class FrameRenderer { this.webcamVideoElement = null; this.webcamSeekPromise = null; this.lastSyncedWebcamTime = null; - this.lastWebcamCacheRefreshTime = null; return; } catch (error) { console.warn( @@ -2452,31 +2448,6 @@ export class FrameRenderer { this.webcamRenderMode = nextMode; } - private shouldRefreshWebcamFrameCache(width: number, height: number): boolean { - const cropRegion = this.config.webcam?.cropRegion; - const sourceRect = getWebcamCropSourceRect(cropRegion, width, height); - const targetWidth = Math.max(1, Math.ceil(sourceRect.sw)); - const targetHeight = Math.max(1, Math.ceil(sourceRect.sh)); - - if ( - !this.webcamFrameCacheCanvas || - this.webcamFrameCacheCanvas.width !== targetWidth || - this.webcamFrameCacheCanvas.height !== targetHeight - ) { - return true; - } - - if (!isWebcamCropRegionDefault(cropRegion)) { - return true; - } - - if (this.lastWebcamCacheRefreshTime === null) { - return true; - } - - return Math.abs(this.currentVideoTime - this.lastWebcamCacheRefreshTime) >= 0.25; - } - private ensureWebcamFrameCache(width: number, height: number): boolean { const targetWidth = Math.max(1, Math.ceil(width)); const targetHeight = Math.max(1, Math.ceil(height)); @@ -2504,7 +2475,6 @@ export class FrameRenderer { source: CanvasImageSource | VideoFrame, width: number, height: number, - referenceTimeSeconds = this.currentVideoTime, ): boolean { const sourceRect = getWebcamCropSourceRect(this.config.webcam?.cropRegion, width, height); if (!this.ensureWebcamFrameCache(sourceRect.sw, sourceRect.sh)) { @@ -2532,7 +2502,6 @@ export class FrameRenderer { this.webcamFrameCacheCanvas.width, this.webcamFrameCacheCanvas.height, ); - this.lastWebcamCacheRefreshTime = referenceTimeSeconds; return true; } @@ -2558,7 +2527,6 @@ export class FrameRenderer { liveSourceWidth: number, liveSourceHeight: number, canUseLiveSource: boolean, - referenceTimeSeconds = this.currentVideoTime, ): WebcamRenderSource | null { if (canUseLiveSource && liveSource && liveSourceWidth > 0 && liveSourceHeight > 0) { const usesDefaultCropRegion = isWebcamCropRegionDefault(this.config.webcam?.cropRegion); @@ -2572,7 +2540,6 @@ export class FrameRenderer { liveSource, liveSourceWidth, liveSourceHeight, - referenceTimeSeconds, ); const cachedSource = this.getCachedWebcamRenderSource(); if (cachedSource) { @@ -2901,7 +2868,6 @@ export class FrameRenderer { liveSourceDimensions.width, liveSourceDimensions.height, canUseLiveSource, - referenceTimeSeconds, ); if (!renderableWebcamSource) { @@ -4045,7 +4011,6 @@ export class FrameRenderer { this.annotationScaleFactor = 1; this.lastSyncedWebcamTime = null; - this.lastWebcamCacheRefreshTime = null; this.webcamRenderMode = "hidden"; this.webcamLayoutCache = null; this.layoutCache = null;