UNPKG

mfx

Version:

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

119 lines (109 loc) 3.13 kB
import { Muxer as MP4Muxer, StreamTarget as MP4StreamTarget } from "mp4-muxer"; import type { MFXEncodedChunk } from "../../types"; import { MFXTransformStream } from "../../stream"; import { MFXBlob } from "../../blob"; import type { ContainerEncoderConfig } from "../encoderConfig"; /** * @group Encode */ export class MP4ContainerEncoder extends MFXTransformStream< MFXEncodedChunk, MFXBlob > { get identifier() { return "MP4ContainerEncoder"; } ready: Promise<any>; constructor(config: ContainerEncoderConfig) { const videoCodecMapper = ( codec: ContainerEncoderConfig["video"]["codec"], ): "avc" | "hevc" | "vp9" | "av1" => { const targets: ("avc" | "hevc" | "vp9" | "av1")[] = [ "avc", "hevc", "vp9", "av1", ]; if (codec.startsWith("hvc")) { return "hevc"; } for (let i = 0; i < targets.length; i++) { if (codec.startsWith(targets[i])) { return targets[i]; } } throw new Error(`Unsupported MP4 video codec ${codec}`); }; const audioCodecMapper = ( codec: ContainerEncoderConfig["audio"]["codec"], ) => { const targets: ("opus" | "aac")[] = ["opus", "aac"]; for (let i = 0; i < targets.length; i++) { if (codec.startsWith(targets[i])) { return targets[i]; } } throw new Error(`Unsupported MP4 audio codec ${codec}`); }; let muxer: MP4Muxer<MP4StreamTarget>; 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 MP4Muxer({ ...(config.video ? { video: { frameRate: config.video.framerate, height: config.video.height, width: config.video.width, codec: videoCodecMapper(config.video.codec), }, } : {}), ...(config.audio ? { audio: { codec: audioCodecMapper(config.audio.codec), numberOfChannels: config.audio.numberOfChannels, sampleRate: config.audio.sampleRate, }, } : {}), firstTimestampBehavior: "offset", fastStart: config.streaming ? "fragmented" : config?.faststart ? "in-memory" : false, target: new MP4StreamTarget({ onData: (data, position) => { this.queue( new MFXBlob([data], { type: "video/mp4", position, config, }), ); }, ...(Number.isInteger(config.chunkSize) ? { chunked: true, chunkSize: config.chunkSize, } : {}), }), }); } }