UNPKG

transavormer

Version:

Transform (transcode, transmux, etc) audio/video formats using libav.js and WebCodecs

387 lines (335 loc) 11.7 kB
/* * Copyright (c) 2024, 2025 Yahweasel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ import type * as LibAVT from "@libav.js/types"; import type * as wcp from "libavjs-webcodecs-polyfill"; import * as demuxer from "./demuxer"; import * as packetSel from "./packet-selector"; import * as decoder from "./decoder"; import * as frameSel from "./frame-selector"; import * as norm from "./normalizer"; import * as pNorm from "./play-normalizer"; import * as filter from "./filter"; import * as encoder from "./encoder"; import * as muxer from "./muxer"; import * as frameStream from "./frame-stream"; import * as cmdsM from "./commands"; import * as ifs from "./interfaces"; export function build(libav: LibAVT.LibAV, init: ifs.InitDemuxer): Promise<ifs.PacketStream>; export function build(libav: LibAVT.LibAV, init: ifs.InitDemuxerPtr): Promise<ifs.PacketStreamPtr>; export function build(libav: LibAVT.LibAV, init: ifs.InitPacketSelector): Promise<ifs.PacketStreamAny>; export function build(libav: LibAVT.LibAV, init: ifs.InitDecoder): Promise<ifs.FrameStream>; export function build(libav: LibAVT.LibAV, init: ifs.InitDecoderPtr): Promise<ifs.FrameStreamPtr>; export function build(libav: LibAVT.LibAV, init: ifs.InitFrameSelector): Promise<ifs.FrameStreamAny>; export function build(libav: LibAVT.LibAV, init: ifs.InitFrameNormalizer): Promise<ifs.LibAVFrameStream>; export function build(libav: LibAVT.LibAV, init: ifs.InitFrameNormalizerPtr): Promise<ifs.LibAVFrameStreamPtr>; export function build(libav: LibAVT.LibAV, init: ifs.InitPlaybackNormalizer): Promise<ifs.FrameStream>; export function build(libav: LibAVT.LibAV, init: ifs.InitEncoder): Promise<ifs.PacketStream>; export function build(libav: LibAVT.LibAV, init: ifs.InitEncoderPtr): Promise<ifs.PacketStreamPtr>; export function build(libav: LibAVT.LibAV, init: ifs.InitMuxer): Promise<ifs.FileStream>; export function build(libav: LibAVT.LibAV, init: ifs.InitUserPacketStream): Promise<ifs.PacketStreamAny>; export function build(libav: LibAVT.LibAV, init: ifs.InitUserFrameStream): Promise<ifs.FrameStreamAny>; export function build(libav: LibAVT.LibAV, init: ifs.InitUserMonoFrameStream): Promise<ifs.FrameStreamAny>; /** * Create a transavormer for the requested task. * @param init Task description (initializer). */ export function build(libav: LibAVT.LibAV, init: any): Promise<any> { switch (init.type) { case "demuxer": return buildDemuxer(libav, init, !!init.ptr); case "packet-selector": return buildPacketSelector(libav, init); case "decoder": return buildDecoder(libav, init, !!init.ptr); case "frame-selector": return buildFrameSelector(libav, init); case "frame-normalizer": return buildNormalizer(libav, init, !!init.ptr); case "play-normalizer": return buildPlayNormalizer(libav, init); case "la-filter": return buildLAFilter(libav, init, !!init.ptr); case "encoder": return buildEncoder(libav, init, !!init.ptr); case "muxer": return buildMuxer(libav, init); case "packet-stream": return buildUserPacketStream(init); case "frame-stream": return buildUserFrameStream(libav, init); case "mono-frame-stream": return buildUserMonoFrameStream(libav, init); } throw new Error(`Unrecognized initializer type ${(<any> init).type}`); } function buildDemuxer( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.PacketStream> { if (init.then) return init; if (init.streamType === "packet") return Promise.resolve(init); if (init.type !== "demuxer") { return buildDemuxer(libav, <ifs.InitDemuxer> { type: "demuxer", input: init }, ptr); } if (init.libav) { libav = init.libav; init.ptr = false; } else { init.ptr = ptr; } return demuxer.Demuxer.build(libav, init); } function buildPacketSelector( libav: LibAVT.LibAV, init: any ): Promise<ifs.PacketStreamAny> { if (init.then) return init; if (init.streamType === "packet") return Promise.resolve(init); return packetSel.PacketSelector.build( libav, init, buildPacketStream(libav, init.input, true) ); } async function buildDecoder( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.FrameStream> { if (init.then) return init; if ( init.streamType === "frame" || init.streamType === "libav-frame" || init.streamType === "webcodecs-frame" ) return Promise.resolve(init); if (init.type !== "decoder") { return buildDecoder(libav, <ifs.InitDecoder> { type: "decoder", input: init }, ptr); } let packetStreamP: Promise<ifs.PacketStream> = buildPacketStream( libav, init.input, true ); init.ptr = ptr; if (init.libav) { libav = init.libav; init.ptr = false; } if (init.LibAV) init.ptr = false; return decoder.Decoder.build( libav, init, packetStreamP ); } function buildFrameSelector( libav: LibAVT.LibAV, init: any ): Promise<ifs.FrameStreamAny> { if (init.then) return init; if (init.streamType === "packet") return Promise.resolve(init); return frameSel.FrameSelector.build( libav, init, buildFrameStream(libav, init.input, true) ); } function buildNormalizer( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.LibAVFrameStream> { if (init.then) return init; if (init.streamType === "libav-frame") return Promise.resolve(init); if (init.type !== "frame-normalizer") { return buildNormalizer(libav, <ifs.InitFrameNormalizer> { type: "frame-normalizer", input: init }, ptr); } init.ptr = ptr; return norm.FrameNormalizer.build( libav, init, buildDecoder(libav, init.input, true) ); } function buildPlayNormalizer( libav: LibAVT.LibAV, init: any ): Promise<ifs.FrameStream> { if (init.then) return init; if (init.streamType === "frame") return Promise.resolve(init); return pNorm.PlaybackNormalizer.build( libav, init, buildDecoder(libav, init.input, true) ); } function buildLAFilter( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.LibAVFrameStream> { if (init.then) return init; if (init.type !== "la-filter") { return buildLAFilter(libav, <ifs.InitLAFilter> { type: "la-filter", input: init }, ptr); } init.ptr = ptr; return filter.LAFilter.build( libav, init, buildFrameStream(libav, init.input, true) ); } function buildFrameStream( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.FrameStream> { if (init.then) return init; if ( init.streamType === "frame" || init.streamType === "libav-frame" || init.streamType === "webcodecs-frame" ) return Promise.resolve(init); if (init.type === "la-filter") { return buildLAFilter(libav, init, ptr || !!init.ptr); } else if (init.type === "frame-normalizer") { return buildNormalizer(libav, init, ptr || !!init.ptr); } else if (init.type === "frame-stream") { return <any> buildUserFrameStream(libav, init); } else if (init.type === "mono-frame-stream") { return <any> buildUserMonoFrameStream(libav, init); } else { return buildDecoder(libav, init, ptr || !!init.ptr); } } function buildEncoder( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.PacketStream> { if (init.then) return init; if (init.streamType === "packet") return Promise.resolve(init); if (init.type !== "encoder") { return buildEncoder(libav, <ifs.InitEncoder> { type: "encoder", videoConfig: <wcp.VideoEncoderConfig> { codec: "vp09.00.10.08.03.1.1.1.0", width: 0, height: 0 }, audioConfig: <wcp.AudioEncoderConfig> { codec: "opus" }, input: init }, ptr); } init.ptr = ptr; return encoder.Encoder.build( libav, init, buildFrameStream(libav, init.input, true) ); } function buildPacketStream( libav: LibAVT.LibAV, init: any, ptr: boolean ): Promise<ifs.PacketStream> { if (init.then) return init; if (init.streamType === "packet") return Promise.resolve(init); if (init.type === "filter" || init.type === "frame-normalizer" || init.type === "encoder") { return buildEncoder(libav, init, ptr || !!init.ptr); } else if (init.type === "packet-stream") { return <any> buildUserPacketStream(init); } else { return buildDemuxer(libav, init, ptr || !!init.ptr); } } function buildMuxer( libav: LibAVT.LibAV, init: any ): Promise<ifs.FileStream> { if (init.then) return init; if (init.streamType === "file") return Promise.resolve(init); if (init.type !== "muxer") { init.ptr = true; return buildMuxer(libav, <ifs.InitMuxer> { type: "muxer", format: "matroska", input: init }); } return muxer.Muxer.build( libav, init, buildPacketStream(libav, init.input, true) ); } export function buildUserPacketStream( init: ifs.InitUserPacketStream ): Promise<ifs.PacketStreamAny> { return Promise.resolve({ component: "packet-stream", ptr: false, streams: Promise.resolve(init.streams), streamType: "packet", stream: <any> init.input, sendCommands(cmds) { return Promise.resolve(cmdsM.addResults(cmds)); } }); } export function buildUserFrameStream( libav: LibAVT.LibAV, init: ifs.InitUserFrameStream ): Promise<ifs.FrameStreamAny> { return Promise.resolve(new frameStream.UserFrameStream( libav, init )); } export function buildUserMonoFrameStream( libav: LibAVT.LibAV, init: ifs.InitUserMonoFrameStream ): Promise<ifs.FrameStreamAny> { const rdr = init.input.getReader(); const rs = new ReadableStream<ifs.StreamFrame[]>({ pull: async (controller) => { const rd = await rdr.read(); if (rd.done) { controller.close(); } else { controller.enqueue(rd.value!.map(x => ({ streamIndex: 0, frame: <any> x }))); } } }); return buildUserFrameStream(libav, { type: "frame-stream", streams: [init.stream], input: rs }); }