Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,16 @@ We expect all contributors to follow our Code of Conduct to create a safe, welco
- **Constructive feedback is encouraged.**

Thank you for making Reframe better! 🎬

## Cross-Origin Isolation & GitHub Pages

This project uses `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy`
headers to enable cross-origin isolation, which allows FFmpeg.wasm to run in
multi-threaded mode for significantly faster video exports.

**GitHub Pages does not support custom HTTP headers.**
If you are deploying to GitHub Pages, cross-origin isolation cannot be enabled
and FFmpeg will automatically fall back to single-threaded mode.

For full multi-threading support, deploy to **Vercel**, **Netlify**, or
**Cloudflare Pages** where custom headers are supported.
3 changes: 3 additions & 0 deletions public/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
20 changes: 15 additions & 5 deletions src/lib/ffmpeg.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FFmpeg } from "@ffmpeg/ffmpeg";
import { fetchFile } from "@ffmpeg/util";
import { fetchFile, toBlobURL } from "@ffmpeg/util";
import { EditRecipe, ExportResult, BackgroundMusicOptions, ImageOverlayOptions } from "./types";
import { getPresetById } from "./presets";
import { simd } from "wasm-feature-detect";
Expand Down Expand Up @@ -57,10 +57,20 @@ export async function loadFFmpeg(
try {
ffmpeg.on("progress", handleProgress);

// Secure engine load using verified runtime checksum hashes from main
const isIsolated = typeof self !== "undefined" && self.crossOriginIsolated;
const baseURL = isIsolated
? "https://unpkg.com/@ffmpeg/core-mt@0.12.6/dist/esm"
: "https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm";

await ffmpeg.load({
coreURL: await fetchWithIntegrity(`${CORE_BASE_URL}/ffmpeg-core.js`, "text/javascript"),
wasmURL: await fetchWithIntegrity(`${CORE_BASE_URL}/ffmpeg-core.wasm`, "application/wasm"),
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"),
...(isIsolated && {
workerURL: await toBlobURL(
`${baseURL}/ffmpeg-core.worker.js`,
"text/javascript"
),
}),
}, { signal });

onProgress?.(100);
Expand Down Expand Up @@ -477,4 +487,4 @@ export async function exportVideo(
export function formatBytes(bytes: number): string {
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
}
10 changes: 9 additions & 1 deletion vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
},
{
"key": "Cross-Origin-Opener-Policy",
"value": "same-origin"
},
{
"key": "Cross-Origin-Embedder-Policy",
"value": "require-corp"
}
]
}
]
}
}
Loading