Fluent FFmpeg wrapper for Node.js, written in TypeScript.
Offers a fluent, chainable API for media/audio/video processing with FFmpeg, supporting streams, crossfade, audio effects, timeouts, and progress tracking.
- TypeScript-first: typed, chainable, and modern
- Streaming: easy integration with Node streams
- Audio processing: built-in EQ, volume, compression controls
- Human-friendly: good defaults and self-describing API
- Advanced features: crossfade, format conversion, codec control, kill/timeout, progress
Powered by FFmpeg.
Works in Node.js and supports streaming (no file required).
# From npm (when published)
npm install fluent-streamer
# From GitHub (latest)
npm install github:dirold2/fluent-streamer
# Using Yarn
yarn add github:dirold2/fluent-streamer
# Using PNPM
pnpm install github:dirold2/fluent-streamer
# Using Bun
bun install github:dirold2/fluent-streamerimport FluentStream from "fluent-streamer";
// Basic conversion example
const fs = new FluentStream()
.input("input.wav")
.audioCodec("aac")
.audioBitrate("192k")
.output("output.m4a");
const { done } = fs.run();
done.then(() => console.log("Conversion finished."));import FluentStream from "fluent-streamer";
import fs from "node:fs";
const input = fs.createReadStream("track.mp3");
const output = fs.createWriteStream("new.wav");
const f = new FluentStream()
.input(input)
.format("wav")
.output(output);
f.run().done.then(() => {
console.log("Done streaming!");
});const streamer = new FluentStream();
streamer
.input("a.mp3")
.input("b.mp3")
.crossfadeAudio(2.5) // 2.5 seconds crossfade
.output("x.mp3")
.run();const https = require('https');
// Stream from HTTP source to response
app.get('/audio/:fileId', async (req, res) => {
const audioUrl = `https://cdn.example.com/audio/${req.params.fileId}.mp3`;
try {
const streamer = new FluentStream()
.input(audioUrl)
.setHeaders({
'Authorization': 'Bearer token',
'X-Client-Id': 'my-app'
})
.audioCodec('aac')
.audioBitrate('128k')
.format('mp3')
.output(FluentStream.stdout);
const { output } = await streamer.run();
res.setHeader('Content-Type', 'audio/mpeg');
res.setHeader('Content-Disposition', 'inline; filename="converted.mp3"');
output.pipe(res);
} catch (error) {
console.error('Audio stream error:', error);
res.status(500).send('Streaming failed');
}
});// Create streamer for live audio processing
const streamer = new FluentStream({
enableProgressTracking: true,
useAudioProcessor: true
});
const inputStream = getAudioSource();
const outputStream = fs.createWriteStream('processed.wav');
const { output, done } = streamer
.input(inputStream)
.audioCodec('pcm_s16le')
.audioFrequency(44100)
.audioChannels(2)
.output(outputStream)
.run();
// Adjust audio effects in real-time
streamer.changeVolume(1.5); // Boost volume by 50%
streamer.changeBass(5); // Increase bass
streamer.changeTreble(-3); // Reduce treble
// Listen for progress events
streamer.on('progress', (progress) => {
console.log(`Processing: ${progress.progress}%`);
});
await done;// Fade in/out example
const streamer = new FluentStream()
.input('background-music.mp3')
.setVolume(0) // Start silent
.output('fade-demo.mp3');
// Fade in over 2 seconds
await new Promise(resolve => setTimeout(resolve, 1000));
streamer.fadeIn(1, 2000); // Target volume 1, fade time 2000ms
await new Promise(resolve => setTimeout(resolve, 8000));
// Fade out over 3 seconds
streamer.fadeOut(3000);
const { done } = streamer.run();
await done;// Real-time audio adjustments during playback
const streamer = new FluentStream({
enableProgressTracking: true,
useAudioProcessor: true
})
.input("music.mp3")
.setVolume(1.5) // Increase volume by 50%
.setBass(8) // Boost bass
.setTreble(-3) // Cut treble slightly
.setCompressor(true) // Enable dynamic compression
.output("enhanced.wav");
// Adjust effects during playback
await streamer.run().done;FluentStream provides a fluent interface:
new FluentStream()
.input("song.mp3")
.seekInput(30) // seek to 30s
.audioCodec("opus")
.audioBitrate("128k")
.output("clip.opus")
.run();Main methods:
.input(src)— add file/stream input.output(dst)— set the output (file/stream/fd).audioCodec(codec)/.videoCodec(codec)— set codecs.audioBitrate(bps)/.videoBitrate(bps).format(fmt)— set output format (e.g. 'mp3', 'wav').seekInput(time)— seek input position.overwrite()— overwrite output file(s).map(spec)— select specific streams.crossfadeAudio(seconds, options?)— crossfade (audio).run()— start processing (returns output, done, stop)
new FluentStream(options?: ProcessorOptions)Creates a new FluentStream instance with optional processor options.
Options:
timeout?: number- FFmpeg process timeout in secondsenableProgressTracking?: boolean- Enable progress event emissionfailFast?: boolean- Stop processing on first errorwallTimeLimit?: number- Maximum wall clock time limituseAudioProcessor?: boolean- Enable built-in audio processingaudioProcessorOptions?: AudioProcessingOptions- Audio effect defaults
Add file, URL, or stream input.
// File input
.input('/path/to/audio.mp3')
// HTTP URL with custom headers
.input('https://cdn.com/track.mp3')
// Stream input
.input(fs.createReadStream('input.wav'), { pipeIndex: 0 })Options:
label?: string- Input label for identificationpipeIndex?: number- Custom pipe index for streamsallowDuplicate?: boolean- Allow duplicate inputs
Set output destination.
// File output (auto-overwrites if .overwrite() called)
.output('/path/to/output.mp4')
// Stream output
.output(fs.createWriteStream('out.wav'))
// Stdout
.output(FluentStream.stdout)
// Pipe
.output({ pipe: 'pipe:1' })Set audio codec: 'aac', 'mp3', 'opus', 'vorbis', 'pcm_s16le', etc.
Set video codec: 'h264', 'h265', 'vp9', 'av1', 'mpeg4', etc.
Set audio bitrate: '128k', '192k', '320k', 'variable', etc.
Set video bitrate: '1M', '2M', '5M', '10M' for high-quality, etc.
Set output container format: 'mp3', 'mp4', 'wav', 'flac', 'webm', etc.
Set sample rate: 44100, 48000, 96000, etc.
Set channel count: 1 (mono), 2 (stereo), 6 (5.1), 8 (7.1), etc.
Seek to position in input: 30, '00:00:30', '1:30', etc.
Limit output duration.
Select specific streams (advanced FFmpeg feature):
.map('0:v') // Select first video stream
.map('1:a') // Select second audio stream
.map('0') // Select all streams from first inputDisable video/audio processing entirely.
Allow overwriting existing output files.
Copy streams without re-encoding (faster, preserves quality).
Set volume multiplier (0-2): 0.5 (half), 1.0 (normal), 1.5 (50% boost).
Adjust bass level (-20 to 20): 0 (neutral), 5 (boost), -3 (cut).
Adjust treble level (-20 to 20): 0 (neutral), 5 (boost), -3 (cut).
Enable/disable audio compression for consistency.
Set all EQ parameters at once.
Fade in from current volume to target volume.
Fade out to silence.
Real-time changes (during playback):
.changeVolume(value: number)— boolean success.changeBass(value: number)— boolean success.changeTreble(value: number)— boolean success.changeCompressor(enabled: boolean)— boolean success.changeEqualizer(bass, treble, compressor)— boolean success
Create crossfade between inputs using FFmpeg's acrossfade filter.
.crossfadeAudio(2.5, {
curve1: 'tri', // crossfade curve
curve2: 'tri',
overlap: true,
secondInput: 'path/to/second.mp3'
})Add complex FFmpeg filter graphs for advanced processing.
Set custom HTTP headers for remote sources.
Set User-Agent header for HTTP requests.
Add FFmpeg arguments at specific positions.
Start processing. Returns:
{
output: Readable, // Output stream if piped
done: Promise<void>, // Completion promise
stop: () => void // Stop function
}Reset instance for reuse (required before .run() after previous use).
Check instance state.
'progress'— Progress updates (ifenableProgressTracking: true)'error'— Processing errors'complete'— Processing finished'start'— Processing started
FluentStream.stdout— { pipe: 'stdout' }FluentStream.stderr— { pipe: 'stderr' }FluentStream.pipe1— { pipe: 'pipe:1' }FluentStream.pipe2— { pipe: 'pipe:2' }
- Timeout: Set the
timeoutoption to auto-kill long FFmpeg jobs. - Progress: Get real-time progress events by enabling
.options({ enableProgressTracking: true })and listening for"progress"events. - Headers: Default humanity headers are sent to FFmpeg HTTP(S) sources; override with
.setHeaders(obj). - Kill:
run()returns a stop function to terminate process safely.
- Written in TypeScript: All primary objects (FluentStream, Processor) are strongly typed
- Built-in Audio processing: Volume, EQ, compression effects through
AudioProcessorclass - Extensible architecture: Built-in audio filters with real-time capabilities
FFmpeg not found:
Error: spawn ffmpeg ENOENT
- Ensure FFmpeg is installed and accessible in PATH
- On Linux/macOS:
which ffmpeg - On Windows: Check if ffmpeg.exe is in your PATH
- Alternative: Provide full path with
new FluentStream({ ffmpegPath: '/usr/bin/ffmpeg' })
Stream errors with HTTP sources:
[tcp @ 0x...] Connection refused
- Verify URL is accessible:
curl -I https://example.com/audio.mp3 - Check network connectivity and firewall settings
- Some servers block requests without proper headers
Codec/format not supported:
Unknown encoder 'xxx' or unsupported codec
- Verify FFmpeg installation supports the codec:
ffmpeg -codecs | grep xxx - Some codecs require additional FFmpeg builds (e.g., non-free codecs)
- Try simpler codecs like
aacfor audio,h264for video
Out of memory during processing:
Cannot allocate memory
- Reduce input resolution/frame rate for videos
- Use
copyCodecs()for passthrough where quality loss is acceptable - Process in smaller chunks for large files
- Increase system memory limits if possible
File locking/permissions issues:
Permission denied (file locked)
- Ensure output directory is writable
- Close file handles before processing
- On Windows, ensure files aren't open in other applications
- Use unique temporary filenames
Enable verbose logging:
const streamer = new FluentStream({
logger: console // Enable console logging
});
// Or
import debug from 'debug';
FluentStream.logger = { /* custom logger */ };Inspect FFmpeg command:
const streamer = new FluentStream()
.input('in.mp3')
.audioCodec('aac')
.output('out.m4a');
console.log('FFmpeg args:', streamer.getArgs());
console.log('Inputs:', streamer.getInputSummary());Check instance state:
console.log('Is dirty:', streamer.isDirtyState());
console.log('Is ready:', streamer.isReady());
console.log('Debug info:', streamer.debugInfo());For real-time streaming:
- Use high priority for FFmpeg processes
- Optimize audio-only processing with
.noVideo() - Use efficient codecs like
aacinstead ofmp3for streaming - Implement buffering to prevent stutter
- Monitor system resources during peak usage
For file processing:
- Use multiple CPU cores with FFmpeg threading:
.outputOptions('-threads', '0') - Process related tasks in parallel with separate FluentStream instances
- Cache processed results when possible
- Use faster presets:
.outputOptions('-preset', 'fast')
Memory management:
- Use streams for large files instead of loading entire content
- Clean up instances with
.clear()after use - Limit concurrent FFmpeg processes
- Monitor memory usage in production
Node.js versions: 18+ recommended FFmpeg versions: 4.0+ required, 5.0+ recommended OS support: Linux, macOS, Windows, Docker containers
Known limitations:
- Real-time effects work best with PCM audio streams
- Some advanced filters may not support all input combinations
- HTTP streaming may have buffering delays depending on network
- Large crossfades can be memory-intensive
git clone https://github.com/Dirold2/fluent-streamer.git
cd fluent-streamer
npm install
npm run build
npm test- TypeScript: Strict typing required, ESLint compliant
- Naming: CamelCase for classes, camelCase for methods/properties
- Documentation: JSDoc comments for all public APIs
- Tests: Vitest-based, aim for >90% coverage
- Create issue on GitHub for new features
- Implement in appropriate module (Core, Types, etc.)
- Add tests for new functionality
- Update documentation (README, JSDoc)
- Ensure no breaking changes without major version bump
The AudioProcessor class provides built-in audio effects that can be extended:
- Real-time effects: Volume, EQ, compression available during playback
- Custom AudioProcessor: Extend
AudioProcessorclass for new effects - Integration: Effects are automatically applied to PCM audio streams
- Performance: C++ bindings ensure low-latency processing
When reporting bugs, please include:
- Node.js and FFmpeg versions
- OS and architecture
- Minimal reproducible example
- Expected vs actual behavior
- Debug output if available
- Enhanced video processing filters
- WebAssembly audio processing for browsers
- GPU acceleration for video encoding
- Advanced audio analysis features
- Streaming server integrations
MIT © dirold2