web/libav: create base libav wrapper class
This commit is contained in:
parent
291a41453e
commit
9e382ee969
3 changed files with 75 additions and 74 deletions
|
@ -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!
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
56
web/src/lib/libav/instance.ts
Normal file
56
web/src/lib/libav/instance.ts
Normal 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue