Skip to content

Commit 1571b2e

Browse files
committed
Fix a second load() leaking a core, name a non-wasm wasmURL, types for TS < 4.4, rename the internal setTimeout
- load() on a loaded instance keeps its core (#494) - a wasmURL serving HTML says which URL is wrong (#609) - Callbacks is a Record, which TypeScript < 4.4 accepts (#701) - bind.js no longer shadows the global setTimeout inside the core (#611)
1 parent a237cc1 commit 1571b2e

6 files changed

Lines changed: 22 additions & 18 deletions

File tree

‎packages/ffmpeg/src/classes.ts‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,10 @@ export class FFmpeg {
299299
public terminate = (): void => this.#terminate(ERROR_TERMINATED);
300300

301301
#terminate = (reason: Error): void => {
302-
const ids = Object.keys(this.#rejects);
303302
// rejects all incomplete Promises.
304-
for (const id of ids) {
305-
this.#rejects[id](reason);
306-
delete this.#rejects[id];
307-
delete this.#resolves[id];
308-
}
303+
Object.values(this.#rejects).forEach((reject) => reject(reason));
304+
this.#rejects = {};
305+
this.#resolves = {};
309306

310307
if (this.#worker) {
311308
this.#worker.terminate();

‎packages/ffmpeg/src/types.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,7 @@ export type CallbackData =
164164
| FSNode[]
165165
| undefined;
166166

167-
export interface Callbacks {
168-
[id: number | string]: (data: CallbackData) => void;
169-
}
167+
export type Callbacks = Record<number, (data: CallbackData) => void>;
170168

171169
export type LogEventCallback = (event: LogEvent) => void;
172170
export type ProgressEventCallback = (event: ProgressEvent) => void;

‎packages/ffmpeg/src/worker.ts‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ const load = async ({
4747
coreURL: _coreURL,
4848
wasmURL: _wasmURL,
4949
}: FFMessageLoadConfig): Promise<IsFirst> => {
50-
const first = !ffmpeg;
50+
// A second core would leak the first one's memory and threads (#494);
51+
// terminate() and load() again to switch cores.
52+
if (ffmpeg) return false;
5153

5254
// ffmpeg-core runs FFmpeg's threads on a SharedArrayBuffer.
5355
if (typeof SharedArrayBuffer === "undefined") throw ERROR_NOT_ISOLATED;
@@ -75,9 +77,15 @@ const load = async ({
7577

7678
// the hash tells FFmpeg's threads where the wasm is (see _locateFile in bind.js)
7779
const mainScriptUrlOrBlob = `${coreURL}#${btoa(JSON.stringify({ wasmURL }))}`;
78-
ffmpeg = await (self as WorkerGlobalScope).createFFmpegCore(
79-
coreURL.startsWith("file:") ? {} : { mainScriptUrlOrBlob }
80-
);
80+
try {
81+
ffmpeg = await (self as WorkerGlobalScope).createFFmpegCore(
82+
coreURL.startsWith("file:") ? {} : { mainScriptUrlOrBlob }
83+
);
84+
} catch (e) {
85+
// e.g. an HTML 404 page or SPA fallback served instead of the wasm (#609)
86+
if (String(e).includes("magic word")) throw new Error(`${wasmURL} is not a WebAssembly file (${e})`);
87+
throw e;
88+
}
8189
ffmpeg.setLogger((data) =>
8290
self.postMessage({ type: FFMessageType.LOG, data })
8391
);
@@ -87,7 +95,7 @@ const load = async ({
8795
data,
8896
})
8997
);
90-
return first;
98+
return true;
9199
};
92100

93101
const exec = ({ args, timeout = -1 }: FFMessageExecData): ExitCode => {

‎src/bind/ffmpeg/bind.js‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ function setLogger(logger) {
7878
Module["logger"] = logger;
7979
}
8080

81-
function setTimeout(timeout) {
81+
// Not named setTimeout: that would shadow the global inside the core (#611).
82+
function setExecTimeout(timeout) {
8283
Module["timeout"] = timeout;
8384
}
8485

@@ -129,7 +130,7 @@ Module["locateFile"] = _locateFile;
129130
Module["exec"] = exec;
130131
Module["ffprobe"] = ffprobe;
131132
Module["setLogger"] = setLogger;
132-
Module["setTimeout"] = setTimeout;
133+
Module["setTimeout"] = setExecTimeout;
133134
Module["setProgress"] = setProgress;
134135
Module["reset"] = reset;
135136
Module["terminateThreads"] = terminateThreads;

‎tests/ffmpeg/issues/494-double-load.test.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { expect } from "vitest";
55
import { test, coreURL } from "../../helpers/ffmpeg.js";
66

7-
test.fails("load() again keeps the loaded core (#494)", { timeout: 60000 }, async ({ ffmpeg }) => {
7+
test("load() again keeps the loaded core", { timeout: 60000 }, async ({ ffmpeg }) => {
88
await ffmpeg.writeFile("/kept.txt", "hello");
99
const isFirst = await ffmpeg.load({ coreURL: coreURL() });
1010
const kept = await ffmpeg.readFile("/kept.txt", "utf8");

‎tests/ffmpeg/issues/609-wasm-not-wasm.test.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { expect } from "vitest";
66
import { test, coreURL } from "../../helpers/ffmpeg.js";
77

8-
test.fails("names the URL when the wasm file is not WebAssembly (#609)", async ({ unloaded }) => {
8+
test("names the URL when the wasm file is not WebAssembly", async ({ unloaded }) => {
99
const wasmURL = `${location.origin}/tests/README.md`;
1010
const loading = unloaded.load({ coreURL: coreURL(), wasmURL });
1111
await expect(loading).rejects.toThrow(wasmURL);

0 commit comments

Comments
 (0)