Make conversion cancelable

This commit is contained in:
Vanilagy
2025-02-09 17:55:59 +01:00
parent 02ac371011
commit b3cd4de1f6
3 changed files with 68 additions and 1 deletions
+8 -1
View File
@@ -18,6 +18,12 @@
const target = new Metamuxer.BufferTarget(); const target = new Metamuxer.BufferTarget();
const outputFormat = new Metamuxer.Mp4OutputFormat() const outputFormat = new Metamuxer.Mp4OutputFormat()
const abortController = new AbortController();
const button = document.createElement('button');
button.textContent = 'Cancel';
button.onclick = () => abortController.abort();
document.body.append(button);
console.time() console.time()
const res = await Metamuxer.convert({ const res = await Metamuxer.convert({
input: new Metamuxer.Input({ input: new Metamuxer.Input({
@@ -61,7 +67,8 @@
}, },
onProgress: (event) => { onProgress: (event) => {
progress.value = event.completion; progress.value = event.completion;
} },
abortSignal: abortController.signal
}); });
console.timeEnd() console.timeEnd()
console.log(res) console.log(res)
+40
View File
@@ -65,6 +65,7 @@ export type ConversionOptions = {
}; };
onProgress?: (event: { completion: number }) => unknown; onProgress?: (event: { completion: number }) => unknown;
abortSignal?: AbortSignal;
}; };
/** @public */ /** @public */
@@ -227,6 +228,9 @@ class Conversion {
if (options.onProgress !== undefined && typeof options.onProgress !== 'function') { if (options.onProgress !== undefined && typeof options.onProgress !== 'function') {
throw new TypeError('options.onProgress, when provided, must be a function.'); throw new TypeError('options.onProgress, when provided, must be a function.');
} }
if (options.abortSignal !== undefined && !(options.abortSignal instanceof AbortSignal)) {
throw new TypeError('options.abortSignal, when provided, must be an AbortSignal.');
}
this.input = options.input; this.input = options.input;
this.output = options.output; this.output = options.output;
@@ -242,6 +246,12 @@ class Conversion {
utilizedTracks: [], utilizedTracks: [],
discardedTracks: [], discardedTracks: [],
}; };
options.abortSignal?.addEventListener('abort', () => {
if (!this.output.isFinalizing) {
void this.output.cancel();
}
});
} }
async execute() { async execute() {
@@ -370,6 +380,10 @@ class Conversion {
await this.synchronizer.wait(sample.timestamp); await this.synchronizer.wait(sample.timestamp);
} }
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(sample, meta); await source.digest(sample, meta);
this.reportProgress(track.id, sample.timestamp + sample.duration); this.reportProgress(track.id, sample.timestamp + sample.duration);
} }
@@ -452,6 +466,10 @@ class Conversion {
context.drawImage(canvas, 0, 0, canvas.width, canvas.height, dx, dy, newWidth, newHeight); context.drawImage(canvas, 0, 0, canvas.width, canvas.height, dx, dy, newWidth, newHeight);
} }
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(Math.max(timestamp - this.startTimestamp, 0), duration); await source.digest(Math.max(timestamp - this.startTimestamp, 0), duration);
} }
@@ -476,6 +494,10 @@ class Conversion {
timestamp: Math.max(timestamp - this.startTimestamp, 0), timestamp: Math.max(timestamp - this.startTimestamp, 0),
}); });
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(clone); await source.digest(clone);
clone.close(); clone.close();
} }
@@ -548,6 +570,10 @@ class Conversion {
await this.synchronizer.wait(sample.timestamp); await this.synchronizer.wait(sample.timestamp);
} }
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(sample, meta); await source.digest(sample, meta);
this.reportProgress(track.id, sample.timestamp + sample.duration); this.reportProgress(track.id, sample.timestamp + sample.duration);
} }
@@ -633,6 +659,10 @@ class Conversion {
await this.synchronizer.wait(timestamp); await this.synchronizer.wait(timestamp);
} }
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(data); await source.digest(data);
data.close(); data.close();
} }
@@ -716,6 +746,11 @@ class Conversion {
if (endTimestamp >= currentContextEndTime) { if (endTimestamp >= currentContextEndTime) {
// Render the audio // Render the audio
const renderedBuffer = await currentContext.startRendering(); const renderedBuffer = await currentContext.startRendering();
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(renderedBuffer); await source.digest(renderedBuffer);
currentContextStartFrame += currentContext.length; currentContextStartFrame += currentContext.length;
@@ -739,6 +774,11 @@ class Conversion {
if (currentContext) { if (currentContext) {
const renderedBuffer = await currentContext.startRendering(); const renderedBuffer = await currentContext.startRendering();
if (this.options.abortSignal?.aborted) {
return;
}
await source.digest(renderedBuffer); await source.digest(renderedBuffer);
} }
+20
View File
@@ -86,8 +86,26 @@ export class Output<
/** @internal */ /** @internal */
_finalizing = false; _finalizing = false;
/** @internal */ /** @internal */
_finalized = false;
/** @internal */
_mutex = new AsyncMutex(); _mutex = new AsyncMutex();
get isStarted() {
return this._started;
}
get isCanceled() {
return this._canceled;
}
get isFinalizing() {
return this._finalizing;
}
get isFinalized() {
return this._finalized;
}
constructor(options: OutputOptions<F, T>) { constructor(options: OutputOptions<F, T>) {
if (!options || typeof options !== 'object') { if (!options || typeof options !== 'object') {
throw new TypeError('options must be an object.'); throw new TypeError('options must be an object.');
@@ -334,6 +352,8 @@ export class Output<
await this._writer.flush(); await this._writer.flush();
await this._writer.finalize(); await this._writer.finalize();
this._finalized = true;
release(); release();
} }
} }