Skip to content

Commit 50f33d3

Browse files
JacobiusMakesclaude
andcommitted
fix: AbortSignal now actually stops the running ffmpeg exec (#719)
Previously, aborting an exec() or ffprobe() only rejected the JS Promise — the underlying WASM process kept running to completion. Fix: when the abort signal fires for EXEC or FFPROBE, immediately send a CANCEL message to the worker. The worker calls ffmpeg.setTimeout(1), setting the WASM watchdog to fire after 1 ms, stopping the current command as quickly as possible (exit code 1) without terminating the entire worker. Changes: - const.ts: add FFMessageType.CANCEL - worker.ts: add cancel() handler calling ffmpeg.setTimeout(1) - classes.ts: send CANCEL on exec/ffprobe abort Closes #719 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f876f90 commit 50f33d3

3 files changed

Lines changed: 32 additions & 0 deletions

File tree

packages/ffmpeg/src/classes.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ export class FFmpeg {
112112
"abort",
113113
() => {
114114
reject(new DOMException(`Message # ${id} was aborted`, "AbortError"));
115+
// If we are aborting an exec or ffprobe, send a CANCEL message so
116+
// the worker's ffmpeg timeout is set to 1 ms, causing the running
117+
// command to stop as quickly as possible. For other message types
118+
// the cancel is a no-op on the worker side.
119+
if (
120+
(type === FFMessageType.EXEC ||
121+
type === FFMessageType.FFPROBE) &&
122+
this.#worker
123+
) {
124+
this.#worker.postMessage({
125+
id: getMessageID(),
126+
type: FFMessageType.CANCEL,
127+
});
128+
}
115129
},
116130
{ once: true }
117131
);

packages/ffmpeg/src/const.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export enum FFMessageType {
1717
DELETE_DIR = "DELETE_DIR",
1818
ERROR = "ERROR",
1919

20+
CANCEL = "CANCEL",
2021
DOWNLOAD = "DOWNLOAD",
2122
PROGRESS = "PROGRESS",
2223
LOG = "LOG",

packages/ffmpeg/src/worker.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,19 @@ const ffprobe = ({ args, timeout = -1 }: FFMessageExecData): ExitCode => {
108108
return ret;
109109
};
110110

111+
/**
112+
* Interrupt any running exec/ffprobe by setting the ffmpeg timeout to 1 ms.
113+
* This causes the WASM-side watchdog to fire almost immediately, stopping
114+
* the current command and returning exit-code 1 (timeout). The exec()
115+
* call on the main thread has already been rejected with an AbortError by
116+
* the time this message arrives, so the exit-code reply is simply discarded.
117+
*/
118+
const cancel = (): void => {
119+
if (ffmpeg) {
120+
ffmpeg.setTimeout(1);
121+
}
122+
};
123+
111124
const writeFile = ({ path, data }: FFMessageWriteFileData): OK => {
112125
ffmpeg.FS.writeFile(path, data);
113126
return true;
@@ -181,6 +194,10 @@ self.onmessage = async ({
181194
case FFMessageType.FFPROBE:
182195
data = ffprobe(_data as FFMessageExecData);
183196
break;
197+
case FFMessageType.CANCEL:
198+
cancel();
199+
// CANCEL is fire-and-forget: no reply needed.
200+
return;
184201
case FFMessageType.WRITE_FILE:
185202
data = writeFile(_data as FFMessageWriteFileData);
186203
break;

0 commit comments

Comments
 (0)