### Install @mmomtchev/ffmpeg Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Install the package using npm. CommonJS and ES Modules/TypeScript import examples are provided. ```sh npm install @mmomtchev/ffmpeg ``` ```ts // CommonJS const ffmpeg = require('@mmomtchev/ffmpeg'); const { Demuxer, Muxer, VideoDecoder, VideoEncoder, AudioDecoder, AudioEncoder, VideoTransform, AudioTransform, Filter, Discarder, MediaTransform } = require('@mmomtchev/ffmpeg/stream'); // ES Modules / TypeScript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, Muxer, VideoDecoder, VideoEncoder, AudioDecoder, AudioEncoder, VideoTransform, AudioTransform, Filter, Discarder, MediaTransform, VideoStreamDefinition, AudioStreamDefinition } from '@mmomtchev/ffmpeg/stream'; ``` -------------------------------- ### Transcode Video Example Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md A low-level C/C++ style example demonstrating video stream transcoding without using the streams API. This is useful for direct manipulation of video streams. ```javascript import ffmpeg from '@mmomtchev/ffmpeg'; async function transcodeVideo(inputFile, outputFile) { const ffmpegProcess = new ffmpeg.FFmpeg(inputFile); await ffmpegProcess.transcode(outputFile); } transcodeVideo('input.mp4', 'output.mp4'); ``` -------------------------------- ### Install node-ffmpeg Package Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md Use npm to install the node-ffmpeg package. This command also triggers the build process for ffmpeg binaries. ```shell npm i @mmomtchev/ffmpeg ``` -------------------------------- ### Transcode Audio Example Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md A low-level C/C++ style example for transcoding an audio stream obtained from a container file without using the streams API. This is suitable for direct audio stream processing. ```javascript import ffmpeg from '@mmomtchev/ffmpeg'; async function transcodeAudio(inputFile, outputFile) { const ffmpegProcess = new ffmpeg.FFmpeg(inputFile); await ffmpegProcess.transcode(outputFile); } transcodeAudio('input.mp3', 'output.wav'); ``` -------------------------------- ### Full Pipeline Audio/Video Transcode (TypeScript) Source: https://context7.com/mmomtchev/ffmpeg/llms.txt This example demonstrates a complete audio and video transcoding pipeline using the FFmpeg Streams API. It handles decoding, re-encoding, and multiplexing into a new MP4 file with comprehensive error handling. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, Muxer, VideoDecoder, VideoEncoder, AudioDecoder, AudioEncoder } from '@mmomtchev/ffmpeg/stream'; ffmpeg.setLogLevel(ffmpeg.AV_LOG_ERROR); const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('error', (err) => { console.error('Input error:', err); process.exit(1); }); demuxer.on('ready', () => { const videoDecoder = new VideoDecoder(demuxer.video[0]); const audioDecoder = new AudioDecoder(demuxer.audio[0]); const videoDef = videoDecoder.definition(); const audioDef = audioDecoder.definition(); const videoEncoder = new VideoEncoder({ type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 2.5e6, width: videoDef.width, height: videoDef.height, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: videoDef.pixelFormat, codecOptions: { preset: 'medium' } }); const audioEncoder = new AudioEncoder({ type: 'Audio', codec: ffmpeg.AV_CODEC_AAC, bitRate: 128e3, sampleRate: audioDef.sampleRate, sampleFormat: audioDef.sampleFormat, channelLayout: audioDef.channelLayout }); const muxer = new Muxer({ outputFile: 'output.mp4', streams: [videoEncoder, audioEncoder] }); muxer.on('finish', () => console.log('Transcoding complete: output.mp4')); // Propagate errors demuxer.video[0].on('error', console.error); demuxer.audio[0].on('error', console.error); videoDecoder.on('error', console.error); audioDecoder.on('error', console.error); videoEncoder.on('error', console.error); audioEncoder.on('error', console.error); muxer.on('error', console.error); // Wire up the pipeline demuxer.video[0].pipe(videoDecoder).pipe(videoEncoder).pipe(muxer.video[0]); demuxer.audio[0].pipe(audioDecoder).pipe(audioEncoder).pipe(muxer.audio[0]); }); ``` -------------------------------- ### Encode Raw Video Frames to Compressed Packets with VideoEncoder Source: https://context7.com/mmomtchev/ffmpeg/llms.txt VideoEncoder converts raw VideoFrame objects into compressed Packet objects suitable for muxing. Output parameters, including codec, bitrate, and resolution, must be explicitly defined in a VideoStreamDefinition. This example shows piping from a Demuxer through a VideoDecoder to the VideoEncoder. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, VideoDecoder, VideoEncoder, VideoStreamDefinition } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const videoDecoder = new VideoDecoder(demuxer.video[0]); const inputDef = videoDecoder.definition(); const outputDef: VideoStreamDefinition = { type: 'Video', codec: ffmpeg.AV_CODEC_H264, // use ffmpeg.AV_CODEC_* constants bitRate: 2.5e6, // 2.5 Mbps width: inputDef.width, height: inputDef.height, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: inputDef.pixelFormat, codecOptions: { preset: 'veryfast' } // pass-through to ffmpeg codec options }; const videoEncoder = new VideoEncoder(outputDef); videoEncoder.on('error', (err) => console.error('VideoEncoder error:', err)); // Demuxer -> Decoder -> Encoder (raw frames in, compressed packets out) demuxer.video[0].pipe(videoDecoder).pipe(videoEncoder); // pipe videoEncoder to a Muxer }); // VideoStreamDefinition fields: // type: 'Video' // codec: ffmpeg.AVCodecID | ffmpeg.Codec // bitRate: number // width: number // height: number // frameRate: ffmpeg.Rational // pixelFormat: ffmpeg.PixelFormat // timeBase?: ffmpeg.Rational // codecOptions?: Record // e.g. { preset: 'veryfast', crf: '23' } // flags?: number ``` -------------------------------- ### Synchronous Low-Level Video Transcode (JavaScript) Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use this snippet for direct, synchronous video transcoding without the Streams API. It requires manual management of context, streams, and packet processing. Ensure input and output file paths are correct. ```javascript const ffmpeg = require('@mmomtchev/ffmpeg'); const { FormatContext, findEncodingCodecFormat, VideoDecoderContext, VideoEncoderContext, OutputFormat, Codec } = ffmpeg; ffmpeg.setLogLevel(ffmpeg.AV_LOG_ERROR); // --- Open input --- const ictx = new FormatContext; ictx.openInput('input.mp4'); ictx.findStreamInfo(); let videoStream = null; let vst = null; for (let i = 0; i < ictx.streamsCount(); i++) { const st = ictx.stream(i); if (st.mediaType() == ffmpeg.AV_MEDIA_TYPE_VIDEO) { videoStream = i; vst = st; break; } } const vdec = new VideoDecoderContext(vst); vdec.setRefCountedFrames(true); vdec.openCodec(new Codec); // --- Open output --- const ofrmt = new OutputFormat; ofrmt.setFormat('', 'output.mp4', ''); const octx = new FormatContext; octx.setOutputFormat(ofrmt); const ocodec = findEncodingCodecFormat(ofrmt, true); const encoder = new VideoEncoderContext(ocodec); encoder.setWidth(vdec.width()); encoder.setHeight(vdec.height()); encoder.setPixelFormat(vdec.pixelFormat()); encoder.setTimeBase(vst.timeBase()); encoder.setBitRate(vdec.bitRate()); encoder.openCodec(new Codec); const ost = octx.addVideoStream(encoder); ost.setFrameRate(vst.frameRate()); octx.openOutput('output.mp4'); octx.writeHeader(); octx.flush(); // --- Process packets --- while (true) { const pkt = ictx.readPacket(); const flush = pkt.isNull(); if (!flush && pkt.streamIndex() != videoStream) continue; do { const frame = vdec.decode(pkt, true); if (!frame.isComplete() && !flush) break; if (frame.isComplete()) { frame.setTimeBase(encoder.timeBase()); frame.setStreamIndex(0); frame.setPictureType(ffmpeg.AV_PICTURE_TYPE_NONE); } const opkt = frame.isComplete() ? encoder.encode(frame) : encoder.finalize(); opkt.setStreamIndex(0); octx.writePacket(opkt); if (flush && !frame.isComplete()) break; } while (flush); if (flush) break; } octx.writeTrailer(); // Output: output.mp4 transcoded using the format's default video codec ``` -------------------------------- ### Import CommonJS Module Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md Import the main ffmpeg module and the Demuxer class using CommonJS syntax. This is suitable for Node.js environments. ```javascript const ffmpeg = require('@mmomtchev/ffmpeg'); const { Demuxer } = require('@mmomtchev/ffmpeg/stream'); ``` -------------------------------- ### Video Transcoding with FFmpeg Streams API Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md Use this snippet for generalized video transcoding. It requires importing necessary components from '@mmomtchev/ffmpeg/stream'. Ensure error handlers are in place to prevent process termination on error. ```typescript import { Muxer, Demuxer, VideoDecoder, VideoEncoder, Discarder, VideoTransform, VideoStreamDefinition } from '@mmomtchev/ffmpeg/stream'; // Create a Demuxer - a Demuxer is an object that exports // multiple Readable, // it decodes the input container format and emits compressed data const input = new Demuxer({ inputFile:'launch.mp4' }); // Wait for the Demuxer to read the file headers and to // identify the various streams input.on('ready', () => { // Once the input Demuxer is ready, // it will contain two arrays of Readable: // input.video[] // input.audio[] // We will be discarding the audio stream // (unless you discard these will keep piling in memory // until you destroy the demuxer object) const audioDiscard = new Discarder(); // A VideoDecoder is a Transform that reads compressed video data // and sends raw video frames (this is the decoding codec) const videoInput = new VideoDecoder(input.video[0]); // A VideoDefinition is an object with all the properties of the stream const videoInputDefinition = videoInput.definition(); // such as codec, bitrate, framerate, frame size, pixel format const videoOutputDefinition = { type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 2.5e6, width: 320, height: 200, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: new ffmpeg.PixelFormat(ffmpeg.AV_PIX_FMT_YUV422P) } as VideoStreamDefinition; // A video encoder is a Transform that reads raw video frames // and sends compressed video data (this is the encoding codec) const videoOutput = new VideoEncoder(videoOutputDefinition); // A VideoTransform is a Transform that reads raw video frames // and sends raw video frames - with different frame size or pixel format const videoRescaler = new VideoTransform({ input: videoInputDefinition, output: videoOutputDefinition, interpolation: ffmpeg.SWS_BILINEAR }); // A Muxer is an object that contains multiple Writable // It multiplexes those streams, handling interleaving by buffering, // and writes the to the output format const output = new Muxer({ outputFile: 'video.mkv', outputFormat: 'mkv', streams: [videoOutput] }); // The transcoding operation is completely asynchronous, it is finished // when all output streams are finished output.on('finish', () => { console.log('we are done!'); }); // These are the error handlers (w/o them the process will stop on error) input.video[0].on('error', (err) => console.error(err)); input.audio[0].on('error', (err) => console.error(err)); output.video[0].on('error', (err) => console.error(err)); // This launches the transcoding // Demuxer -> Decoder -> Rescaler -> Encoder -> Muxer input.video[0].pipe(videoInput).pipe(videoRescaler) .pipe(videoOutput).pipe(output.video[0]); input.audio[0].pipe(audioDiscard); }); ``` -------------------------------- ### Read and demux media container with Demuxer Source: https://context7.com/mmomtchev/ffmpeg/llms.txt The Demuxer reads a media container and exposes elementary streams as Readable Packet streams. It emits 'ready' when stream info is parsed. Can read from a file or a Node.js ReadStream. ```ts import * as fs from 'node:fs'; import { Demuxer, Discarder, AudioDecoder, VideoDecoder } from '@mmomtchev/ffmpeg/stream'; // --- Read from filesystem (fastest path) --- const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('error', (err) => console.error('Demuxer error:', err)); demuxer.on('ready', () => { console.log(`Streams: ${demuxer.streams.length} total, ` + `${demuxer.video.length} video, ${demuxer.audio.length} audio`); const videoDecoder = new VideoDecoder(demuxer.video[0]); const audioDecoder = new AudioDecoder(demuxer.audio[0]); // ...pipe further }); // --- Read from a Node.js ReadStream --- const streamingDemuxer = new Demuxer(); // no inputFile → exposes .input Writable streamingDemuxer.on('ready', () => { const audioDecoder = new AudioDecoder(streamingDemuxer.audio[0]); // ... }); fs.createReadStream('input.mkv').pipe(streamingDemuxer.input!); // DemuxerOptions interface: // { // inputFile?: string; // path to file; omit to use streaming input // highWaterMark?: number; // buffer size for streaming input, default 64KB // openOptions?: Record; // ffmpeg open options (e.g. RTMP headers) // } ``` -------------------------------- ### Apply ffmpeg filter graph to video streams Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use the `Filter` class to apply complex filter chains to raw decoded streams. Define named inputs and outputs, and provide a standard ffmpeg filter graph string. Ensure all input stream definitions match the filter graph's expectations. ```typescript import * as fs from 'node:fs'; import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, VideoDecoder, VideoEncoder, Muxer, Filter, Discarder, VideoStreamDefinition } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const audioDiscard = new Discarder(); const videoDecoder = new VideoDecoder(demuxer.video[0]); const videoDef = videoDecoder.definition(); const videoEncoder = new VideoEncoder({ type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 2.5e6, width: videoDef.width, height: videoDef.height, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: videoDef.pixelFormat, codecOptions: { preset: 'veryfast' } }); // Create an ffmpeg filter graph const filter = new Filter({ inputs: { // Named input sources with their stream definitions 'video_in': videoDef, 'overlay_in': { type: 'Video', width: 500, height: 20, pixelFormat: new ffmpeg.PixelFormat(ffmpeg.AV_PIX_FMT_RGBA), timeBase: videoDef.timeBase } as VideoStreamDefinition }, outputs: { // Named output sinks 'out': videoEncoder.definition() }, // Standard ffmpeg filter graph string graph: `[video_in][overlay_in] overlay=x=20:y=${videoDef.height - 40} [out];`, timeBase: videoDef.timeBase! }); filter.on('error', console.error); const muxer = new Muxer({ outputFormat: 'matroska', streams: [videoEncoder] }); muxer.on('finish', () => console.log('Done')); muxer.on('error', console.error); // Inject a single static overlay frame const overlayFrame = ffmpeg.VideoFrame.create( Buffer.alloc(500 * 20 * 4), new ffmpeg.PixelFormat(ffmpeg.AV_PIX_FMT_RGBA), 500, 20 ); filter.src['overlay_in'].write(overlayFrame); filter.src['overlay_in'].end(); // Pipeline: Demuxer -> Decoder -> Filter -> Encoder -> Muxer -> File demuxer.video[0].pipe(videoDecoder).pipe(filter.src['video_in']); filter.sink['out'].pipe(videoEncoder).pipe(muxer.video[0]); demuxer.audio[0].pipe(audioDiscard); muxer.output!.pipe(fs.createWriteStream('output.mkv')); }); // FilterOptions interface: // { // inputs: Record; // outputs: Record; // graph: string; // ffmpeg filter graph expression // timeBase: ffmpeg.Rational; // } ``` -------------------------------- ### Resample Audio and Convert Sample Format/Layout with AudioTransform Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use AudioTransform to resample audio, convert sample formats and channel layouts, and adapt frame sizes. This is particularly useful for codecs like Vorbis that require a fixed frame size. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, AudioDecoder, AudioEncoder, AudioTransform, Muxer, Discarder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const videoDiscard = new Discarder(); const audioDecoder = new AudioDecoder(demuxer.audio[0]); const inputDef = audioDecoder.definition(); const audioEncoder = new AudioEncoder({ type: 'Audio', codec: ffmpeg.AV_CODEC_VORBIS, bitRate: 128e3, sampleRate: 44100, sampleFormat: inputDef.sampleFormat, channelLayout: inputDef.channelLayout }); // Vorbis requires a fixed frame size only known after codec opens audioEncoder.on('ready', () => { const audioTransform = new AudioTransform({ input: inputDef, output: audioEncoder.definition() // picks up the resolved frameSize }); const muxer = new Muxer({ outputFormat: 'webm', streams: [audioEncoder] }); muxer.on('finish', () => console.log('Done')); muxer.on('error', console.error); demuxer.audio[0].pipe(audioDecoder).pipe(audioTransform).pipe(audioEncoder).pipe(muxer.audio[0]); demuxer.video[0].pipe(videoDiscard); muxer.output!.pipe(require('fs').createWriteStream('output.webm')); }); }); ``` -------------------------------- ### Import ES6/TS Module Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md Import the main ffmpeg module and the Demuxer class using ES6 import syntax. This is suitable for modern JavaScript environments including TypeScript. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer } from '@mmomtchev/ffmpeg/stream'; ``` -------------------------------- ### Demuxer Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Reads and demuxes a media container. The Demuxer extends EventEmitter and opens a media container, parses its header, and exposes each elementary stream as a Readable emitting compressed Packet objects. It emits 'ready' once stream information has been parsed. ```APIDOC ## Demuxer — Read and demux a media container `Demuxer` extends `EventEmitter` and opens a media container, parses its header, and exposes each elementary stream as a `Readable` emitting compressed `Packet` objects. It emits `'ready'` once stream information has been parsed and `input.video[]` / `input.audio[]` arrays are populated. When no `inputFile` is provided, `demuxer.input` is a `Writable` into which a `fs.ReadStream` can be piped (for streamable formats only). ### Constructor `new Demuxer(options?: DemuxerOptions)` ### Options (`DemuxerOptions`) - **inputFile** (`string`, optional): Path to the media file. If omitted, the demuxer will expose a `Writable` stream (`.input`) for piping input. - **highWaterMark** (`number`, optional): Buffer size for streaming input, defaults to 64KB. - **openOptions** (`Record`, optional): Additional ffmpeg open options (e.g., RTMP headers). ### Events - **error** (`Error`): Emitted when an error occurs. - **ready** (`void`): Emitted when stream information has been parsed and stream arrays are populated. ### Example (Reading from filesystem) ```ts import * as fs from 'node:fs'; import { Demuxer, Discarder, AudioDecoder, VideoDecoder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('error', (err) => console.error('Demuxer error:', err)); demuxer.on('ready', () => { console.log(`Streams: ${demuxer.streams.length} total, ` + `${demuxer.video.length} video, ${demuxer.audio.length} audio`); const videoDecoder = new VideoDecoder(demuxer.video[0]); const audioDecoder = new AudioDecoder(demuxer.audio[0]); // ...pipe further }); ``` ### Example (Reading from a Node.js ReadStream) ```ts import * as fs from 'node:fs'; import { Demuxer, Discarder, AudioDecoder, VideoDecoder } from '@mmomtchev/ffmpeg/stream'; const streamingDemuxer = new Demuxer(); // no inputFile → exposes .input Writable streamingDemuxer.on('ready', () => { const audioDecoder = new AudioDecoder(streamingDemuxer.audio[0]); // ... }); fs.createReadStream('input.mkv').pipe(streamingDemuxer.input!); ``` ``` -------------------------------- ### Create custom media transforms with MediaTransform Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Extend `MediaTransform` to build custom object-mode Transform streams for processing `VideoFrame` or `AudioSamples`. This class handles boilerplate and can be inserted anywhere in a pipeline. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, VideoDecoder, VideoEncoder, Muxer, MediaTransform } from '@mmomtchev/ffmpeg/stream'; // Custom transform that inverts every frame's PTS for demonstration const frameLogger = new MediaTransform({ transform(chunk, encoding, callback) { if (chunk instanceof ffmpeg.VideoFrame) { console.log(`Frame pts=${chunk.pts().seconds().toFixed(3)}s, ` + `${chunk.width()}x${chunk.height()}`); } this.push(chunk); // pass frame through unchanged callback(); } }); const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const videoDecoder = new VideoDecoder(demuxer.video[0]); const def = videoDecoder.definition(); const videoEncoder = new VideoEncoder({ type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 2.5e6, width: def.width, height: def.height, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: def.pixelFormat }); const muxer = new Muxer({ outputFile: 'output.mp4', streams: [videoEncoder] }); muxer.on('finish', () => console.log('Done')); // Insert the custom transform between decoder and encoder demuxer.video[0].pipe(videoDecoder).pipe(frameLogger).pipe(videoEncoder).pipe(muxer.video[0]); }); ``` -------------------------------- ### Encode Raw Audio Samples to Compressed Packets with AudioEncoder Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use AudioEncoder to convert raw AudioSamples to compressed Packet objects. Output parameters must be explicitly defined in an AudioStreamDefinition. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, AudioDecoder, AudioEncoder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const audioDecoder = new AudioDecoder(demuxer.audio[0]); const inputDef = audioDecoder.definition(); const audioEncoder = new AudioEncoder({ type: 'Audio', codec: ffmpeg.AV_CODEC_AAC, bitRate: 128e3, // 128 kbps sampleRate: inputDef.sampleRate, // e.g. 44100 sampleFormat: inputDef.sampleFormat, // e.g. fltp channelLayout: inputDef.channelLayout // e.g. stereo }); audioEncoder.on('error', (err) => console.error('AudioEncoder error:', err)); demuxer.audio[0].pipe(audioDecoder).pipe(audioEncoder); // pipe audioEncoder to a Muxer }); // AudioStreamDefinition fields: // type: 'Audio' // codec: ffmpeg.AVCodecID | ffmpeg.Codec // bitRate: number // sampleRate: number // sampleFormat: ffmpeg.SampleFormat // channelLayout: ffmpeg.ChannelLayout // frameSize?: number // set automatically after codec opens // timeBase?: ffmpeg.Rational // codecOptions?: Record ``` -------------------------------- ### Stream Copy (Remux) with @mmomtchev/ffmpeg Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use this snippet to remux media files without transcoding. It pipes compressed packets directly from a Demuxer to a Muxer, bypassing decode/encode for fast format conversion when the output container supports the input codec. Ensure 'error' event handlers are attached to all stream components. ```typescript import { Demuxer, Muxer } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('error', console.error); demuxer.on('ready', () => { // Pass both raw encoded streams directly — no decode/encode round-trip const muxer = new Muxer({ outputFile: 'output.mp4', streams: [demuxer.audio[0], demuxer.video[0]] // EncodedMediaReadable directly }); muxer.on('finish', () => console.log('Remux complete')); muxer.on('error', console.error); demuxer.video[0].pipe(muxer.video[0]); demuxer.audio[0].pipe(muxer.audio[0]); }); ``` -------------------------------- ### Mux streams into a file or stream Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use Muxer to combine encoded video and audio streams. It can output to a file or a readable stream. Ensure streams are provided upfront. For streaming formats like Matroska or fragmented MP4, specify the output format and relevant options. ```typescript import * as fs from 'node:fs'; import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, VideoDecoder, VideoEncoder, AudioDecoder, AudioEncoder, Muxer } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const videoDecoder = new VideoDecoder(demuxer.video[0]); const audioDecoder = new AudioDecoder(demuxer.audio[0]); const videoDef = videoDecoder.definition(); const audioDef = audioDecoder.definition(); const videoEncoder = new VideoEncoder({ type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 2.5e6, width: videoDef.width, height: videoDef.height, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: videoDef.pixelFormat }); const audioEncoder = new AudioEncoder({ type: 'Audio', codec: ffmpeg.AV_CODEC_AAC, bitRate: 128e3, sampleRate: audioDef.sampleRate, sampleFormat: audioDef.sampleFormat, channelLayout: audioDef.channelLayout }); // --- Write to a file --- const fileMuxer = new Muxer({ outputFile: 'output.mp4', streams: [videoEncoder, audioEncoder] // outputFormat deduced from filename; override with outputFormat: 'mp4' }); fileMuxer.on('finish', () => console.log('File mux done')); fileMuxer.on('error', console.error); demuxer.video[0].pipe(videoDecoder).pipe(videoEncoder).pipe(fileMuxer.video[0]); demuxer.audio[0].pipe(audioDecoder).pipe(audioEncoder).pipe(fileMuxer.audio[0]); // --- Write to a Node.js WriteStream (streaming output) --- // Use a streamable format; plain mp4 requires movflags const streamMuxer = new Muxer({ outputFormat: 'matroska', // mkv is natively streamable streams: [videoEncoder, audioEncoder], outputFormatOptions: {}, // per-format options highWaterMark: 16 * 1024 // outputFile omitted → exposes .output Readable }); streamMuxer.on('finish', () => console.log('Stream mux done')); streamMuxer.on('error', console.error); streamMuxer.output!.pipe(fs.createWriteStream('output.mkv')); // For fragmented mp4 streaming: const mp4Muxer = new Muxer({ outputFormat: 'mp4', outputFormatOptions: { movflags: 'isml+frag_keyframe' }, streams: [videoEncoder, audioEncoder] }); }); // MuxerOptions interface: // { // streams: EncodedMediaReadable[]; // required: encoder outputs // outputFile?: string; // write to file (seekable) // outputFormat?: string; // e.g. 'mp4', 'matroska', 'webm', 'gif' // outputFormatOptions?: Record; // openOptions?: Record; // highWaterMark?: number; // streaming buffer, default 64KB // } ``` -------------------------------- ### Configure ffmpeg log verbosity with setLogLevel Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Control ffmpeg's internal logging to stderr. Useful for diagnosing codec or container errors that may not be apparent in thrown exceptions. Use AV_LOG_QUIET for production, AV_LOG_WARNING for warnings, and AV_LOG_DEBUG for troubleshooting. ```ts import ffmpeg from '@mmomtchev/ffmpeg'; // Suppress all output (production) ffmpeg.setLogLevel(ffmpeg.AV_LOG_QUIET); // Show warnings only ffmpeg.setLogLevel(ffmpeg.AV_LOG_WARNING); // Full debug output (useful when troubleshooting codec issues) ffmpeg.setLogLevel(ffmpeg.AV_LOG_DEBUG); // Other levels: AV_LOG_VERBOSE, AV_LOG_ERROR ``` -------------------------------- ### Decode Compressed Video Packets to Raw Frames with VideoDecoder Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Use VideoDecoder to transform compressed video packets from a Demuxer into raw VideoFrame objects. It automatically derives codec parameters and emits 'ready' when the codec is open. Pipe compressed packets into the decoder and pipe the resulting VideoFrames further. ```typescript import { Demuxer, VideoDecoder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const videoDecoder = new VideoDecoder(demuxer.video[0]); videoDecoder.on('ready', () => { const def = videoDecoder.definition(); console.log( `Decoded video: ${def.width}x${def.height}, ` + `codec=${def.codec}, ` + `frameRate=${def.frameRate}, ` + `pixelFormat=${def.pixelFormat}` ); }); videoDecoder.on('error', (err) => console.error('VideoDecoder error:', err)); // Pipe compressed packets into the decoder; raw VideoFrames flow out demuxer.video[0].pipe(videoDecoder); // videoDecoder is a Readable — pipe it further into a VideoEncoder or VideoTransform }); ``` -------------------------------- ### ffmpeg.setLogLevel(level) Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Configures the verbosity of ffmpeg's internal logging to stderr. This is useful for diagnosing codec or container errors, as ffmpeg errors are often only visible in verbose output. ```APIDOC ## ffmpeg.setLogLevel(level) — Configure ffmpeg log verbosity Controls the verbosity of ffmpeg's internal logging to stderr. Useful for diagnosing codec or container errors; ffmpeg errors are often only visible in verbose output, not in thrown exceptions. ### Parameters #### Path Parameters - **level** (number) - Required - The desired log level. Use constants like `ffmpeg.AV_LOG_QUIET`, `ffmpeg.AV_LOG_WARNING`, `ffmpeg.AV_LOG_DEBUG`. ### Example ```ts import ffmpeg from '@mmomtchev/ffmpeg'; // Suppress all output (production) ffmpeg.setLogLevel(ffmpeg.AV_LOG_QUIET); // Show warnings only ffmpeg.setLogLevel(ffmpeg.AV_LOG_WARNING); // Full debug output (useful when troubleshooting codec issues) ffmpeg.setLogLevel(ffmpeg.AV_LOG_DEBUG); ``` ``` -------------------------------- ### AudioTransform Source: https://context7.com/mmomtchev/ffmpeg/llms.txt `AudioTransform` is a `Transform` stream that uses ffmpeg's `libswresample` to convert between sample rates, sample formats, and channel layouts. It also handles frame-size adaptation (required by some codecs like Vorbis that mandate a fixed frame size). ```APIDOC ## `AudioTransform` — Resample audio and convert sample format/layout `AudioTransform` is a `Transform` stream that uses ffmpeg's `libswresample` to convert between sample rates, sample formats, and channel layouts. It also handles frame-size adaptation (required by some codecs like Vorbis that mandate a fixed frame size). ```ts import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, AudioDecoder, AudioEncoder, AudioTransform, Muxer, Discarder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const videoDiscard = new Discarder(); const audioDecoder = new AudioDecoder(demuxer.audio[0]); const inputDef = audioDecoder.definition(); const audioEncoder = new AudioEncoder({ type: 'Audio', codec: ffmpeg.AV_CODEC_VORBIS, bitRate: 128e3, sampleRate: 44100, sampleFormat: inputDef.sampleFormat, channelLayout: inputDef.channelLayout }); // Vorbis requires a fixed frame size only known after codec opens audioEncoder.on('ready', () => { const audioTransform = new AudioTransform({ input: inputDef, output: audioEncoder.definition() // picks up the resolved frameSize }); const muxer = new Muxer({ outputFormat: 'webm', streams: [audioEncoder] }); muxer.on('finish', () => console.log('Done')); muxer.on('error', console.error); demuxer.audio[0].pipe(audioDecoder).pipe(audioTransform).pipe(audioEncoder).pipe(muxer.audio[0]); demuxer.video[0].pipe(videoDiscard); muxer.output!.pipe(require('fs').createWriteStream('output.webm')); }); }); ``` ``` -------------------------------- ### Rescale and Convert Video Pixel Format with VideoTransform Source: https://context7.com/mmomtchev/ffmpeg/llms.txt Utilize VideoTransform to rescale video frames and convert pixel formats between a VideoDecoder and VideoEncoder. Specify input and output definitions, and an interpolation method. ```typescript import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, VideoDecoder, VideoEncoder, VideoTransform, Muxer, Discarder, VideoStreamDefinition } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const audioDiscard = new Discarder(); const videoDecoder = new VideoDecoder(demuxer.video[0]); const inputDef = videoDecoder.definition(); const outputDef: VideoStreamDefinition = { type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 1e6, width: 320, height: 200, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: new ffmpeg.PixelFormat(ffmpeg.AV_PIX_FMT_YUV422P) }; const rescaler = new VideoTransform({ input: inputDef, // source resolution + pixel format output: outputDef, // target resolution + pixel format interpolation: ffmpeg.SWS_BILINEAR // or SWS_BICUBIC, SWS_LANCZOS, etc. }); const videoEncoder = new VideoEncoder(outputDef); const muxer = new Muxer({ outputFile: 'output.mp4', streams: [videoEncoder] }); muxer.on('finish', () => console.log('Done')); muxer.on('error', console.error); // Demuxer -> Decoder -> Rescaler -> Encoder -> Muxer demuxer.video[0].pipe(videoDecoder).pipe(rescaler).pipe(videoEncoder).pipe(muxer.video[0]); demuxer.audio[0].pipe(audioDiscard); }); ``` -------------------------------- ### Decode Compressed Audio Packets to Raw Samples with AudioDecoder Source: https://context7.com/mmomtchev/ffmpeg/llms.txt AudioDecoder processes compressed audio packets from a Demuxer, outputting decoded AudioSamples. It inherits codec parameters from the demuxed stream. Ensure the demuxer is ready before creating and piping to the AudioDecoder. ```typescript import { Demuxer, AudioDecoder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const audioDecoder = new AudioDecoder(demuxer.audio[0]); audioDecoder.on('ready', () => { const def = audioDecoder.definition(); console.log( `Decoded audio: sampleRate=${def.sampleRate}, ` + `sampleFormat=${def.sampleFormat}, ` + `channelLayout=${def.channelLayout}, ` + `frameSize=${def.frameSize}` ); }); audioDecoder.on('error', (err) => console.error('AudioDecoder error:', err)); demuxer.audio[0].pipe(audioDecoder); // audioDecoder is a Readable — pipe to AudioEncoder or AudioTransform }); ``` -------------------------------- ### Set Log Level Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md Configure the verbosity of ffmpeg console output. Use this to control the amount of debugging information displayed, ranging from quiet to debug level. ```javascript import ffmpeg from '@mmomtchev/ffmpeg'; // Set log level to debug ffmpeg.setLogLevel(ffmpeg.AV_LOG_DEBUG); // Set log level to quiet // ffmpeg.setLogLevel(ffmpeg.AV_LOG_QUIET); ``` -------------------------------- ### AudioEncoder Source: https://context7.com/mmomtchev/ffmpeg/llms.txt `AudioEncoder` is a `Transform` stream that accepts `AudioSamples` objects and emits compressed `Packet` objects for muxing. Output parameters must be provided explicitly via an `AudioStreamDefinition`. ```APIDOC ## `AudioEncoder` — Encode raw audio samples to compressed packets `AudioEncoder` is a `Transform` stream that accepts `AudioSamples` objects and emits compressed `Packet` objects for muxing. Output parameters must be provided explicitly via an `AudioStreamDefinition`. ```ts import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, AudioDecoder, AudioEncoder } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const audioDecoder = new AudioDecoder(demuxer.audio[0]); const inputDef = audioDecoder.definition(); const audioEncoder = new AudioEncoder({ type: 'Audio', codec: ffmpeg.AV_CODEC_AAC, bitRate: 128e3, // 128 kbps sampleRate: inputDef.sampleRate, // e.g. 44100 sampleFormat: inputDef.sampleFormat, // e.g. fltp channelLayout: inputDef.channelLayout // e.g. stereo }); audioEncoder.on('error', (err) => console.error('AudioEncoder error:', err)); demuxer.audio[0].pipe(audioDecoder).pipe(audioEncoder); // pipe audioEncoder to a Muxer }); // AudioStreamDefinition fields: // type: 'Audio' // codec: ffmpeg.AVCodecID | ffmpeg.Codec // bitRate: number // sampleRate: number // sampleFormat: ffmpeg.SampleFormat // channelLayout: ffmpeg.ChannelLayout // frameSize?: number // set automatically after codec opens // timeBase?: ffmpeg.Rational // codecOptions?: Record ``` ``` -------------------------------- ### VideoTransform Source: https://context7.com/mmomtchev/ffmpeg/llms.txt `VideoTransform` is a `Transform` stream that uses ffmpeg's `libswscale` to rescale video frames to a different resolution and/or convert between pixel formats. It must sit between a `VideoDecoder` and a `VideoEncoder`. ```APIDOC ## `VideoTransform` — Rescale and/or convert pixel format of video frames `VideoTransform` is a `Transform` stream that uses ffmpeg's `libswscale` to rescale video frames to a different resolution and/or convert between pixel formats. It must sit between a `VideoDecoder` and a `VideoEncoder`. ```ts import ffmpeg from '@mmomtchev/ffmpeg'; import { Demuxer, VideoDecoder, VideoEncoder, VideoTransform, Muxer, Discarder, VideoStreamDefinition } from '@mmomtchev/ffmpeg/stream'; const demuxer = new Demuxer({ inputFile: 'input.mp4' }); demuxer.on('ready', () => { const audioDiscard = new Discarder(); const videoDecoder = new VideoDecoder(demuxer.video[0]); const inputDef = videoDecoder.definition(); const outputDef: VideoStreamDefinition = { type: 'Video', codec: ffmpeg.AV_CODEC_H264, bitRate: 1e6, width: 320, height: 200, frameRate: new ffmpeg.Rational(25, 1), pixelFormat: new ffmpeg.PixelFormat(ffmpeg.AV_PIX_FMT_YUV422P) }; const rescaler = new VideoTransform({ input: inputDef, // source resolution + pixel format output: outputDef, // target resolution + pixel format interpolation: ffmpeg.SWS_BILINEAR // or SWS_BICUBIC, SWS_LANCZOS, etc. }); const videoEncoder = new VideoEncoder(outputDef); const muxer = new Muxer({ outputFile: 'output.mp4', streams: [videoEncoder] }); muxer.on('finish', () => console.log('Done')); muxer.on('error', console.error); // Demuxer -> Decoder -> Rescaler -> Encoder -> Muxer demuxer.video[0].pipe(videoDecoder).pipe(rescaler).pipe(videoEncoder).pipe(muxer.video[0]); demuxer.audio[0].pipe(audioDiscard); }); ``` ``` -------------------------------- ### Debug Transpilation Source: https://github.com/mmomtchev/ffmpeg/blob/main/README.md Enable debug logging for specific ffmpeg components during TypeScript transpilation. Set the `TSC_DEBUG` environment variable to `1` and then use component-specific debug flags. ```bash # Example for enabling audio decoder debugging export TSC_DEBUG=1 npm run prepare export DEBUG_AUDIO_DECODER=1 node your_script.js ```