Node-fluent-ffmpeg: Concat/merge files without intermediates

Created on 5 Mar 2015  路  5Comments  路  Source: fluent-ffmpeg/node-fluent-ffmpeg

Hi all,

I was wondering if there's a way to get fluent-ffmpeg to merge files without having to create intermediate files? Or should I just use child_process?

The approximate command I'm trying to achieve is
ffmpeg -i "concat:head.ts|1.ts|2.ts|3.ts|4.ts|tail.ts" -c copy -bsf:a aac_adtstoasc output.mp4
... which i have tested to be very quick and not very intensive since no intermediates are created afaik

My current test code looks something like:

var testfile = '/tmp/test.ts';
var proc = ffmpeg(testfile)
    .input(testfile)
    .input(testfile)
    .videoCodec('libx264')
    .audioCodec('aac')
    .format('mp4')
    .on('start', function (cli) {
        console.log('starting', cli);
    })
    .on('end', function () {
        console.log('end ok);
    })
    .on('error', function (err) {
        console.log('had an error', err);
    })
    .mergeToFile('/tmp/output.mp4', '/tmp/');

... but i notice that the actual command is
ffmpeg -i testfile.ts -i testfile.ts -i testfile.ts -y -filter_complex concat=n=3:v=1:a=1 -acodec aac -strict experimental -vcodec libx264 -f mp4 output.mp4
and since I do run a couple concats in parallel and different bitrates, my system gets bogged down, which worries me if I put this out in our production environment

Thank you

Most helpful comment

yep, it was a year ago, but I didn't find an appropriate answer for this question. So, here is my solution.

Target: run fluent-ffmpeg with ffmpeg -i "concat:1.ts|2.ts" -c copy out.mp4 command

Step 0: preparation

If you have MPEG files - skip this step.

Otherwise, you should convert your videos (ffmpeg-concat-protocol works only with mpg and mpeg transport streams, possibly others):

let inputTsFiles = [];

for (let inputFileName of ['1.mp4', '2.mp4']) {
    let tmpFile = input + '.ts';
    inputTsFiles.push(tmpFile);

    let cmd = ffmpeg()
        .on('start', function(cmdline) {
            console.log('Command line: ' + cmdline);
        })
        .on('progress', function(progress) {
            console.info(`Processing ${outName}: ${progress.percent} % done`);
        })
        .input(input)
        .output(tmpFile)
        .videoCodec('copy')
        .audioCodec('copy')
        .outputOptions('-bsf:v h264_mp4toannexb')
        .outputOptions('-f mpegts')
        .run();
};
// don't forget to wait for results. I used bluebird-promises...

Step 1: Actual concat

We have paths to sources: inputTsFiles = ['1.ts', '2.ts'];. Lets run ffmpeg:

let inputNamesFormatted = 'concat:' + inputTsFiles.join('|');

let cmd = ffmpeg()
    .on('start', function(cmdline) {
        console.log('Command line: ' + cmdline);
    })
    .on('progress', function(progress) {
        console.info(`Processing ${outName}: ${progress.percent} % done`);
    })
    .input(inputNamesFormatted)
    .output(outputName)
    .outputOption('-strict -2')     // I have an issue with experimental codecs, it is a solution
    .outputOption('-bsf:a aac_adtstoasc')
    .videoCodec('copy')
    .run();

Done!

All 5 comments

This command does not create intermediate files. It uses the concat filter to concatenate all the inputs in one go.

I was under the impression that mergeToFile() creates intermediates due to the 2nd argument being a dir for temp files, unless I'm reading outdated documentation

I think I need to phrase my question differently: how do I use the concat protocol as opposed to the concat filter via fluent-ffmpeg? Apologies as I'm not too familiar with ffmpeg, but from what I've read in the ffmpeg concatenating files documentation, the first command I have shown above is using a concat _protocol_, which is what I have used in testing via terminal commands and I think is suited for our needs. Now trying to concat files using node, I end up using the concat _filter_ and looks like internally, it does the concatenation differently, much more resource intensive from what I've observed. Are my observations correct?

Thank you for your time

Indeed the documentation is incorrect. The second argument is an options object in case the first is a WritableStream.

There's no way to use the concat: protocol with fluent-ffmpeg currently. You could try using the concat input format instead, though I'm not sure how it works.

I'm not surprised the concat filter is more resource intensive, as it most likely makes ffmpeg decode all input streams, then concatenate them and finally encode them, whereas the concat: protocol and concat input format simply concatenate inputs before transcoding (which only works when all inputs have the exact same format).

Thank you for clarifying. The segments are all the same format, and all I need is to glue them together. I will just spawn an ffmpeg process to use the concat protocol for my use case.

yep, it was a year ago, but I didn't find an appropriate answer for this question. So, here is my solution.

Target: run fluent-ffmpeg with ffmpeg -i "concat:1.ts|2.ts" -c copy out.mp4 command

Step 0: preparation

If you have MPEG files - skip this step.

Otherwise, you should convert your videos (ffmpeg-concat-protocol works only with mpg and mpeg transport streams, possibly others):

let inputTsFiles = [];

for (let inputFileName of ['1.mp4', '2.mp4']) {
    let tmpFile = input + '.ts';
    inputTsFiles.push(tmpFile);

    let cmd = ffmpeg()
        .on('start', function(cmdline) {
            console.log('Command line: ' + cmdline);
        })
        .on('progress', function(progress) {
            console.info(`Processing ${outName}: ${progress.percent} % done`);
        })
        .input(input)
        .output(tmpFile)
        .videoCodec('copy')
        .audioCodec('copy')
        .outputOptions('-bsf:v h264_mp4toannexb')
        .outputOptions('-f mpegts')
        .run();
};
// don't forget to wait for results. I used bluebird-promises...

Step 1: Actual concat

We have paths to sources: inputTsFiles = ['1.ts', '2.ts'];. Lets run ffmpeg:

let inputNamesFormatted = 'concat:' + inputTsFiles.join('|');

let cmd = ffmpeg()
    .on('start', function(cmdline) {
        console.log('Command line: ' + cmdline);
    })
    .on('progress', function(progress) {
        console.info(`Processing ${outName}: ${progress.percent} % done`);
    })
    .input(inputNamesFormatted)
    .output(outputName)
    .outputOption('-strict -2')     // I have an issue with experimental codecs, it is a solution
    .outputOption('-bsf:a aac_adtstoasc')
    .videoCodec('copy')
    .run();

Done!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TomKaltz picture TomKaltz  路  5Comments

LauraWebdev picture LauraWebdev  路  3Comments

jfrux picture jfrux  路  4Comments

elartix picture elartix  路  5Comments

joergbirkhold picture joergbirkhold  路  5Comments