UNPKG

mfx

Version:

In-browser video editing toolkit, with effects accelerated by WebGL

115 lines (104 loc) 3.1 kB
import type { MFXEncodedChunk } from "../../types"; import { MFXTransformStream } from "../../stream"; import { MFXBlob } from "../../blob"; import { Muxer as WebMMuxer, StreamTarget as WebMStreamTarget, } from "webm-muxer"; import type { ContainerEncoderConfig } from "../encoderConfig"; /** * @group Encode */ export class WebMContainerEncoder extends MFXTransformStream< MFXEncodedChunk, MFXBlob > { get identifier() { return "WebMContainerEncoder"; } ready: Promise<any>; constructor(config: ContainerEncoderConfig) { const videoCodecMapper = ( codec: ContainerEncoderConfig["video"]["codec"], ) => { if (codec.startsWith("vp08") || codec === "vp8") { return "V_VP8"; } if (codec.startsWith("vp09") || codec === "vp9") { return "V_VP9"; } // TODO: Can we support MPEG encoding? (https://www.matroska.org/technical/codec_specs.html#:~:text=Initialization%3A%20none-,V_MPEG2,-Codec%20ID%3A%20V_MPEG2) throw new Error(`Unsupported webM codec ${codec}`); }; const audioCodecMapper = ( codec: ContainerEncoderConfig["audio"]["codec"], ) => { if (codec === "opus") { return "A_OPUS"; } if (codec === "vorbis") { return "A_VORBIS"; } throw new Error(`Unsupported webM audio codec ${codec}`); }; let muxer: WebMMuxer<WebMStreamTarget>; super({ transform: async (chunk) => { // TODO: support raw chunks such as addAudioChunkRaw // to allow passthrough tracks if (chunk.video) { muxer.addVideoChunk(chunk.video.chunk, chunk.video.metadata); } if (chunk.audio) { muxer.addAudioChunk(chunk.audio.chunk, chunk.audio.metadata); } }, flush: async () => { muxer.finalize(); }, }); muxer = new WebMMuxer({ ...(config.video ? { video: { height: config.video.height, width: config.video.width, frameRate: config.video.framerate, alpha: config.video.alpha !== "discard" || Boolean(config.video.alpha), codec: videoCodecMapper(config.video.codec), }, } : {}), ...(config.audio ? { audio: { codec: audioCodecMapper(config.audio.codec), // TODO: Perform mapping numberOfChannels: config.audio.numberOfChannels, sampleRate: config.audio.sampleRate, }, } : {}), firstTimestampBehavior: "offset", type: "matroska", streaming: config.streaming, target: new WebMStreamTarget({ onData: (data, position) => { this.queue( new MFXBlob([data], { type: "video/webm", position, config, }), ); }, ...(Number.isInteger(config.chunkSize) ? { chunked: true, chunkSize: config.chunkSize, } : {}), }), }); } }