Giter Club home page Giter Club logo

node-fluent-ffmpeg's Introduction

Fluent ffmpeg-API for node.js

Coverage Status FOSSA Status

Fluent-ffmpeg is looking for new maintainers More details on the wiki

This library abstracts the complex command-line usage of ffmpeg into a fluent, easy to use node.js module. In order to be able to use this module, make sure you have ffmpeg installed on your system (including all necessary encoding libraries like libmp3lame or libx264).

This is the documentation for fluent-ffmpeg 2.x. You can still access the code and documentation for fluent-ffmpeg 1.7 here.

Installation

Via npm:

$ npm install fluent-ffmpeg

Or as a submodule:

$ git submodule add git://github.com/schaermu/node-fluent-ffmpeg.git vendor/fluent-ffmpeg

Usage

You will find a lot of usage examples (including a real-time streaming example using flowplayer and express!) in the examples folder.

Prerequisites

ffmpeg and ffprobe

fluent-ffmpeg requires ffmpeg >= 0.9 to work. It may work with previous versions but several features won't be available (and the library is not tested with lower versions anylonger).

If the FFMPEG_PATH environment variable is set, fluent-ffmpeg will use it as the full path to the ffmpeg executable. Otherwise, it will attempt to call ffmpeg directly (so it should be in your PATH). You must also have ffprobe installed (it comes with ffmpeg in most distributions). Similarly, fluent-ffmpeg will use the FFPROBE_PATH environment variable if it is set, otherwise it will attempt to call it in the PATH.

Most features should work when using avconv and avprobe instead of ffmpeg and ffprobe, but they are not officially supported at the moment.

Windows users: most probably ffmpeg and ffprobe will not be in your %PATH, so you must set %FFMPEG_PATH and %FFPROBE_PATH.

Debian/Ubuntu users: the official repositories have the ffmpeg/ffprobe executable in the libav-tools package, and they are actually rebranded avconv/avprobe executables (avconv is a fork of ffmpeg). They should be mostly compatible, but should you encounter any issue, you may want to use the real ffmpeg instead. You can either compile it from source or find a pre-built .deb package at https://ffmpeg.org/download.html (For Ubuntu, the ppa:mc3man/trusty-media PPA provides recent builds).

flvtool2 or flvmeta

If you intend to encode FLV videos, you must have either flvtool2 or flvmeta installed and in your PATH or fluent-ffmpeg won't be able to produce streamable output files. If you set either the FLVTOOL2_PATH or FLVMETA_PATH, fluent-ffmpeg will try to use it instead of searching in the PATH.

Setting binary paths manually

Alternatively, you may set the ffmpeg, ffprobe and flvtool2/flvmeta binary paths manually by using the following API commands:

  • Ffmpeg.setFfmpegPath(path) Argument path is a string with the full path to the ffmpeg binary.
  • Ffmpeg.setFfprobePath(path) Argument path is a string with the full path to the ffprobe binary.
  • Ffmpeg.setFlvtoolPath(path) Argument path is a string with the full path to the flvtool2 or flvmeta binary.

Creating an FFmpeg command

The fluent-ffmpeg module returns a constructor that you can use to instanciate FFmpeg commands.

var FfmpegCommand = require('fluent-ffmpeg');
var command = new FfmpegCommand();

You can also use the constructor without the new operator.

var ffmpeg = require('fluent-ffmpeg');
var command = ffmpeg();

You may pass an input file name or readable stream, a configuration object, or both to the constructor.

var command = ffmpeg('/path/to/file.avi');
var command = ffmpeg(fs.createReadStream('/path/to/file.avi'));
var command = ffmpeg({ option: "value", ... });
var command = ffmpeg('/path/to/file.avi', { option: "value", ... });

The following options are available:

  • source: input file name or readable stream (ignored if an input file is passed to the constructor)
  • timeout: ffmpeg timeout in seconds (defaults to no timeout)
  • preset or presets: directory to load module presets from (defaults to the lib/presets directory in fluent-ffmpeg tree)
  • niceness or priority: ffmpeg niceness value, between -20 and 20; ignored on Windows platforms (defaults to 0)
  • logger: logger object with debug(), info(), warn() and error() methods (defaults to no logging)
  • stdoutLines: maximum number of lines from ffmpeg stdout/stderr to keep in memory (defaults to 100, use 0 for unlimited storage)

Specifying inputs

You can add any number of inputs to an Ffmpeg command. An input can be:

  • a file name (eg. /path/to/file.avi);
  • an image pattern (eg. /path/to/frame%03d.png);
  • a readable stream; only one input stream may be used for a command, but you can use both an input stream and one or several file names.
// Note that all fluent-ffmpeg methods are chainable
ffmpeg('/path/to/input1.avi')
  .input('/path/to/input2.avi')
  .input(fs.createReadStream('/path/to/input3.avi'));

// Passing an input to the constructor is the same as calling .input()
ffmpeg()
  .input('/path/to/input1.avi')
  .input('/path/to/input2.avi');

// Most methods have several aliases, here you may use addInput or mergeAdd instead
ffmpeg()
  .addInput('/path/to/frame%02d.png')
  .addInput('/path/to/soundtrack.mp3');

ffmpeg()
  .mergeAdd('/path/to/input1.avi')
  .mergeAdd('/path/to/input2.avi');

Input options

The following methods enable passing input-related options to ffmpeg. Each of these methods apply on the last input added (including the one passed to the constructor, if any). You must add an input before calling those, or an error will be thrown.

inputFormat(format): specify input format

Aliases: fromFormat(), withInputFormat().

This is only useful for raw inputs, as ffmpeg can determine the input format automatically.

ffmpeg()
  .input('/dev/video0')
  .inputFormat('mov')
  .input('/path/to/file.avi')
  .inputFormat('avi');

Fluent-ffmpeg checks for format availability before actually running the command, and throws an error when a specified input format is not available.

inputFPS(fps): specify input framerate

Aliases: withInputFps(), withInputFPS(), withFpsInput(), withFPSInput(), inputFps(), fpsInput(), FPSInput().

This is only valid for raw inputs, as ffmpeg can determine the input framerate automatically.

ffmpeg('/dev/video0').inputFPS(29.7);

native(): read input at native framerate

Aliases: nativeFramerate(), withNativeFramerate().

ffmpeg('/path/to/file.avi').native();

seekInput(time): set input start time

Alias: setStartTime().

Seeks an input and only start decoding at given time offset. The time argument may be a number (in seconds) or a timestamp string (with format [[hh:]mm:]ss[.xxx]).

ffmpeg('/path/to/file.avi').seekInput(134.5);
ffmpeg('/path/to/file.avi').seekInput('2:14.500');

loop([duration]): loop over input

ffmpeg('/path/to/file.avi').loop();
ffmpeg('/path/to/file.avi').loop(134.5);
ffmpeg('/path/to/file.avi').loop('2:14.500');

inputOptions(option...): add custom input options

Aliases: inputOption(), addInputOption(), addInputOptions(), withInputOption(), withInputOptions().

This method allows passing any input-related option to ffmpeg. You can call it with a single argument to pass a single option, optionally with a space-separated parameter:

/* Single option */
ffmpeg('/path/to/file.avi').inputOptions('-someOption');

/* Single option with parameter */
ffmpeg('/dev/video0').inputOptions('-r 24');

You may also pass multiple options at once by passing an array to the method:

ffmpeg('/path/to/file.avi').inputOptions([
  '-option1',
  '-option2 param2',
  '-option3',
  '-option4 param4'
]);

Finally, you may also directly pass command line tokens as separate arguments to the method:

ffmpeg('/path/to/file.avi').inputOptions(
  '-option1',
  '-option2', 'param2',
  '-option3',
  '-option4', 'param4'
);

Audio options

The following methods change the audio stream(s) in the produced output.

noAudio(): disable audio altogether

Aliases: withNoAudio().

Disables audio in the output and remove any previously set audio option.

ffmpeg('/path/to/file.avi').noAudio();

audioCodec(codec): set audio codec

Aliases: withAudioCodec().

ffmpeg('/path/to/file.avi').audioCodec('libmp3lame');

Fluent-ffmpeg checks for codec availability before actually running the command, and throws an error when a specified audio codec is not available.

audioBitrate(bitrate): set audio bitrate

Aliases: withAudioBitrate().

Sets the audio bitrate in kbps. The bitrate parameter may be a number or a string with an optional k suffix. This method is used to enforce a constant bitrate; use audioQuality() to encode using a variable bitrate.

ffmpeg('/path/to/file.avi').audioBitrate(128);
ffmpeg('/path/to/file.avi').audioBitrate('128');
ffmpeg('/path/to/file.avi').audioBitrate('128k');

audioChannels(count): set audio channel count

Aliases: withAudioChannels().

ffmpeg('/path/to/file.avi').audioChannels(2);

audioFrequency(freq): set audio frequency

Aliases: withAudioFrequency().

The freq parameter specifies the audio frequency in Hz.

ffmpeg('/path/to/file.avi').audioFrequency(22050);

audioQuality(quality): set audio quality

Aliases: withAudioQuality().

This method fixes a quality factor for the audio codec (VBR encoding). The quality scale depends on the actual codec used.

ffmpeg('/path/to/file.avi')
  .audioCodec('libmp3lame')
  .audioQuality(0);

audioFilters(filter...): add custom audio filters

Aliases: audioFilter(), withAudioFilter(), withAudioFilters().

This method enables adding custom audio filters. You may add multiple filters at once by passing either several arguments or an array. See the Ffmpeg documentation for available filters and their syntax.

Each filter pased to this method can be either a filter string (eg. volume=0.5) or a filter specification object with the following keys:

  • filter: filter name
  • options: optional; either an option string for the filter (eg. n=-50dB:d=5), an options array for unnamed options (eg. ['-50dB', 5]) or an object mapping option names to values (eg. { n: '-50dB', d: 5 }). When options is not specified, the filter will be added without any options.
ffmpeg('/path/to/file.avi')
  .audioFilters('volume=0.5')
  .audioFilters('silencedetect=n=-50dB:d=5');

ffmpeg('/path/to/file.avi')
  .audioFilters('volume=0.5', 'silencedetect=n=-50dB:d=5');

ffmpeg('/path/to/file.avi')
  .audioFilters(['volume=0.5', 'silencedetect=n=-50dB:d=5']);

ffmpeg('/path/to/file.avi')
  .audioFilters([
    {
      filter: 'volume',
      options: '0.5'
    },
    {
      filter: 'silencedetect',
      options: 'n=-50dB:d=5'
    }
  ]);

ffmpeg('/path/to/file.avi')
  .audioFilters(
    {
      filter: 'volume',
      options: ['0.5']
    },
    {
      filter: 'silencedetect',
      options: { n: '-50dB', d: 5 }
    }
  ]);

Video options

The following methods change the video stream(s) in the produced output.

noVideo(): disable video altogether

Aliases: withNoVideo().

This method disables video output and removes any previously set video option.

ffmpeg('/path/to/file.avi').noVideo();

videoCodec(codec): set video codec

Aliases: withVideoCodec().

ffmpeg('/path/to/file.avi').videoCodec('libx264');

Fluent-ffmpeg checks for codec availability before actually running the command, and throws an error when a specified video codec is not available.

videoBitrate(bitrate[, constant=false]): set video bitrate

Aliases: withVideoBitrate().

Sets the target video bitrate in kbps. The bitrate argument may be a number or a string with an optional k suffix. The constant argument specifies whether a constant bitrate should be enforced (defaults to false).

Keep in mind that, depending on the codec used, enforcing a constant bitrate often comes at the cost of quality. The best way to have a constant video bitrate without losing too much quality is to use 2-pass encoding (see Fffmpeg documentation).

ffmpeg('/path/to/file.avi').videoBitrate(1000);
ffmpeg('/path/to/file.avi').videoBitrate('1000');
ffmpeg('/path/to/file.avi').videoBitrate('1000k');
ffmpeg('/path/to/file.avi').videoBitrate('1000k', true);

videoFilters(filter...): add custom video filters

Aliases: videoFilter(), withVideoFilter(), withVideoFilters().

This method enables adding custom video filters. You may add multiple filters at once by passing either several arguments or an array. See the Ffmpeg documentation for available filters and their syntax.

Each filter pased to this method can be either a filter string (eg. fade=in:0:30) or a filter specification object with the following keys:

  • filter: filter name
  • options: optional; either an option string for the filter (eg. in:0:30), an options array for unnamed options (eg. ['in', 0, 30]) or an object mapping option names to values (eg. { t: 'in', s: 0, n: 30 }). When options is not specified, the filter will be added without any options.
ffmpeg('/path/to/file.avi')
  .videoFilters('fade=in:0:30')
  .videoFilters('pad=640:480:0:40:violet');

ffmpeg('/path/to/file.avi')
  .videoFilters('fade=in:0:30', 'pad=640:480:0:40:violet');

ffmpeg('/path/to/file.avi')
  .videoFilters(['fade=in:0:30', 'pad=640:480:0:40:violet']);

ffmpeg('/path/to/file.avi')
  .videoFilters([
    {
      filter: 'fade',
      options: 'in:0:30'
    },
    {
      filter: 'pad',
      options: '640:480:0:40:violet'
    }
  ]);

ffmpeg('/path/to/file.avi')
    .videoFilters(
    {
      filter: 'fade',
      options: ['in', 0, 30]
    },
    {
      filter: 'filter2',
      options: { w: 640, h: 480, x: 0, y: 40, color: 'violet' }
    }
  );

fps(fps): set output framerate

Aliases: withOutputFps(), withOutputFPS(), withFpsOutput(), withFPSOutput(), withFps(), withFPS(), outputFPS(), outputFps(), fpsOutput(), FPSOutput(), FPS().

ffmpeg('/path/to/file.avi').fps(29.7);

frames(count): specify frame count

Aliases: takeFrames(), withFrames().

Set ffmpeg to only encode a certain number of frames.

ffmpeg('/path/to/file.avi').frames(240);

Video frame size options

The following methods enable resizing the output video frame size. They all work together to generate the appropriate video filters.

size(size): set output frame size

Aliases: videoSize(), withSize().

This method sets the output frame size. The size argument may have one of the following formats:

  • 640x480: set a fixed output frame size. Unless autopad() is called, this may result in the video being stretched or squeezed to fit the requested size.
  • 640x?: set a fixed width and compute height automatically. If aspect() is also called, it is used to compute video height; otherwise it is computed so that the input aspect ratio is preserved.
  • ?x480: set a fixed height and compute width automatically. If aspect() is also called, it is used to compute video width; otherwise it is computed so that the input aspect ratio is preserved.
  • 50%: rescale both width and height to the given percentage. Aspect ratio is always preserved.

Note that for compatibility with some codecs, computed dimensions are always rounded down to multiples of 2.

ffmpeg('/path/to/file.avi').size('640x480');
ffmpeg('/path/to/file.avi').size('640x?');
ffmpeg('/path/to/file.avi').size('640x?').aspect('4:3');
ffmpeg('/path/to/file.avi').size('50%');

aspect(aspect): set output frame aspect ratio

Aliases: withAspect(), withAspectRatio(), setAspect(), setAspectRatio(), aspectRatio().

This method enforces a specific output aspect ratio. The aspect argument may either be a number or a X:Y string.

Note that calls to aspect() are ignored when size() has been called with a fixed width and height or a percentage, and also when size() has not been called at all.

ffmpeg('/path/to/file.avi').size('640x?').aspect('4:3');
ffmpeg('/path/to/file.avi').size('640x?').aspect(1.33333);

autopad([color='black']): enable auto-padding the output video

Aliases: applyAutopadding(), applyAutoPadding(), applyAutopad(), applyAutoPad(), withAutopadding(), withAutoPadding(), withAutopad(), withAutoPad(), autoPad().

This method enables applying auto-padding to the output video. The color parameter specifies which color to use for padding, and must be a color code or name supported by ffmpeg (defaults to 'black').

The behaviour of this method depends on calls made to other video size methods:

  • when size() has been called with a percentage or has not been called, it is ignored;
  • when size() has been called with WxH, it adds padding so that the input aspect ratio is kept;
  • when size() has been called with either Wx? or ?xH, padding is only added if aspect() was called (otherwise the output dimensions are computed from the input aspect ratio and padding is not needed).
// No size specified, autopad() is ignored
ffmpeg('/path/to/file.avi').autopad();

// Adds padding to keep original aspect ratio.
// - with a 640x400 input, 40 pixels of padding are added on both sides
// - with a 600x480 input, 20 pixels of padding are added on top and bottom
// - with a 320x200 input, video is scaled up to 640x400 and 40px of padding
//   is added on both sides
// - with a 320x240 input, video is scaled up to 640x480 and and no padding
//   is needed
ffmpeg('/path/to/file.avi').size('640x480').autopad();
ffmpeg('/path/to/file.avi').size('640x480').autopad('white');
ffmpeg('/path/to/file.avi').size('640x480').autopad('#35A5FF');

// Size computed from input, autopad() is ignored
ffmpeg('/path/to/file.avi').size('50%').autopad();
ffmpeg('/path/to/file.avi').size('640x?').autopad();
ffmpeg('/path/to/file.avi').size('?x480').autopad();

// Calling .size('640x?').aspect('4:3') is similar to calling .size('640x480')
// - with a 640x400 input, 40 pixels of padding are added on both sides
// - with a 600x480 input, 20 pixels of padding are added on top and bottom
// - with a 320x200 input, video is scaled up to 640x400 and 40px of padding
//   is added on both sides
// - with a 320x240 input, video is scaled up to 640x480 and and no padding
//   is needed
ffmpeg('/path/to/file.avi').size('640x?').aspect('4:3').autopad();
ffmpeg('/path/to/file.avi').size('640x?').aspect('4:3').autopad('white');
ffmpeg('/path/to/file.avi').size('640x?').aspect('4:3').autopad('#35A5FF');

// Calling .size('?x480').aspect('4:3') is similar to calling .size('640x480')
ffmpeg('/path/to/file.avi').size('?x480').aspect('4:3').autopad();
ffmpeg('/path/to/file.avi').size('?x480').aspect('4:3').autopad('white');
ffmpeg('/path/to/file.avi').size('?x480').aspect('4:3').autopad('#35A5FF');

For compatibility with previous fluent-ffmpeg versions, this method also accepts an additional boolean first argument, which specifies whether to apply auto-padding.

ffmpeg('/path/to/file.avi').size('640x480').autopad(true);
ffmpeg('/path/to/file.avi').size('640x480').autopad(true, 'pink');

keepDAR(): force keeping display aspect ratio

Aliases: keepPixelAspect(), keepDisplayAspect(), keepDisplayAspectRatio().

This method is useful when converting an input with non-square pixels to an output format that does not support non-square pixels (eg. most image formats). It rescales the input so that the display aspect ratio is the same.

ffmpeg('/path/to/file.avi').keepDAR();

Specifying multiple outputs

output(target[, options]): add an output to the command

Aliases: addOutput().

Adds an output to the command. The target argument may be an output filename or a writable stream (but at most one output stream may be used with a single command).

When target is a stream, an additional options object may be passed. If it is present, it will be passed ffmpeg output stream pipe() method.

Adding an output switches the "current output" of the command, so that any fluent-ffmpeg method that applies to an output is indeed applied to the last output added. For backwards compatibility reasons, you may as well call those methods before adding the first output (in which case they will apply to the first output when it is added). Methods that apply to an output are all non-input-related methods, except for complexFilter(), which is global.

Also note that when calling output(), you should not use the save() or stream() (formerly saveToFile() and writeToStream()) methods, as they already add an output. Use the run() method to start processing.

var stream  = fs.createWriteStream('outputfile.divx');

ffmpeg('/path/to/file.avi')
  .output('outputfile.mp4')
  .output(stream);

ffmpeg('/path/to/file.avi')
  // You may pass a pipe() options object when using a stream
  .output(stream, { end:true });

// Output-related methods apply to the last output added
ffmpeg('/path/to/file.avi')

  .output('outputfile.mp4')
  .audioCodec('libfaac')
  .videoCodec('libx264')
  .size('320x200')

  .output(stream)
  .preset('divx')
  .size('640x480');

// Use the run() method to run commands with multiple outputs
ffmpeg('/path/to/file.avi')
  .output('outputfile.mp4')
  .output(stream)
  .on('end', function() {
    console.log('Finished processing');
  })
  .run();

Output options

duration(time): set output duration

Aliases: withDuration(), setDuration().

Forces ffmpeg to stop transcoding after a specific output duration. The time parameter may be a number (in seconds) or a timestamp string (with format [[hh:]mm:]ss[.xxx]).

ffmpeg('/path/to/file.avi').duration(134.5);
ffmpeg('/path/to/file.avi').duration('2:14.500');

seek(time): seek output

Aliases: seekOutput().

Seeks streams before encoding them into the output. This is different from calling seekInput() in that the offset will only apply to one output. This is also slower, as skipped frames will still be decoded (but dropped).

The time argument may be a number (in seconds) or a timestamp string (with format [[hh:]mm:]ss[.xxx]).

ffmpeg('/path/to/file.avi')
  .seekInput('1:00')

  .output('from-1m30s.avi')
  .seek(30)

  .output('from-1m40s.avi')
  .seek('0:40');

format(format): set output format

Aliases: withOutputFormat(), toFormat(), outputFormat().

ffmpeg('/path/to/file.avi').format('flv');

flvmeta(): update FLV metadata after transcoding

Aliases: updateFlvMetadata().

Calling this method makes fluent-ffmpeg run flvmeta or flvtool2 on the output file to add FLV metadata and make files streamable. It does not work when outputting to a stream, and is only useful when outputting to FLV format.

ffmpeg('/path/to/file.avi').flvmeta().format('flv');

outputOptions(option...): add custom output options

Aliases: outputOption(), addOutputOption(), addOutputOptions(), withOutputOption(), withOutputOptions(), addOption(), addOptions().

This method allows passing any output-related option to ffmpeg. You can call it with a single argument to pass a single option, optionally with a space-separated parameter:

/* Single option */
ffmpeg('/path/to/file.avi').outputOptions('-someOption');

/* Single option with parameter */
ffmpeg('/dev/video0').outputOptions('-r 24');

You may also pass multiple options at once by passing an array to the method:

ffmpeg('/path/to/file.avi').outputOptions([
  '-option1',
  '-option2 param2',
  '-option3',
  '-option4 param4'
]);

Finally, you may also directly pass command line tokens as separate arguments to the method:

ffmpeg('/path/to/file.avi').outputOptions(
  '-option1',
  '-option2', 'param2',
  '-option3',
  '-option4', 'param4'
);

Miscellaneous options

preset(preset): use fluent-ffmpeg preset

Aliases: usingPreset().

There are two kinds of presets supported by fluent-ffmpeg. The first one is preset modules; to use those, pass the preset name as the preset argument. Preset modules are loaded from the directory specified by the presets constructor option (defaults to the lib/presets fluent-ffmpeg subdirectory).

// Uses <path-to-fluent-ffmpeg>/lib/presets/divx.js
ffmpeg('/path/to/file.avi').preset('divx');

// Uses /my/presets/foo.js
ffmpeg('/path/to/file.avi', { presets: '/my/presets' }).preset('foo');

Preset modules must export a load() function that takes an FfmpegCommand as an argument. fluent-ffmpeg comes with the following preset modules preinstalled:

  • divx
  • flashvideo
  • podcast

Here is the code from the included divx preset as an example:

exports.load = function(ffmpeg) {
  ffmpeg
    .format('avi')
    .videoBitrate('1024k')
    .videoCodec('mpeg4')
    .size('720x?')
    .audioBitrate('128k')
    .audioChannels(2)
    .audioCodec('libmp3lame')
    .outputOptions(['-vtag DIVX']);
};

The second kind of preset is preset functions. To use those, pass a function which takes an FfmpegCommand as a parameter.

function myPreset(command) {
  command.format('avi').size('720x?');
}

ffmpeg('/path/to/file.avi').preset(myPreset);

complexFilter(filters[, map]): set complex filtergraph

Aliases: filterGraph()

The complexFilter() method enables setting a complex filtergraph for a command. It expects a filter specification (or a filter specification array) and an optional output mapping parameter as arguments.

Filter specifications may be either plain ffmpeg filter strings (eg. split=3[a][b][c]) or objects with the following keys:

  • filter: filter name
  • options: optional; either an option string for the filter (eg. in:0:30), an options array for unnamed options (eg. ['in', 0, 30]) or an object mapping option names to values (eg. { t: 'in', s: 0, n: 30 }). When options is not specified, the filter will be added without any options.
  • inputs: optional; input stream specifier(s) for the filter. The value may be either a single stream specifier string or an array of stream specifiers. Each specifier can be optionally enclosed in square brackets. When input streams are not specified, ffmpeg will use the first unused streams of the correct type.
  • outputs: optional; output stream specifier(s) for the filter. The value may be either a single stream specifier string or an array of stream specifiers. Each specifier can be optionally enclosed in square brackets.

The output mapping parameter specifies which stream(s) to include in the output from the filtergraph. It may be either a single stream specifier string or an array of stream specifiers. Each specifier can be optionally enclosed in square brackets. When this parameter is not present, ffmpeg will default to saving all unused outputs to the output file.

Note that only one complex filtergraph may be set on a given command. Calling complexFilter() again will override any previously set filtergraph, but you can set as many filters as needed in a single call.

ffmpeg('/path/to/file.avi')
  .complexFilter([
    // Rescale input stream into stream 'rescaled'
    'scale=640:480[rescaled]',

    // Duplicate rescaled stream 3 times into streams a, b, and c
    {
      filter: 'split', options: '3',
      inputs: 'rescaled', outputs: ['a', 'b', 'c']
    },

    // Create stream 'red' by removing green and blue channels from stream 'a'
    {
      filter: 'lutrgb', options: { g: 0, b: 0 },
      inputs: 'a', outputs: 'red'
    },

    // Create stream 'green' by removing red and blue channels from stream 'b'
    {
      filter: 'lutrgb', options: { r: 0, b: 0 },
      inputs: 'b', outputs: 'green'
    },

    // Create stream 'blue' by removing red and green channels from stream 'c'
    {
      filter: 'lutrgb', options: { r: 0, g: 0 },
      inputs: 'c', outputs: 'blue'
    },

    // Pad stream 'red' to 3x width, keeping the video on the left,
    // and name output 'padded'
    {
      filter: 'pad', options: { w: 'iw*3', h: 'ih' },
      inputs: 'red', outputs: 'padded'
    },

    // Overlay 'green' onto 'padded', moving it to the center,
    // and name output 'redgreen'
    {
      filter: 'overlay', options: { x: 'w', y: 0 },
      inputs: ['padded', 'green'], outputs: 'redgreen'
    },

    // Overlay 'blue' onto 'redgreen', moving it to the right
    {
      filter: 'overlay', options: { x: '2*w', y: 0 },
      inputs: ['redgreen', 'blue'], outputs: 'output'
    },
  ], 'output');

Setting event handlers

Before actually running a command, you may want to set event listeners on it to be notified when it's done. The following events are available:

'start': ffmpeg process started

The start event is emitted just after ffmpeg has been spawned. It is emitted with the full command line used as an argument.

ffmpeg('/path/to/file.avi')
  .on('start', function(commandLine) {
    console.log('Spawned Ffmpeg with command: ' + commandLine);
  });

'codecData': input codec data available

The codecData event is emitted when ffmpeg outputs codec information about its input streams. It is emitted with an object argument with the following keys:

  • format: input format
  • duration: input duration
  • audio: audio codec
  • audio_details: audio encoding details
  • video: video codec
  • video_details: video encoding details
ffmpeg('/path/to/file.avi')
  .on('codecData', function(data) {
    console.log('Input is ' + data.audio + ' audio ' +
      'with ' + data.video + ' video');
  });

'progress': transcoding progress information

The progress event is emitted every time ffmpeg reports progress information. It is emitted with an object argument with the following keys:

  • frames: total processed frame count
  • currentFps: framerate at which FFmpeg is currently processing
  • currentKbps: throughput at which FFmpeg is currently processing
  • targetSize: current size of the target file in kilobytes
  • timemark: the timestamp of the current frame in seconds
  • percent: an estimation of the progress percentage

Note that percent can be (very) inaccurate, as the only progress information fluent-ffmpeg gets from ffmpeg is the total number of frames written (and the corresponding duration). To estimate percentage, fluent-ffmpeg has to guess what the total output duration will be, and uses the first input added to the command to do so. In particular:

  • percentage is not available when using an input stream
  • percentage may be wrong when using multiple inputs with different durations and the first one is not the longest
ffmpeg('/path/to/file.avi')
  .on('progress', function(progress) {
    console.log('Processing: ' + progress.percent + '% done');
  });

'stderr': FFmpeg output

The stderr event is emitted every time FFmpeg outputs a line to stderr. It is emitted with a string containing the line of stderr (minus trailing new line characters).

ffmpeg('/path/to/file.avi')
  .on('stderr', function(stderrLine) {
    console.log('Stderr output: ' + stderrLine);
  });

'error': transcoding error

The error event is emitted when an error occurs when running ffmpeg or when preparing its execution. It is emitted with an error object as an argument. If the error happened during ffmpeg execution, listeners will also receive two additional arguments containing ffmpegs stdout and stderr.

If streams are used for input or output, any errors emitted from these streams will be passed through to this event, attached to the error as inputStreamError and outputStreamError for input and output streams respectively.

Warning: you should always set a handler for the error event, as node's default behaviour when an error event without any listeners is emitted is to output the error to the console and terminate the program.

ffmpeg('/path/to/file.avi')
  .on('error', function(err, stdout, stderr) {
    console.log('Cannot process video: ' + err.message);
  });

'end': processing finished

The end event is emitted when processing has finished. Listeners receive ffmpeg standard output and standard error as arguments, except when generating thumbnails (see below), in which case they receive an array of the generated filenames.

ffmpeg('/path/to/file.avi')
  .on('end', function(stdout, stderr) {
    console.log('Transcoding succeeded !');
  });

stdout is empty when the command outputs to a stream. Both stdout and stderr are limited by the stdoutLines option (defaults to 100 lines).

Starting FFmpeg processing

save(filename): save the output to a file

Aliases: saveToFile()

Starts ffmpeg processing and saves the output to a file.

ffmpeg('/path/to/file.avi')
  .videoCodec('libx264')
  .audioCodec('libmp3lame')
  .size('320x240')
  .on('error', function(err) {
    console.log('An error occurred: ' + err.message);
  })
  .on('end', function() {
    console.log('Processing finished !');
  })
  .save('/path/to/output.mp4');

Note: the save() method is actually syntactic sugar for calling both output() and run().

pipe([stream], [options]): pipe the output to a writable stream

Aliases: stream(), writeToStream().

Starts processing and pipes ffmpeg output to a writable stream. The options argument, if present, is passed to ffmpeg output stream's pipe() method (see nodejs documentation).

var outStream = fs.createWriteStream('/path/to/output.mp4');

ffmpeg('/path/to/file.avi')
  .videoCodec('libx264')
  .audioCodec('libmp3lame')
  .size('320x240')
  .on('error', function(err) {
    console.log('An error occurred: ' + err.message);
  })
  .on('end', function() {
    console.log('Processing finished !');
  })
  .pipe(outStream, { end: true });

When no stream argument is present, the pipe() method returns a PassThrough stream, which you can pipe to somewhere else (or just listen to events on).

Note: this is only available with node >= 0.10.

var command = ffmpeg('/path/to/file.avi')
  .videoCodec('libx264')
  .audioCodec('libmp3lame')
  .size('320x240')
  .on('error', function(err) {
    console.log('An error occurred: ' + err.message);
  })
  .on('end', function() {
    console.log('Processing finished !');
  });

var ffstream = command.pipe();
ffstream.on('data', function(chunk) {
  console.log('ffmpeg just wrote ' + chunk.length + ' bytes');
});

Note: the stream() method is actually syntactic sugar for calling both output() and run().

run(): start processing

Aliases: exec(), execute().

This method is mainly useful when producing multiple outputs (otherwise the save() or stream() methods are more straightforward). It starts processing with the specified outputs.

Warning: do not use run() when calling other processing methods (eg. save(), pipe() or screenshots()).

ffmpeg('/path/to/file.avi')
  .output('screenshot.png')
  .noAudio()
  .seek('3:00')

  .output('small.avi')
  .audioCodec('copy')
  .size('320x200')

  .output('big.avi')
  .audioCodec('copy')
  .size('640x480')

  .on('error', function(err) {
    console.log('An error occurred: ' + err.message);
  })
  .on('end', function() {
    console.log('Processing finished !');
  })
  .run();

mergeToFile(filename, tmpdir): concatenate multiple inputs

Use the input and mergeToFile methods on a command to concatenate multiple inputs to a single output file. The mergeToFile needs a temporary folder as its second argument.

ffmpeg('/path/to/part1.avi')
  .input('/path/to/part2.avi')
  .input('/path/to/part2.avi')
  .on('error', function(err) {
    console.log('An error occurred: ' + err.message);
  })
  .on('end', function() {
    console.log('Merging finished !');
  })
  .mergeToFile('/path/to/merged.avi', '/path/to/tempDir');

screenshots(options[, dirname]): generate thumbnails

Aliases: thumbnail(), thumbnails(), screenshot(), takeScreenshots().

Use the screenshots method to extract one or several thumbnails and save them as PNG files. There are a few caveats with this implementation, though:

  • It will not work on input streams.
  • Progress information reported by the progress event is not accurate.
  • It doesn't interract well with filters. In particular, don't use the size() method to resize thumbnails, use the size option instead.

The options argument is an object with the following keys:

  • folder: output folder for generated image files. Defaults to the current folder.
  • filename: output filename pattern (see below). Defaults to "tn.png".
  • count: specifies how many thumbnails to generate. When using this option, thumbnails are generated at regular intervals in the video (for example, when requesting 3 thumbnails, at 25%, 50% and 75% of the video length). count is ignored when timemarks or timestamps is specified.
  • timemarks or timestamps: specifies an array of timestamps in the video where thumbnails should be taken. Each timestamp may be a number (in seconds), a percentage string (eg. "50%") or a timestamp string with format "hh:mm:ss.xxx" (where hours, minutes and milliseconds are both optional).
  • size: specifies a target size for thumbnails (with the same format as the .size() method). Note: you should not use the .size() method when generating thumbnails.

The filename option specifies a filename pattern for generated files. It may contain the following format tokens:

  • '%s': offset in seconds
  • '%w': screenshot width
  • '%h': screenshot height
  • '%r': screenshot resolution (same as '%wx%h')
  • '%f': input filename
  • '%b': input basename (filename w/o extension)
  • '%i': index of screenshot in timemark array (can be zero-padded by using it like %000i)

If multiple timemarks are passed and no variable format token ('%s' or '%i') is specified in the filename pattern, _%i will be added automatically.

When generating thumbnails, an additional filenames event is dispatched with an array of generated filenames as an argument.

ffmpeg('/path/to/video.avi')
  .on('filenames', function(filenames) {
    console.log('Will generate ' + filenames.join(', '))
  })
  .on('end', function() {
    console.log('Screenshots taken');
  })
  .screenshots({
    // Will take screens at 20%, 40%, 60% and 80% of the video
    count: 4,
    folder: '/path/to/output'
  });

ffmpeg('/path/to/video.avi')
  .screenshots({
    timestamps: [30.5, '50%', '01:10.123'],
    filename: 'thumbnail-at-%s-seconds.png',
    folder: '/path/to/output',
    size: '320x240'
  });

Controlling the FFmpeg process

kill([signal='SIGKILL']): kill any running ffmpeg process

This method sends signal (defaults to 'SIGKILL') to the ffmpeg process. It only has sense when processing has started. Sending a signal that terminates the process will result in the error event being emitted.

var command = ffmpeg('/path/to/video.avi')
  .videoCodec('libx264')
  .audioCodec('libmp3lame')
  .on('start', function() {
    // Send SIGSTOP to suspend ffmpeg
    command.kill('SIGSTOP');

    doSomething(function() {
      // Send SIGCONT to resume ffmpeg
      command.kill('SIGCONT');
    });
  })
  .save('/path/to/output.mp4');

// Kill ffmpeg after 60 seconds anyway
setTimeout(function() {
  command.on('error', function() {
    console.log('Ffmpeg has been killed');
  });

  command.kill();
}, 60000);

renice([niceness=0]): change ffmpeg process priority

This method alters the niceness (priority) value of any running ffmpeg process (if any) and any process spawned in the future. The niceness parameter may range from -20 (highest priority) to 20 (lowest priority) and defaults to 0 (which is the default process niceness on most *nix systems).

Note: this method is ineffective on Windows platforms.

// Set startup niceness
var command = ffmpeg('/path/to/file.avi')
  .renice(5)
  .save('/path/to/output.mp4');

// Command takes too long, raise its priority
setTimeout(function() {
  command.renice(-5);
}, 60000);

Reading video metadata

You can read metadata from any valid ffmpeg input file with the modules ffprobe method.

ffmpeg.ffprobe('/path/to/file.avi', function(err, metadata) {
    console.dir(metadata);
});

You may also call the ffprobe method on an FfmpegCommand to probe one of its input. You may pass a 0-based input number as a first argument to specify which input to read metadata from, otherwise the method will probe the last added input.

ffmpeg('/path/to/file1.avi')
  .input('/path/to/file2.avi')
  .ffprobe(function(err, data) {
    console.log('file2 metadata:');
    console.dir(data);
  });

ffmpeg('/path/to/file1.avi')
  .input('/path/to/file2.avi')
  .ffprobe(0, function(err, data) {
    console.log('file1 metadata:');
    console.dir(data);
  });

Warning: ffprobe may be called with an input stream, but in this case it will consume data from the stream, and this data will no longer be available for ffmpeg. Using both ffprobe and a transcoding command on the same input stream will most likely fail unless the stream is a live stream. Only do this if you know what you're doing.

The returned object is the same that is returned by running the following command from your shell (depending on your ffmpeg version you may have to replace -of with -print_format) :

$ ffprobe -of json -show_streams -show_format /path/to/file.avi

It will contain information about the container (as a format key) and an array of streams (as a stream key). The format object and each stream object also contains metadata tags, depending on the format:

{
  "streams": [
    {
      "index": 0,
      "codec_name": "h264",
      "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
      "profile": "Constrained Baseline",
      "codec_type": "video",
      "codec_time_base": "1/48",
      "codec_tag_string": "avc1",
      "codec_tag": "0x31637661",
      "width": 320,
      "height": 180,
      "has_b_frames": 0,
      "sample_aspect_ratio": "1:1",
      "display_aspect_ratio": "16:9",
      "pix_fmt": "yuv420p",
      "level": 13,
      "r_frame_rate": "24/1",
      "avg_frame_rate": "24/1",
      "time_base": "1/24",
      "start_pts": 0,
      "start_time": "0.000000",
      "duration_ts": 14315,
      "duration": "596.458333",
      "bit_rate": "702655",
      "nb_frames": "14315",
      "disposition": {
        "default": 0,
        "dub": 0,
        "original": 0,
        "comment": 0,
        "lyrics": 0,
        "karaoke": 0,
        "forced": 0,
        "hearing_impaired": 0,
        "visual_impaired": 0,
        "clean_effects": 0,
        "attached_pic": 0
      },
      "tags": {
        "creation_time": "1970-01-01 00:00:00",
        "language": "und",
        "handler_name": "\fVideoHandler"
      }
    },
    {
      "index": 1,
      "codec_name": "aac",
      "codec_long_name": "AAC (Advanced Audio Coding)",
      "codec_type": "audio",
      "codec_time_base": "1/48000",
      "codec_tag_string": "mp4a",
      "codec_tag": "0x6134706d",
      "sample_fmt": "fltp",
      "sample_rate": "48000",
      "channels": 2,
      "bits_per_sample": 0,
      "r_frame_rate": "0/0",
      "avg_frame_rate": "0/0",
      "time_base": "1/48000",
      "start_pts": 0,
      "start_time": "0.000000",
      "duration_ts": 28619776,
      "duration": "596.245333",
      "bit_rate": "159997",
      "nb_frames": "27949",
      "disposition": {
        "default": 0,
        "dub": 0,
        "original": 0,
        "comment": 0,
        "lyrics": 0,
        "karaoke": 0,
        "forced": 0,
        "hearing_impaired": 0,
        "visual_impaired": 0,
        "clean_effects": 0,
        "attached_pic": 0
      },
      "tags": {
        "creation_time": "1970-01-01 00:00:00",
        "language": "und",
        "handler_name": "\fSoundHandler"
      }
    }
  ],
  "format": {
    "filename": "http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4",
    "nb_streams": 2,
    "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
    "format_long_name": "QuickTime / MOV",
    "start_time": "0.000000",
    "duration": "596.459000",
    "size": "64657027",
    "bit_rate": "867211",
    "tags": {
      "major_brand": "isom",
      "minor_version": "512",
      "compatible_brands": "mp41",
      "creation_time": "1970-01-01 00:00:00",
      "title": "Big Buck Bunny",
      "artist": "Blender Foundation",
      "composer": "Blender Foundation",
      "date": "2008",
      "encoder": "Lavf52.14.0"
    }
  }
}

Querying ffmpeg capabilities

fluent-ffmpeg enables you to query your installed ffmpeg version for supported formats, codecs, encoders and filters.

var Ffmpeg = require('fluent-ffmpeg');

Ffmpeg.getAvailableFormats(function(err, formats) {
  console.log('Available formats:');
  console.dir(formats);
});

Ffmpeg.getAvailableCodecs(function(err, codecs) {
  console.log('Available codecs:');
  console.dir(codecs);
});

Ffmpeg.getAvailableEncoders(function(err, encoders) {
  console.log('Available encoders:');
  console.dir(encoders);
});

Ffmpeg.getAvailableFilters(function(err, filters) {
  console.log("Available filters:");
  console.dir(filters);
});

// Those methods can also be called on commands
new Ffmpeg({ source: '/path/to/file.avi' })
  .getAvailableCodecs(...);

These methods pass an object to their callback with keys for each available format, codec or filter.

The returned object for formats looks like:

{
  ...
  mp4: {
    description: 'MP4 (MPEG-4 Part 14)',
    canDemux: false,
    canMux: true
  },
  ...
}
  • canDemux indicates whether ffmpeg is able to extract streams from (demux) this format
  • canMux indicates whether ffmpeg is able to write streams into (mux) this format

The returned object for codecs looks like:

{
  ...
  mp3: {
    type: 'audio',
    description: 'MP3 (MPEG audio layer 3)',
    canDecode: true,
    canEncode: true,
    intraFrameOnly: false,
    isLossy: true,
    isLossless: false
  },
  ...
}
  • type indicates the codec type, either "audio", "video" or "subtitle"
  • canDecode tells whether ffmpeg is able to decode streams using this codec
  • canEncode tells whether ffmpeg is able to encode streams using this codec

Depending on your ffmpeg version (or if you use avconv instead) other keys may be present, for example:

  • directRendering tells if codec can render directly in GPU RAM; useless for transcoding purposes
  • intraFrameOnly tells if codec can only work with I-frames
  • isLossy tells if codec can do lossy encoding/decoding
  • isLossless tells if codec can do lossless encoding/decoding

With some ffmpeg/avcodec versions, the description includes encoder/decoder mentions in the form "Foo codec (decoders: libdecodefoo) (encoders: libencodefoo)". In this case you will want to use those encoders/decoders instead (the codecs object returned by getAvailableCodecs will also include them).

The returned object for encoders looks like:

{
  ...
  libmp3lame: {
    type: 'audio',
    description: 'MP3 (MPEG audio layer 3) (codec mp3)',
    frameMT: false,
    sliceMT: false,
    experimental: false,
    drawHorizBand: false,
    directRendering: false
  },
  ...
}
  • type indicates the encoder type, either "audio", "video" or "subtitle"
  • experimental indicates whether the encoder is experimental. When using such a codec, fluent-ffmpeg automatically adds the '-strict experimental' flag.

The returned object for filters looks like:

{
  ...
  scale: {
    description: 'Scale the input video to width:height size and/or convert the image format.',
    input: 'video',
    multipleInputs: false,
    output: 'video',
    multipleOutputs: false
  },
  ...
}
  • input tells the input type this filter operates on, one of "audio", "video" or "none". When "none", the filter likely generates output from nothing
  • multipleInputs tells whether the filter can accept multiple inputs
  • output tells the output type this filter generates, one of "audio", "video" or "none". When "none", the filter has no output (sink only)
  • multipleInputs tells whether the filter can generate multiple outputs

Cloning an FfmpegCommand

You can create clones of an FfmpegCommand instance by calling the clone() method. The clone will be an exact copy of the original at the time it has been called (same inputs, same options, same event handlers, etc.). This is mainly useful when you want to apply different processing options on the same input.

Setting options, adding inputs or event handlers on a clone will not affect the original command.

// Create a command to convert source.avi to MP4
var command = ffmpeg('/path/to/source.avi')
  .audioCodec('libfaac')
  .videoCodec('libx264')
  .format('mp4');

// Create a clone to save a small resized version
command.clone()
  .size('320x200')
  .save('/path/to/output-small.mp4');

// Create a clone to save a medium resized version
command.clone()
  .size('640x400')
  .save('/path/to/output-medium.mp4');

// Save a converted version with the original size
command.save('/path/to/output-original-size.mp4');

Contributing

Contributions in any form are highly encouraged and welcome! Be it new or improved presets, optimized streaming code or just some cleanup. So start forking!

Code contributions

If you want to add new features or change the API, please submit an issue first to make sure no one else is already working on the same thing and discuss the implementation and API details with maintainers and users by creating an issue. When everything is settled down, you can submit a pull request.

When fixing bugs, you can directly submit a pull request.

Make sure to add tests for your features and bugfixes and update the documentation (see below) before submitting your code!

Documentation contributions

You can directly submit pull requests for documentation changes. Make sure to regenerate the documentation before submitting (see below).

Updating the documentation

When contributing API changes (new methods for example), be sure to update the README file and JSDoc comments in the code. fluent-ffmpeg comes with a plugin that enables two additional JSDoc tags:

  • @aliases: document method aliases
/**
 * ...
 * @method FfmpegCommand#myMethod
 * @aliases myMethodAlias,myOtherMethodAlias
 */
  • @category: set method category
/**
 * ...
 * @category Audio
 */

You can regenerate the JSDoc documentation by running the following command:

$ make doc

To avoid polluting the commit history, make sure to only commit the regenerated JSDoc once and in a specific commit.

Running tests

To run unit tests, first make sure you installed npm dependencies (run npm install).

$ make test

Make sure your ffmpeg installation is up-to-date to prevent strange assertion errors because of missing codecs/bugfixes.

Main contributors

License

(The MIT License)

Copyright (c) 2011 Stefan Schaermeli <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

FOSSA Status

node-fluent-ffmpeg's People

Contributors

bencevans avatar brunoceleste avatar btmdave avatar carles-vancast avatar ejholmes avatar enobrev avatar eralpkaraduman avatar freezy avatar jdp avatar jlank avatar kusmayadi avatar navilan avatar nickdesaulniers avatar njoyard avatar oohnoitz avatar rhodgkins avatar riophae avatar saschagehlich avatar schaermu avatar sergiokas avatar smremde avatar stf1981 avatar tbasse avatar tee-yankov avatar timdp avatar tommedema avatar tony-0tis avatar wltsmrz avatar xaiki avatar xedsvg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-fluent-ffmpeg's Issues

Allow streaming input rather than static file

The library expects this kind of input:
new ffmpeg('/path/to/your_movie.avi')

Thus, it expects the movie/audio to be stored on the server. When receiving an audio stream, it makes no sense to store this on the server if the goal is to immediately pipe it to another client. This is exactly what I need to do.

Instead of having to give a static file, I need the ability to input a read stream or supply ordered chunks whenever I have them.

Would this be difficult to implement?

Add ability to export Calculate module

I want to use the calculate module like the metadata module since resizing ogv videos currently breaks with ffmpeg (so I've found). I tested out video resizing with ffmpeg2theora and it works great, so I want to be able to use the nice API provided by fluent to dynamically calculate the video resolutions (a-la 420x?, x?200, 50%). I already did a rough cut of this as a proof of concept, you can see it here. I'm going to clean it up and send a PR, I think this is the easiest solution for ogv video's right now. That is unless you want to incorporate ffmpeg2theora to fluent in the future, I'm not sure what else that program offers though, I'm not too familiar with it.

streaming not ending correctly

When using the sample express-stream.js with NodeJS 0.4.9 or latest version and ffmpeg latest release or from latest code, with latest express, on Ubuntu 10.04, there are few issues:

  • superfast preset doesn't exist
  • in processor.js in writeToStream(), stream.fd is always undefined, which causes an error on exist of ffmpegProc on line 122: fs.close(stream.fd,...)
  • flashvideo preset withSize('320x?') doesn't work unless one specify an height eg 320x200.

This makes me think: Is the lib code aligned with this sample?

Thanks,

Mike

takeScreenshots to return filenames

I use the takeScreenshots feature but I find it slightly inconvenient to use. I have to namespace my files in a dictionary since I have no option to supply a prefix for the files to make them unique. This causes me to make/delete directories for all videos. I would like to be able to pass in an optional file name prefix to solve this.
Then when the screenshot(s) are generated I need to find out what their name are. The naming formula is rather simple but even if I ask for specific times or know the duration of the video there is no guaranty you can know what the file name will be. I would suggest that the takeScreenshots callback is invoked with a second argument containing a list of file names generated.

Thanks

Add support for Multithreading

Hi,

FFmpeg has built-in support for multithreading using "-thread ", so an option like .withThreads(n) that passes "-thread " to FFmpeg would do the job.

Regards,
Krenar

Maybe package.json is broken

Hello,

We are recently updated our node modules linking the latest version of [email protected].

Unfortunately, when I try to start my nodejs application, I get an error. Nodejs is not able to find the "fluent-ffmpeg" module.

I see the package.json file, and it seems to be correct, but I saw that in the package.json file the "main" key is set to "index" but my tree has not an index.js file in the root folder of fluent-ffmpeg.

Could you please have a look into this?

Maybe the npm remote module has temporary broken?

Thanks in advance

.addOption params

I recently updated fffmpeg. My addOptions now don't work. ffmpeg can't parse them. Can someone tell me what I'm doing wrong here?

    proc.addOptions [
        '-y', '-ac 2', '-ar 48000', '-bf 0', '-refs 1', '-wpred-dct8x8',  '-flags2'
        '-level 30', '-maxrate 10M', '-bufsize 10M' 
        '-g ' + (Math.ceil origVideoMeta.video.fps)
    ]

Version 0.3.0 broken

I did a npm install fluent-ffmpeg on Ubuntu 10.10, and this version throws the following error when I tried a simple mp3 to ogg conversion.

Error: Source is not a ReadableStream
    at new FfmpegCommand (/home/kishore/Others/app/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:21:13)
    at new <anonymous> (/home/kishore/Others/app/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:241:10)
    at convertFile (/home/kishore/Others/app/test.js:6:16)
    at Object.<anonymous> (/home/kishore/Others/app/test.js:23:1)
    at Module._compile (module.js:441:26)
    at Object..js (module.js:459:10)
    at Module.load (module.js:348:31)
    at Function._load (module.js:308:12)
    at Array.0 (module.js:479:10)
    at EventEmitter._tickCallback (node.js:192:40)

This is the simple program I tried (modified from one of the examples):

var fs = require('fs'),
    ffmpeg = require('fluent-ffmpeg');

function convertFile(fPath) {
    console.log('Converting file: '+fPath);
    var proc = new ffmpeg({
        // input source, required
        source: fPath,
        // timout of the spawned ffmpeg sub-processes in seconds (optional, defaults to 30)
        timeout: 30,
        // default priority for all ffmpeg sub-processes (optional, defaults to 0 which is no priorization)
        priority: 0,
        // set a custom [winston](https://github.com/flatiron/winston) logging instance (optional, default null which will cause fluent-ffmpeg to spawn a winston console logger)
        logger: null,
        // completely disable logging (optional, defaults to false)
        nolog: false
    });
    proc.saveToFile(fPath.substr(0, fPath.lastIndexOf('.')) + '.ogg', function(retcode, error) {
        console.log('file has been converted succesfully');
    });
}

convertFile('/home/kishore/test.mp3');

I believe the examples do not work on 0.3.0? I then did a npm install of the master tarball and the file conversion worked.

video not centered after padding

I'm not sure if this happens with all padded video but my output is not centered (majority of the black padding is on the right) when:

input = 270x480

.withAspect('16:9')
.withSize('?x480')
.applyAutopadding(true)

Add logging facility

Has to be able to accept most common loggers as a facility, no custom implementation.

implement adapters for

  • log4js-node
  • Winston
  • ..proposals welcome...

How to run ffmpeg synchro?

It seems that node-fluent-ffmpeg runs ffmpeg async,
so the writeToStream() of processor.js runs after my main.js is over.

I want to run ffmpeg with standard input, and bring data after.
but writeToStream() runs after others!

comments?

Piping from a http.request stream looses data

Since _prepare does asynchronous operations before piping the input stream to ffmpeg stdin, some data events are missed. The stream is being paused but that does not guarantee data events won't happen as discussed here.

The solution is to buffer the data events in memory while _prepare does its thing.

writeToStream callback err parameter cannot be used to detect actual errors

I'm posting this as an issue, but I am willing to help you out as well with a pull request. I first want to know if you agree on this issue, or maybe you prefer to do it yourself if so.

The callback is called both on a successful conversion and on a failure. Normally, one would do something like if (err) { // conversion failed somehow }. At the moment, it is not possible to programatically detect a failed convertion because the error object simply contains all data send to ffmpeg's process' stderr channel.

Unfortunately the data send to stderr is almost not parseable, as it contains all kinds of debugging information rather than something like {conversionStatus : 0, msg : 'conversion failed', reason: 'libvorbis library not found'}.

For me it is of crucial importance to be able to detect whether conversion succeeded, as I need to inform my users if not (even better would be if I could tell them why not, although this is not mandatory).

Do you agree this is an issue? Do you have any ideas or suggestions on how this could be improved?

Source is not a ReadableStream

I did an NPM install of fluent-ffmpeg on both OSX lion and CentOS.

I've tried all the examples with no success:

Error: Source is not a ReadableStream
at new FfmpegCommand (/home/alloyking/nodeJS/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:20:13)
at new (/home/alloyking/nodeJS/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:240:10)
at Object. (/home/alloyking/nodeJS/app.js:3:12)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)

The only thing that I has not produced an error is when pulling Metadata.
So I built my own preset that simply converted an AVI to an MP4. I tested the script in the terminal with success, but within fluent-ffmpeg it still shows not a ReadableStream.

Any advice?

Question: Capture Webcam / dshow Input

In the examples, every case given is with a static file. Is there any way to feed in a "dshow" source into node-fluent-ffmpeg and use it to kick off a live streaming session?

meta data does not contain time even if it does.

Hey somehow i dont get it to work properly. My ffmpeg was an installation for airVideo, so maybe that's the problem? Can you look over it and maybe say what i'm doing wrong?

Thanks for your great engagement to do a cool wrapper.

Spruce

Error thrown of actual code
Error: meta data contains no duration, aborting screenshot creation
at module.exports.takeScreenshots.screenshotcount (/home/spruce/fbrowser/node_modules/fluent-ffmpeg/lib/processor.js:345:27)
at FfmpegCommand._prepare (/home/spruce/fbrowser/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:297:9)
at module.exports._loadDataInternal (/home/spruce/fbrowser/node_modules/fluent-ffmpeg/lib/metadata.js:165:7)
at ChildProcess.exithandler (child_process.js:544:7)
at ChildProcess.EventEmitter.emit (events.js:99:17)
at maybeClose (child_process.js:638:16)
at Process._handle.onexit (child_process.js:680:5)

your example of getting meta info

{ ffmpegversion: 0,
title: '',
artist: '',
album: '',
track: '',
date: '',
durationraw: '',
durationsec: 0,
synched: false,
major_brand: undefined,
video:
{ container: '',
bitrate: 0,
codec: '',
resolution: { w: 0, h: 0 },
resolutionSquare: { w: 0, h: NaN },
rotate: 0,
fps: 0,
stream: 0,
aspect: 0,
pixel: 0 },
audio: { codec: '', bitrate: 0, sample_rate: 0, stream: 0 } }

Output off ffmpeg -i filmname.mkv

ffmpeg version UNKNOWN, Copyright (c) 2000-2011 the FFmpeg developers
built on Apr 27 2012 01:14:46 with gcc 4.4.5
configuration: --enable-pthreads --disable-shared --enable-static --enable-gpl --enable-libx264 --enable-libmp3lame --disable-decoder=aac
libavutil 51. 2. 1 / 51. 2. 1
libavcodec 53. 3. 0 / 53. 3. 0
libavformat 53. 0. 3 / 53. 0. 3
libavdevice 53. 0. 0 / 53. 0. 0
libavfilter 2. 4. 0 / 2. 4. 0
libswscale 0. 14. 0 / 0. 14. 0
[matroska,webm @ 0x1cecd60] Estimating duration from bitrate, this may be inaccurate
Input #0, matroska,webm, from 'filmname.mkv':
Duration: 01:23:05.53, start: 0.000000, bitrate: 1760 kb/s
Chapter #0.0: start 0.000000, end 242.283000
Metadata:
title : Chapter.01
Chapter #0.1: start 242.283000, end 614.322000
Metadata:
title : Chapter.02
Chapter #0.2: start 614.322000, end 947.905000
Metadata:
title : Chapter.03
Chapter #0.3: start 947.905000, end 1226.642000
Metadata:
title : Chapter.04
Chapter #0.4: start 1226.642000, end 1627.000000
Metadata:
title : Chapter.05
Chapter #0.5: start 1627.000000, end 1819.984000
Metadata:
title : Chapter.06
Chapter #0.6: start 1819.984000, end 2103.017000
Metadata:
title : Chapter.07
Chapter #0.7: start 2103.017000, end 2470.926000
Metadata:
title : Chapter.08
Chapter #0.8: start 2470.926000, end 2714.712000
Metadata:
title : Chapter.09
Chapter #0.9: start 2714.712000, end 2994.449000
Metadata:
title : Chapter.10
Chapter #0.10: start 2994.449000, end 3241.821000
Metadata:
title : Chapter.11
Chapter #0.11: start 3241.821000, end 3522.185000
Metadata:
title : Chapter.12
Chapter #0.12: start 3522.185000, end 3819.232000
Metadata:
title : Chapter.13
Chapter #0.13: start 3819.232000, end 4073.778000
Metadata:
title : Chapter.14
Chapter #0.14: start 4073.778000, end 4491.737000
Metadata:
title : Chapter.15
Chapter #0.15: start 4491.737000, end 4491.737000
Metadata:
title : Chapter.16
Stream #0.0: Video: h264 (High), yuv420p, 1280x534 [PAR 1:1 DAR 640:267], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc
Stream #0.1(ger): Audio: ac3, 48000 Hz, stereo, s16, 224 kb/s (default)
Metadata:
title : 2.0 AC3MD THX to TwixX
Stream #0.2(eng): Audio: dca (DTS), 48000 Hz, 5.1, s16, 1536 kb/s
At least one output file must be specified

warn: -99 timeout

var ffmpeg = require('fluent-ffmpeg'),
        proc;

proc = new ffmpeg({ source: '/Users/nicholas/Music/KYPO.aif' })
    .withAudioCodec('libmp3lame')
    .withAudioBitrate('64k')
    .withAudioChannels(2);

proc.saveToFile('/Users/nicholas/KYPO.mp3', function(retCode, err) {
    console.log(retCode);
    console.log(err);
});

The code above times out, sending a -99 WARN error.

more flexible options

Is there a way to use any arbitrary option when processing? For example, I don't see an option for transposition, and It would be nice if I could just pass in -vf transpose=2.

Not using sync I/O anywhere

I've seen a few places where it's using synchronous I/O read (to load the presets for example), is there any rewrite of these planned?

normalize paths

Some fluent-ffmpeg functions (saveToFile or when creating Metadata object) work incorrectly with not normalized file paths on Windows (when you use slashes in path instead of backslashes), so user need to normalize it explicitly when passing to the functions.

Maybe it is better to use path.normalize inside fluent-ffmpeg.

add -loop [1] and -t [time] parameters

To convert corectly image and audiotrack to video I use the following command:

ffmpeg -loop 1 -i videoimage.jpg -i audio.mp3 -t 00:06:46.81 video.mp4 (ffmpeg --i videoimage.jpg -i audio.mp3 video.mp4 doesn't work for me)

How to implement this command with fluent-ffmpeg API?

Streaming example doesn't work

The error message I get is:

child_process.js:413
      throw new Error('customFds not yet supported');
            ^
Error: customFds not yet supported
    at setStreamOption (child_process.js:413:13)
    at ChildProcess.spawn (child_process.js:425:3)
    at child_process.js:343:9
    at FfmpegCommand._spawnProcess (***/node_modules/fluent-ffmpeg/lib/processor.js:278:19)
    at ***/node_modules/fluent-ffmpeg/lib/processor.js:90:33
    at ***/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:221:9
    at ***/node_modules/fluent-ffmpeg/lib/metadata.js:88:9
    at ChildProcess.exithandler (child_process.js:287:7)
    at ChildProcess.emit (events.js:70:17)
    at maybeExit (child_process.js:361:16)

Tried to pass FD instead of stream, but it didn't work either.

Only need to check for available libraries during initialization

I can create a pull request for this if you like.

At the moment, you are checking for available libraries during every spawn (eg. exec('which flvtool2', ...) while this only has to be done during initialization, after which a boolean can be set (eg. flvtool2Found = true/false).

Missing ascpect ratio on audio transcode.

I'm trying to convert mp3 to ogg with libvorbis.

/***/node_modules/fluent-ffmpeg/lib/metadata.js:80
        ret.video.aspectString = aspect[1];
                                       ^
TypeError: Cannot read property '1' of null
    at /var/www/player/node_modules/fluent-ffmpeg/lib/metadata.js:80:40
    at ChildProcess.exithandler (child_process.js:287:7)

version 0.3.0 causing node to peak at 100% on require

Wish I had more info about what is going on but the title about says it. Whenever I require it, it seems to work properly but it seems as if it doesnt "close" a connection or process as node will peak at 100% and disallow any further interaction.

conversion test fails.

/home/andre/node-fluent-ffmpeg/test/conversion.test.js:41
test.ok(stats.size > 0);
^
TypeError: Cannot read property 'size' of undefined

the express streaming example also fails, the player shows stream not found, and browsing directly to the stream link downloads a 0 bytes file.

-using node v0.6.13
-using latest version of fluent-ffmpeg
-using debian squeeze with multimedia repo.


ffmpeg version 0.7.12, Copyright (c) 2000-2011 the FFmpeg developers
built on Apr 11 2012 07:40:07 with gcc 4.4.5
configuration: --enable-libdc1394 --prefix=/usr --extra-cflags='-Wall -g ' --cc='ccache cc' --enable-shared --enable-libmp3lame --enable-gpl --enable-libvorbis --enable-pthreads --enable-libfaac --enable-libxvid --enable-postproc --enable-x11grab --enable-libgsm --enable-libtheora --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libx264 --enable-libspeex --enable-nonfree --disable-stripping --enable-avfilter --enable-libdirac --disable-decoder=libdirac --enable-libfreetype --enable-libschroedinger --disable-encoder=libschroedinger --enable-version3 --enable-libopenjpeg --enable-libvpx --enable-librtmp --extra-libs=-lgcrypt --disable-altivec --disable-armv5te --disable-armv6 --disable-vis
libavutil 50. 43. 0 / 50. 43. 0
libavcodec 52.123. 0 / 52.123. 0
libavformat 52.111. 0 / 52.111. 0
libavdevice 52. 5. 0 / 52. 5. 0
libavfilter 1. 80. 0 / 1. 80. 0
libswscale 0. 14. 1 / 0. 14. 1
libpostproc 51. 2. 0 / 51. 2. 0

fluent-ffmpeg hangs on Windows

I'm using Windows 7 x64 with node 0.6.15 and I'm trying to get fluent to run in an express project. Whenever I require the fluent-ffmpeg library it maxes out one of my cpu cores and hangs until I restart the server.

I'm trying to use this inside one of my mongoose-js models to create a thumbnail before it saves the model. If I require the library at the top of the file, my app doesn't start.

If I require the library within the creation function the app starts and will execute the thumbnail command and give me what I'm after, but it will hang once this is completed.

Any ideas or anything you need me to do to help debug?

crash in get metadata

/Users/Firejune/Workspace/velox/node_modules/fluent-ffmpeg/lib/processor.js:235
        if (!meta.durationsec) {`
                 ^

TypeError: Cannot read property 'durationsec' of undefined
at module.exports.takeScreenshots.screenshotcount (/Users/Firejune/Workspace/velox/node_modules/fluent-ffmpeg/lib/processor.js:235:18)
at FfmpegCommand._prepare (/Users/Firejune/Workspace/velox/node_modules/fluent-ffmpeg/lib/fluent-ffmpeg.js:287:11)
at module.exports._loadDataInternal (/Users/Firejune/Workspace/velox/node_modules/fluent-ffmpeg/lib/metadata.js:163:7)
at ChildProcess.exithandler (child_process.js:544:7)
at ChildProcess.EventEmitter.emit (events.js:96:17)
at maybeClose (child_process.js:638:16)
at Socket.ChildProcess.spawn.stdin (child_process.js:815:11)
at Socket.EventEmitter.emit (events.js:93:17)
at Socket._destroy.destroyed (net.js:357:10)
at process.startup.processNextTick.process._tickCallback (node.js:244:9)

need to be able to specify full path to ffmpeg

The path to ffmpeg in processor.js is hard coded to ffmpeg. When running node,js from a cron job on a shared server, ffmpeg may not be in a path available to cron and so it can not be run. For example the path to ffmpeg on my shared server is /usr/local/bin/ffmpeg. When the cron job runs, node cannot find ffmpeg because it is not in the path used by cron. I do not have root access to add ffmpeg to the PATH used by cron.

If I replace 'ffmpeg' with '/usr/local/bin/ffmpeg' in processor.js it works.

I would like a method in the api to specify the location of ffmpeg so I don't have to modify processor.js.

Thanks,

Dale

Take screenshots doesn't work

Currently if you run you get an error about input file being undefined. The problem is due to this refering to the global object instead of to the options object.

The fix is to use self instead of this, the author already set var self = this up and just forgot to use it; just a simple typo. The fix will be applied to Processor.js line 242.

Here is the change with some context
https://gist.github.com/1728346

applyAutopadding and withAspect

I found that using both applyAutopadding and withAspect together sometimes results in a 0-byte file. For example:

.withAspect('16:9')
.withSize('?x360')
.applyAutopadding(true)

I'm a little unclear on what exactly the failure conditions are, but when using it on an 852x480 input it fails. At first I assumed it had only to do with the aspect ratio, but I don't get the failure condition when testing with a 640x360 input.

Add option .withNoAudio()

Hi,

FFmpeg has an option to drop audio completely and that's achieved using "-an", so an option like .withNoAudio() that would pass "-an" to FFmpeg would do the job.

Regards,
Krenar

timemark precision

Hi, first thanks for that module, very handy.

I've been trying to get screenshots of some videos but it happens that the result was not enough precise. I found that TimemarkToSeconds function floors the result of the duration.

this.ffmpegTimemarkToSeconds = function(timemark) {

    ...
    // split sec/msec part
    var secParts = parts[2].split('.');

    // add seconds
    secs += parseInt(secParts[0], 10);
'You dont add msec part.'
    return secs;
  };

The problem is that, in the screenshots function you use durationsec to calculate the frame i want so i'm not able to get the last one when i put '100%'

timemarks[i] = (parseInt(timemarks[i], 10) / 100) * meta.durationsec;

Is there any particular reason to drop the milliseconds part in timemarkToSeconds ?

Can't stream .mov files via stdin

I'm working on a project and I'd like to implement an interface where I can pass the stream of a .mov file to fluent and let ffmpeg do it's thing converting/stripping video/etc. However, it doesn't look like ffmpeg supports streaming .mov files via stdin in this way, see this thread: http://ffmpeg-users.933282.n4.nabble.com/STDIN-amp-QuickTime-Movie-Part-Deux-td937297.html. I tried to google around as much as possible, that's all I could find on this topic.

I was thinking about implementing a .mov stream check within fluent, if the stdin is a .mov stream (by checking file extention?), simply pipe the stream to a temporary file, pass it to ffmpeg, let it process, then return a stream. Ideally it would stay a stream the whole way, but since ffmpeg can't handle .mov streams I was thinking this would be an acceptable work around. If this is something you think belongs in fluent I can implement it, otherwise I'll figure something else out in my application logic. At the very least I think it would be helpful to have an error message saying .mov streams on stdin aren't supported, since ffmpeg doesn't really spell that out.

Converting mp3 stream to ogg vorbis

I realize this may not be an issue specific to your project, it's more an issue of mine. If there is a better place to post this please tell me.

Anyway, I have created a simple test project using your library, called NodeFFmpegTest, as you can see here:
https://github.com/tommedema/NodeFFmpegTest/blob/master/main.js

The goal of this test is to provide a demo of converting an incoming audio stream (at the moment mp3) to a writable stream (currently ogg vorbis). This is a very common use case, for example to allow iPad users to listen to music in their browsers using HTML5's audio tag (it supports ogg vorbis, but not mp3).

Please look at the link above to see my simple code. The problem I face is that the input mp3 is about 5MB while by default, the output ogg vorbis file 28.8MB (yet it's supposed to be a highly optimized format for the web).

Strangely enough, the setAudioBitrate option does not seem to have any effect on file size. Could this be a bug? I've also tried setting the -ar option, which had the same result (or lack thereof).

Forcing mono sound (1 channel) instead of 2 did reduce the size to about 14MB, but this should not be necessary. This original MP3 is in stereo as well.

An audio frequency of 22050 did help, it reduced the size to 15.8MB, note that the MP3 apparently has a frequency of 44100Hz. As expected, a frequency of 11025 makes the sound quality horrible.

The -aq option (audio quality) appears to have no effect at all.

Have you experimented with such conversion? Any help would be greatly appreciated.

Slight nice spawning issue

At line 210: https://github.com/schaermu/node-fluent-ffmpeg/blob/master/lib/processor.js#L210

There is a typo '7' at the end of the line, and two other problems:

  1. the renice level is set as the parameter for the nice command, yet nice is an adjustment while renice is a fixed level (you do use renice in the spawnProcess function)
  2. the spawnProcess function should be used here as well

On a side note, can you execute renice immediately without waiting for a callback from the spawn command (if there is such thing)? Aka, does that process exist immediately after calling spawn?

Finally, should there be a check if 'nice' or 'renice' exists on the system during initialization, eg. for when node is available on windows?

No way to concat files?

I'm trying to stitch several videos together, but node-fluent-ffmpeg doesn't seem to be able to perform the concat function. This is what I'm trying to accomplish:

ffmpeg -i input1.avi -qscale:v 1 intermediate1.mpg
ffmpeg -i input2.avi -qscale:v 1 intermediate2.mpg
ffmpeg -i concat:"intermediate1.mpg|intermediate2.mpg" -c copy intermediate_all.mpg
ffmpeg -i intermediate_all.mpg -qscale:v 2 output.avi

converting image to video not working

While I can convert a single image into a video from the command line, nothing that I tried with fluent-ffmpeg worked for me. This is the simple command line call:

ffmpeg -i 'image.jpg' -r 1 out.mp4

It should produce a one second video, but, either I couldn't figure out the proper syntax, or this library is unable to do this.

Audio codec detection while receiving inward stream

I've got a rather complex problem here, which should be solveable in theory, but I am not sure whether ffmpeg allows for this to work out of the box.

Basically, what I need is to get the audio codec as soon as it is detected while having ffmpeg convert an incoming audio stream to a certain format.

The reason I need to get this audio codec while already converting it, is because I can only access this stream once and I do not want to buffer the initial chunks in memory so that I could use these first for audio codec detection and then for conversion.

If I detect a certain audio format, I want to stop the process.

In the end, this is the API that I need. I wonder if you can tell me whether this is possible:

new ffmpeg(inputStream)
.withAudioCodec('libvorbis')
.toFormat('ogg')
.informFileType(function(type, processor) {
    if (type == 'ogg') processor.stop();
})
.writeToStream(outputStream, function(retcode, err) {
});

Note that this also requires a processor object with utility functions, which is why I'd also recommend a different construction API, but this is not compulsory:

var processor = ffmpeg.createProcessor(inputstream)
processor.on('done', function(retcode, err) { });
processor.on('timeout', function(err) { });
processor.on('fileType', function(type) { });
....

Could you tell me whether such informFileType method is possible, or maybe a informAudioCodec and informVideoCodec is better?

.svn folder in presets folder crashes require statement

We keep our node.js server in subversion and the .svn folder was breaking the .forEach loop that includes the presets. (fluent-ffmpeg.js). (Sorry I've never used github, but I wanted to report this so someone that works on it can include it in future releases).

We added this to make it not crash on us:

    fs.readdirSync(__dirname + '/presets').forEach(function(file) {
        var modname = file.substring(0, file.length - 3);

+        if (file.substring(0, 1) == '.') {
+            return true;
+        }

        var preset = require('./presets/' + modname);
        if (typeof preset.load == 'function') {
            presets[modname] = preset;
        }
    });

Edit for markup

Add progress report callback

Figure out some way to report the conversion progress to the user (maybe using stderr scanning for some patterns).

timeout issue

on timeout, callback will be called twice

fixing this right now...

New streaming issues

I'm sure something is still going wrong with the file descriptors. Anyway, the synchronized file descriptor closing methods you were using were causing the following error:

  return binding.close(fd);
                 ^
Error: EBADF, Bad file descriptor
    at Object.closeSync (fs.js:192:18)
    at ChildProcess.<anonymous> (/media/sda3/Workspaces/Node.js/node-fluent-ffmpeg/lib/processor.js:118:18)
    at ChildProcess.emit (events.js:67:17)
    at ChildProcess.onexit (child_process.js:192:12)

Apparently, the fd was properly closed already. I fixed this by making the call asynchronous (which it should be anyway), like so:

     ffmpegProc.on('exit', function(code, signal) {
        if (processTimer) clearTimeout(processTimer);
        // close file descriptor on outstream
        fs.close(stream.fd, function() {
          if (self.options.inputstream) {
            fs.close(self.options.inputstream.fd, function() {
              callback(code, stderr);
            });
          }
          else {
            callback(code, stderr);
          }
        });
      });

Unfortunately a 5Mb mp3 is only converted to 23KB of ogg audio, which plays for about 1 second. The awkward thing is that retcode is 0 indiciating a normal termination of ffmpeg. This is the output:

DEBUG: inputStream end event: stream received EOF, no more data events DEBUG: inputStream close event: file descriptor has been closed DEBUG: input stream written to write stream successfully, err: ffmpeg version git-N-30727-gd127d26, Copyright (c) 2000-2011 the FFmpeg developers built on Jun 12 2011 14:49:21 with gcc 4.4.5 configuration: --disable-encoder=vorbis --enable-libvorbis libavutil 51. 8. 0 / 51. 8. 0 libavcodec 53. 7. 0 / 53. 7. 0 libavformat 53. 3. 1 / 53. 3. 1 libavdevice 53. 1. 1 / 53. 1. 1 libavfilter 2. 15. 1 / 2. 15. 1 libswscale 0. 14. 1 / 0. 14. 1 [NULL @ 0x3251400] Format mp3 detected only with low score of 25, misdetection possible! [mp3 @ 0x32533a0] Header missing [mp3 @ 0x3251400] Estimating duration from bitrate, this may be inaccurate Input #0, mp3, from 'pipe:': Duration: N/A, start: 0.000000, bitrate: 160 kb/s Stream #0.0: Audio: mp3, 44100 Hz, stereo, s16, 160 kb/s Output #0, ogg, to 'pipe:1': Metadata: encoder : Lavf53.3.1 Stream #0.0: Audio: libvorbis, 44100 Hz, stereo, s16, 64 kb/s Stream mapping: Stream #0.0 -> #0.0 [mp3 @ 0x32533a0] Header missing Error while decoding stream #0.0 [mp3 @ 0x32533a0] overread, skip -6 enddists: -2 -2 [mp3 @ 0x32533a0] overread, skip -7 enddists: -5 -5 [mp3 @ 0x32533a0] overread, skip -7 enddists: -3 -3 [mp3 @ 0x32533a0] Header missing Error while decoding stream #0.0 [mp3 @ 0x32533a0] incomplete frame Error while decoding stream #0.0 size= 24kB time=00:00:02.71 bitrate= 72.0kbits/s video:0kB audio:20kB global headers:4kB muxing overhead 1.083495%

You can see my code here: https://github.com/tommedema/NodeFFmpegTest/blob/master/main.js

Note that this is exactly the same input file as I used before, which was handled successfully.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.