fluent-ffmpeg / node-fluent-ffmpeg

A fluent API to FFMPEG (http://www.ffmpeg.org)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is it possible to write multiple output files from a complex filter?

ChrisMICDUP opened this issue · comments

Version information

  • fluent-ffmpeg version: 2.1.2
  • ffmpeg version: 6.1.1
  • OS: MacOS 13.6

Code to reproduce

async function extractClipsWithComplexFilter(id, jsonClips, localMP4File) {
    const startTime = Date.now();

    let i = 0;
    let complexFilterArray = [];
    for (const clip of jsonClips.clips) {
        console.log(`Extracting clip_${i}`);
        complexFilterArray.push(`[0:v]trim=start=${utils.parseTime(clip.startTimestamp)/1000}:end=${utils.parseTime(clip.endTimestamp)/1000},setpts=PTS-STARTPTS[v_clip_${i}]`);
        complexFilterArray.push(`[0:a]atrim=start=${utils.parseTime(clip.startTimestamp) / 1000}:end=${utils.parseTime(clip.endTimestamp) / 1000},asetpts=PTS-STARTPTS[a_clip_${i++}]`);
        break; // only do one clip for now
    }
    let mapArray = [];
    let outputArray = [];
    for (let j = 0; j < i; j++) {
        mapArray.push(`-map '[v_clip_${j}]' -map '[a_clip_${j}]'`);
        outputArray.push(`clip_${j}.mp4`);
    }

    /* This CLI works
     ffmpeg -i test.mp4 -filter_complex
        [0:v]trim=start=1948.605:end=2038.755,setpts=PTS-STARTPTS[v_clip_0];
        [0:a]atrim=start=1948.605:end=2038.755,asetpts=PTS-STARTPTS[a_clip_0];
        ...
        [0:v]trim=start=5041.255:end=5131.045,setpts=PTS-STARTPTS[v_clip_10];
        [0:a]atrim=start=5041.255:end=5131.045,asetpts=PTS-STARTPTS[a_clip_10];
        -map '[v_clip_0]' -map '[a_clip_0]' clip_0.mp4
        ...
        -map '[v_clip_10]' -map '[a_clip_10]' clip_10.mp4
     */
    ffmpeg(localMP4File)
        .complexFilter(complexFilterArray)
        .output('clip_0.mp4')
        .map('[v_clip_0]')
        .map('[a_clip_0]')
        .on('error', error => console.log('Error: ' + error.message))
        .on('end', () => console.log('Success!'))
        .run();
    console.log(`Extracted ${complexFilterArray.length} clips in ${Date.now()-startTime}`);

    return {status: 200} // TODO: failure
}

(note: if the problem only happens with some inputs, include a link to such an input file)

Expected results

I pass a json array of clip information with start and end timestamps. The number of clips could vary from 1-50.

What I want is a way to define an array of outputs and maps at runtime and create a number of clips.

Observed results

A single clip works as above. The ffmpeg command works for multiple clips as above. I've had various attempts to pass arrays to output and map without success.

Checklist

Forgot about ChatGPT... In the spirit of OpenAI ripping off github, I present to you a working example (note the loop on .input and .output):

const extractClipCommand = ffmpeg(); jsonClips.clips.forEach((clip, index) => { extractClipCommand.input(localMP4File); }); let complexFilterArray = []; console.log("create complexFilterArray"); jsonClips.clips.forEach((clip, index) => { complexFilterArray.push(`[0:v]trim=start=${utils.parseTime(clip.startTimestamp)/1000}:end=${utils.parseTime(clip.endTimestamp)/1000},setpts=PTS-STARTPTS[v_clip_${index}]`); complexFilterArray.push(`[0:a]atrim=start=${utils.parseTime(clip.startTimestamp) / 1000}:end=${utils.parseTime(clip.endTimestamp) / 1000},asetpts=PTS-STARTPTS[a_clip_${index}]`); }); extractClipCommand.complexFilter(complexFilterArray); console.log("create output and maps"); // Map the outputs jsonClips.clips.forEach((clip, index) => { extractClipCommand.output(`clip_${index}.mp4`) .map(`[v_clip_${index}]`) .map(`[a_clip_${index}]`); }); // Run the command console.log("Run the command"); extractClipCommand .on('error', function(err) { console.log('An error occurred: ' + err.message); }) .on('end', function() { console.log(`Extracted ${complexFilterArray.length/2} clips in ${Date.now()-startTime}`); }) .run();

I should point out however that with a 1GB mp4 and more than 3 clips, ffmpeg runs out of memory and is killed...