UNPKG

transavormer

Version:

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

106 lines (90 loc) 3.99 kB
/* * Copyright (c) 2024 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 * as lawc from "libavjs-webcodecs-bridge"; import type * as wcp from "libavjs-webcodecs-polyfill"; import * as cmdsM from "./commands"; import * as ifs from "./interfaces"; declare let AudioDecoder: typeof wcp.AudioDecoder; /** * A stream of frames coming from the user. */ export class UserFrameStream implements ifs.FrameStream { constructor( la: LibAVT.LibAV, init: ifs.InitUserFrameStream ) { const streams: Promise<ifs.StreamParameters>[] = []; const streamRes: (((x:ifs.StreamParameters)=>void)|null)[] = []; for (const stream of init.streams) { streams.push(new Promise(res => streamRes.push(res))); } this.streams = Promise.all(streams); const rdr = init.input.getReader(); this.stream = new ReadableStream({ pull: async (controller) => { const rd = await rdr.read(); if (rd.done) { controller.close(); return; } for (const frame of rd.value!) { const res = streamRes[frame.streamIndex]; if (!res) continue; let spar: ifs.StreamParameters; let laf: LibAVT.Frame; if ((<wcp.VideoFrame> frame.frame).codedWidth) { laf = await lawc.videoFrameToLAFrame( <wcp.VideoFrame> frame.frame ); } else if ((<wcp.AudioData> frame.frame).numberOfFrames) { laf = await lawc.audioDataToLAFrame( <wcp.AudioData> frame.frame ); } else if (typeof frame.frame === "number") { laf = await la.ff_copyout_frame(frame.frame); } else { laf = <LibAVT.Frame> frame.frame; } spar = { codec_id: 0, codec_type: laf.width ? la.AVMEDIA_TYPE_VIDEO : la.AVMEDIA_TYPE_AUDIO, format: laf.format, width: laf.width, height: laf.height, sample_rate: laf.sample_rate, channel_layoutmask: laf.channel_layout, channels: laf.channels, time_base_num: laf.time_base_num || 1, time_base_den: laf.time_base_den || 1000000 }; Object.assign(spar, init.streams[frame.streamIndex]); res(spar); streamRes[frame.streamIndex] = null; } controller.enqueue(rd.value!); } }); } sendCommands(cmds: ifs.Command[]): Promise<ifs.CommandResult[]> { return Promise.resolve(cmdsM.addResults(cmds)); } component: ifs.Component = "frame-stream"; ptr: false = false; streams: Promise<ifs.StreamParameters[]>; streamType: "frame" = "frame"; stream: ReadableStream<ifs.StreamFrame[]>; }