web/libav: create base libav wrapper class

This commit is contained in:
dumbmoron 2024-08-23 14:20:11 +00:00
parent 291a41453e
commit 9e382ee969
No known key found for this signature in database
3 changed files with 75 additions and 74 deletions

View file

@ -4,53 +4,31 @@ import * as LibAVWebCodecs from "libavjs-webcodecs-bridge";
import { BufferStream } from "./buffer-stream"; import { BufferStream } from "./buffer-stream";
import { BufferStream } from "../buffer-stream"; import { BufferStream } from "../buffer-stream";
import WebCodecsWrapper from "./webcodecs"; import WebCodecsWrapper from "./webcodecs";
import { browser } from "$app/environment"; import LibAVWrapper from "./instance";
const QUEUE_THRESHOLD_MIN = 16; const QUEUE_THRESHOLD_MIN = 16;
const QUEUE_THRESHOLD_MAX = 128; const QUEUE_THRESHOLD_MAX = 128;
export default class LibAVWrapper { export default class EncodeLibAV extends LibAVWrapper {
libav: Promise<LibAVInstance> | null; webcodecs: WebCodecsWrapper | null = null;
webcodecs: WebCodecsWrapper | null;
concurrency: number;
onProgress?: FFmpegProgressCallback;
constructor(onProgress?: FFmpegProgressCallback) { constructor() {
this.libav = null; super(LibAV);
this.webcodecs = null;
this.concurrency = Math.min(4, browser ? navigator.hardwareConcurrency : 0);
this.onProgress = onProgress;
} }
init() { async init() {
if (this.concurrency && !this.libav) { await super.init();
this.libav = LibAV.LibAV({ if (!this.webcodecs) {
yesthreads: true, this.webcodecs = new WebCodecsWrapper(
base: '/_libav' super.get().then(({ libav }) => libav)
}); );
this.webcodecs = new WebCodecsWrapper(this.libav);
}
}
async terminate() {
if (this.libav) {
const libav = await this.libav;
libav.terminate();
} }
} }
async #get() { async #get() {
if (!this.libav) throw new Error("LibAV wasn't initialized");
const libav = await this.libav;
if (!this.webcodecs) {
throw new Error("unreachable");
}
return { return {
libav, ...await super.get(),
webcodecs: this.webcodecs webcodecs: this.webcodecs!
}; };
} }

View file

@ -0,0 +1,56 @@
import mime from "mime";
import LibAV, { type LibAV as LibAVInstance } from "@imput/libav.js-remux-cli";
import { browser } from "$app/environment";
export default class LibAVWrapper {
#libav__constructor: typeof LibAV;
#libav: Promise<LibAVInstance> | null;
#useThreads: boolean;
concurrency: number;
constructor(ctor: typeof LibAV, threads = true) {
this.#libav = null;
this.#useThreads = threads;
this.#libav__constructor = ctor;
this.concurrency = Math.min(4, browser ? navigator.hardwareConcurrency : 0);
}
async init() {
if (!this.#libav) {
this.#libav = this.#libav__constructor.LibAV({
yesthreads: this.#useThreads,
base: '/_libav'
});
}
}
async terminate() {
if (this.#libav) {
const libav = await this.#libav;
libav.terminate();
}
}
protected async get() {
if (!this.#libav) throw new Error("LibAV wasn't initialized");
return {
libav: await this.#libav
};
}
static getExtensionFromType(blob: Blob) {
const extensions = mime.getAllExtensions(blob.type);
const overrides = ['mp3', 'mov'];
if (!extensions)
return;
for (const override of overrides)
if (extensions?.has(override))
return override;
return [...extensions][0];
}
}

View file

@ -1,38 +1,19 @@
import mime from "mime"; import mime from "mime";
import LibAV, { type LibAV as LibAVInstance } from "@imput/libav.js-remux-cli"; import LibAV from "@imput/libav.js-remux-cli";
import type { FFmpegProgressCallback, FFmpegProgressEvent, FFmpegProgressStatus, FileInfo, RenderParams } from "../types/libav"; import type { FFmpegProgressCallback, FFmpegProgressEvent, FFmpegProgressStatus, FileInfo, RenderParams } from "../types/libav";
import type { FfprobeData } from "fluent-ffmpeg"; import type { FfprobeData } from "fluent-ffmpeg";
import LibAVWrapper from "./instance";
export default class LibAVWrapper { export default class RemuxLibAV extends LibAVWrapper {
libav: Promise<LibAVInstance> | null;
concurrency: number;
onProgress?: FFmpegProgressCallback; onProgress?: FFmpegProgressCallback;
constructor(onProgress?: FFmpegProgressCallback) { constructor(onProgress?: FFmpegProgressCallback) {
this.libav = null; super(LibAV);
this.concurrency = Math.min(4, navigator.hardwareConcurrency);
this.onProgress = onProgress; this.onProgress = onProgress;
} }
async init() {
if (!this.libav) {
this.libav = LibAV.LibAV({
yesthreads: true,
base: '/_libav'
});
}
}
async #get() {
if (!this.libav) throw new Error("LibAV wasn't initialized");
return {
libav: await this.libav
};
}
async probe(blob: Blob) { async probe(blob: Blob) {
const { libav } = await this.#get(); const { libav } = await this.get();
const OUT_FILE = 'output.json'; const OUT_FILE = 'output.json';
await libav.mkreadaheadfile('input', blob); await libav.mkreadaheadfile('input', blob);
@ -69,22 +50,8 @@ export default class LibAVWrapper {
return JSON.parse(text) as FfprobeData; return JSON.parse(text) as FfprobeData;
} }
static getExtensionFromType(blob: Blob) {
const extensions = mime.getAllExtensions(blob.type);
const overrides = ['mp3', 'mov'];
if (!extensions)
return;
for (const override of overrides)
if (extensions?.has(override))
return override;
return [...extensions][0];
}
async remux({ blob, output, args }: RenderParams) { async remux({ blob, output, args }: RenderParams) {
const { libav } = await this.#get(); const { libav } = await this.get();
const inputKind = blob.type.split("/")[0]; const inputKind = blob.type.split("/")[0];
const inputExtension = LibAVWrapper.getExtensionFromType(blob); const inputExtension = LibAVWrapper.getExtensionFromType(blob);