Skip to content

Commit 3910cc4

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 9ee4d50 commit 3910cc4

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
@@ -304,13 +304,10 @@ export class FFmpeg {
304304
public terminate = (): void => this.#terminate(ERROR_TERMINATED);
305305

306306
#terminate = (reason: Error): void => {
307-
const ids = Object.keys(this.#rejects);
308307
// rejects all incomplete Promises.
309-
for (const id of ids) {
310-
this.#rejects[id](reason);
311-
delete this.#rejects[id];
312-
delete this.#resolves[id];
313-
}
308+
Object.values(this.#rejects).forEach((reject) => reject(reason));
309+
this.#rejects = {};
310+
this.#resolves = {};
314311

315312
if (this.#worker) {
316313
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;
@@ -77,9 +79,15 @@ const load = async ({
7779
// URL for _locateFile() in bind.js. In Node.js (file: URLs) emscripten
7880
// starts them from the core module itself and finds the wasm next to it.
7981
const mainScriptUrlOrBlob = `${coreURL}#${btoa(JSON.stringify({ wasmURL }))}`;
80-
ffmpeg = await (self as WorkerGlobalScope).createFFmpegCore(
81-
coreURL.startsWith("file:") ? {} : { mainScriptUrlOrBlob }
82-
);
82+
try {
83+
ffmpeg = await (self as WorkerGlobalScope).createFFmpegCore(
84+
coreURL.startsWith("file:") ? {} : { mainScriptUrlOrBlob }
85+
);
86+
} catch (e) {
87+
// e.g. an HTML 404 page or SPA fallback served instead of the wasm (#609)
88+
if (String(e).includes("magic word")) throw new Error(`${wasmURL} is not a WebAssembly file (${e})`);
89+
throw e;
90+
}
8391
ffmpeg.setLogger((data) =>
8492
self.postMessage({ type: FFMessageType.LOG, data })
8593
);
@@ -89,7 +97,7 @@ const load = async ({
8997
data,
9098
})
9199
);
92-
return first;
100+
return true;
93101
};
94102

95103
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
@@ -83,7 +83,8 @@ function setLogger(logger) {
8383
Module["logger"] = logger;
8484
}
8585

86-
function setTimeout(timeout) {
86+
// Not named setTimeout: that would shadow the global inside the core (#611).
87+
function setExecTimeout(timeout) {
8788
Module["timeout"] = timeout;
8889
}
8990

@@ -134,7 +135,7 @@ Module["locateFile"] = _locateFile;
134135
Module["exec"] = exec;
135136
Module["ffprobe"] = ffprobe;
136137
Module["setLogger"] = setLogger;
137-
Module["setTimeout"] = setTimeout;
138+
Module["setTimeout"] = setExecTimeout;
138139
Module["setProgress"] = setProgress;
139140
Module["reset"] = reset;
140141
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
expect(await ffmpeg.load({ coreURL: coreURL() })).to.equal(false);
1010
expect(await ffmpeg.readFile("/kept.txt", "utf8")).to.equal("hello");

‎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
await expect(unloaded.load({ coreURL: coreURL(), wasmURL })).rejects.toThrow(wasmURL);
1111
});

0 commit comments

Comments
 (0)