Easily compose complex and reusable ffmpeg filters using JavaScript functions and generate the command string on the fly
npm install --save composable
Just require the filters from this module and use them.
Note This project comes with typescript definition files right out of the box. Type away!
import { trim, concat, source } from "composable";
import { color, silence } from "composable";
import { separator } from "composable";
import { compile, command } from "composable";
import { ConversionExecutor } from "composable/executors";
// These will be the times we want to trim
const times = [ [ 0, 10 ], [ 20, 30 ], [ 40, 50 ] ];
const video = [];
const audio = [];
let file = source( "C:\\source\\file\\path.mkv" );
for ( let time of times ) {
const [ v, a ] = trim( file.select( 'v' ), file.select( 'a' ), time[ 0 ], time[ 1 ] );
video.push( v );
audio.push( a );
}
// Note that each stream in this case needs to be a stream factory: a method that returns a stream
const [ sepV, sepA ] = [ () => color( 'black', 1920, 1080, 3 ), () => silence( 3 ) ];
// Adds a 3-second black scene between eahc original scene
const [ outV, outA ] = concat( separator( video, sepV ), separator( audio, sepA ) );
const cmd = command( [ outV, outA ], 'C:\\generated\\file\\path.mkv', { outputArgs: [ '-f', 'matroska', '-y' ] } );
console.log( compile( cmd ).toString() );
// Or run the command right away
const process = new ConversionExecutor( [ outV, outA ], {
output: 'C:\\generated\\file\\path.mkv',
outputArgs: [ '-f', 'matroska', '-y' ]
} ).execute();
process.onProgress.subscribe( p => console.log( p.duration, p.percentage, p.frame, p.speed, p.time ) );
process.onError.subscribe( e => console.error( e ) );
process.onEnd.subscribe( () => console.log( 'ended' ) );
Will result in the following output:
-filter_complex "[0:v]trim=start=0:end=10[stream0];
[stream0]setpts=PTS-STARTPTS[stream1];
[0:a]atrim=start=0:end=10[stream2];
[stream2]asetpts=PTS-STARTPTS[stream3];
[0:v]trim=start=20:end=30[stream4];
[stream4]setpts=PTS-STARTPTS[stream5];
[0:a]atrim=start=20:end=30[stream6];
[stream6]asetpts=PTS-STARTPTS[stream7];
[0:v]trim=start=40:end=50[stream8];
[stream8]setpts=PTS-STARTPTS[stream9];
[0:a]atrim=start=40:end=50[stream10];
[stream10]asetpts=PTS-STARTPTS[stream11];
[stream1][stream3][stream5][stream7][stream9][stream11]concat=n=6:v=1:a=1[stream12][stream13]"
Try writing that by hand!
The core concept of composable are streams. You give them as arguments, and you get them as outputs. This simulates the actual way ffmpeg filters work! Because most streams need a unique name that allows them to be used and connected with other filters, managing these names can be cumbersome and error-prone. Because of this, most of composable's streams are dynamic objects, references if you will, that only get assigned a unique name during compilation.
Every filter offers a functional API to keep things simple and clean. On the inside, however, every filter is a class that contains all the information it needs. Because most functions in the public API return the streams outputed by the filter, and not the filter itself, there is a handy attribute on every stream called source
that gives access to the filter that originated that stream.
trim ( video : Stream, audio : Stream, start : number, end : number ) : [ Stream, Stream ];
Trims the video and audio at the same time, beginning at start
and ending at end
;
source ( file : string ) : SourceStream;
Returns a stream that comes from a file source.
concat ( videos : Stream[], audios : Stream[] ) : [ Stream, Stream ];
Receives a list of videos and a list of audios, and concatenates them in order.
export function boxblur ( input : Stream, named ?: FilterNamedArguments ) : OutputStream;
export function boxblur ( input : Stream, positional : FilterArgument[], named ?: FilterNamedArguments ) : OutputStream;
Blurs a given video stream.
Blur a video between 5 and 15 seconds.
video = boxblur( video, { luma_radius: 60, enable: `'between(t,5,15)'` } );
export function blackout ( input : Stream, width : number, height : number, start : number, end : number ) : OutputStream;
Receives a video stream and covers it with a black screen for the specified duration.
color ( color : string, width : number, height : number, duration ?: number ) : OutputStream;
Returns a video stream with the specified resolution, with nothing more than the background color for the specified duration.
silence ( duration ?: number ) : OutputStream;
Returns a silent audio stream with an optional duration.
export function mute ( input : Stream, start : number, end : number ) : OutputStream;
Receives an audio stream and mutes a specific part of it.
command( streams : Stream[], output ?: string, options ?: Partial ) : CommandFragment
Generates a command fragment that can be used to output the command as a string, using the generate()
method, or can
run the command right away in a child_process using execute()
. Executing returns a NodeJs.ReadableStream containing the stdout
of the process.