@omnimedia/omnitool
Version:
open source video processing tools
97 lines • 3.63 kB
JavaScript
import { Comrade, tune } from "@e280/comrade";
import { ALL_FORMATS, Input } from "mediabunny";
import { Machina } from "./parts/machina.js";
import { setupDriverHost } from "./fns/host.js";
import { loadDecoderSource } from "./utils/load-decoder-source.js";
export class Driver {
machina;
thread;
static async setup(options) {
const machina = new Machina();
const thread = await Comrade.thread({
label: "OmnitoolDriver",
workerUrl: options.workerUrl,
setupHost: setupDriverHost(machina),
});
return new this(machina, thread);
}
constructor(machina, thread) {
this.machina = machina;
this.thread = thread;
}
async hello() {
return this.thread.work.hello();
}
async getAudioDuration(source) {
const input = new Input({
source: await loadDecoderSource(source),
formats: ALL_FORMATS
});
const audioTrack = await input.getPrimaryAudioTrack();
return await audioTrack?.computeDuration();
}
async getVideoDuration(source) {
const input = new Input({
source: await loadDecoderSource(source),
formats: ALL_FORMATS
});
const videoTrack = await input.getPrimaryVideoTrack();
return await videoTrack?.computeDuration();
}
decode(input) {
let lastFrame = null;
const videoTransform = new TransformStream({
async transform(chunk, controller) {
const frame = await input.onFrame?.(chunk) ?? chunk;
// below code is to prevent mem leaks and hardware accelerated decoder stall
lastFrame?.close();
controller.enqueue(frame);
lastFrame = frame;
}
});
const audioTransform = new TransformStream();
this.thread.work.decode[tune]({ transfer: [videoTransform.writable, audioTransform.writable] })({
source: input.source,
video: videoTransform.writable,
audio: audioTransform.writable,
});
return {
audio: audioTransform.readable,
video: videoTransform.readable
};
}
async encode({ readables, config }) {
const handle = await window.showSaveFilePicker();
const writable = await handle.createWritable();
// making bridge because file picker writable is not transferable
const bridge = new WritableStream({
async write(chunk) {
await writable.write(chunk);
},
async close() {
await writable.close();
}
});
return await this.thread.work.encode[tune]({ transfer: [readables.audio, readables.video, bridge] })({ readables, config, bridge });
}
async composite(composition) {
const transfer = this.#collectTransferablesFromComposition(composition);
return await this.thread.work.composite[tune]({ transfer })(composition);
}
#collectTransferablesFromComposition(composition) {
const transferables = [];
const visit = (node) => {
if (Array.isArray(node)) {
for (const child of node)
visit(child);
}
else if (node && typeof node === 'object' && 'kind' in node) {
if (node.kind === 'image' && node.frame instanceof VideoFrame)
transferables.push(node.frame);
}
};
visit(composition);
return transferables;
}
}
//# sourceMappingURL=driver.js.map