-
-
Save disco0/90c48c424591219d209bf0ba4392ceac to your computer and use it in GitHub Desktop.
segment & concat with ffmpeg via node + fessonia
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* original command: | |
ffmpeg -i video.mp4 -filter_complex "[0:v]trim=1:2,setpts=PTS-STARTPTS[v0]; [0:a]atrim=1:2,asetpts=PTS-STARTPTS[a0]; [0:v]trim=120:125,setpts=PTS-STARTPTS[v1]; [0:a]atrim=120:125,asetpts=PTS-STARTPTS[a1]; [v0][a0][v1][a1]concat=n=2:v=1:a=1[out]" -map "[out]" output.mp4 | |
*/ | |
/* | |
Rewrite using Fessonia library in node.js | |
Get Fessonia: https://www.npmjs.com/package/@tedconf/fessonia | |
Fessonia Docs: https://tedconf.github.io/fessonia/ | |
*/ | |
const { | |
FFmpegCommand, | |
FFmpegInput, | |
FFmpegOutput, | |
FilterNode, | |
FilterChain | |
} = require('@tedconf/fessonia')({ | |
// options here if you have them... location of ffmpeg binary, etc. | |
}); | |
// trim stream returning stream segment from start to end | |
// default is video stream, unless audio = true | |
function trimStream (start, end, audio = false) { | |
return new FilterNode(`${audio ? 'a' : ''}trim`, { | |
start: start, | |
end: end | |
}); | |
} | |
// Adjust frame presentation timestamps (PTS) by subtracting | |
// the PTS of the first frame in the segment | |
function adjustTimestamps (audio = false) { | |
return new FilterNode(`${audio ? 'a' : ''}setpts`, { | |
expr: 'PTS-STARTPTS' | |
}); | |
} | |
// Concatenate the video (and audio) streams together | |
function concatenateStreams ({ video, audio }) { | |
hasAudio = typeof audio !== 'undefined' && audio.length > 0 | |
if (hasAudio && video.length !== audio.length) { | |
throw new Error('Must have same number of streams of each type.') | |
} | |
const concatNode = new FilterNode('concat', { | |
n: video.length, | |
v: 1, | |
a: hasAudio ? 1 : 0 | |
}); | |
const concatenator = new FilterChain([concatNode]); | |
for (let n = 0; n < video.length; n++) { | |
concatenator.addInput(video[n].streamSpecifier()); | |
if (hasAudio) concatenator.addInput(audio[n].streamSpecifier()); | |
} | |
return concatenator; | |
} | |
const input = new FFmpegInput('video.mp4'); | |
const segment1Video = new FilterChain([trimStream(1, 2), adjustTimestamps()]); | |
segment1Video.addInput(input.streamSpecifier('v')); | |
const segment1Audio = new FilterChain([trimStream(1, 2, true), adjustTimestamps(true)]); | |
segment1Audio.addInput(input.streamSpecifier('a')); | |
const segment2Video = new FilterChain([trimStream(120, 125), adjustTimestamps()]); | |
segment2Video.addInput(input.streamSpecifier('v')); | |
const segment2Audio = new FilterChain([trimStream(120, 125, true), adjustTimestamps(true)]); | |
segment2Audio.addInput(input.streamSpecifier('a')); | |
const concatenator = concatenateStreams({ | |
video: [segment1Video, segment2Video], | |
audio: [segment1Audio, segment2Audio] | |
}); | |
const cmd = new FFmpegCommand(); | |
cmd.addInput(input); | |
cmd.addFilterChain(segment1Video); | |
cmd.addFilterChain(segment1Audio); | |
cmd.addFilterChain(segment2Video); | |
cmd.addFilterChain(segment2Audio); | |
cmd.addFilterChain(concatenator); | |
const output = new FFmpegOutput('output.mp4'); | |
output.addStreams([ | |
concatenator.streamSpecifier('v'), | |
concatenator.streamSpecifier('a') | |
]); | |
cmd.addOutput(output); | |
console.log(cmd.toString()); | |
/* output: | |
ffmpeg -i "video.mp4" -filter_complex "[0:v]trim=start=1:end=2,setpts=expr=PTS-STARTPTS[chain0_setpts_0];[0:a]atrim=start=1:end=2,asetpts=expr=PTS-STARTPTS[chain1_asetpts_0];[0:v]trim=start=120:end=125,setpts=expr=PTS-STARTPTS[chain2_setpts_0];[0:a]atrim=start=120:end=125,asetpts=expr=PTS-STARTPTS[chain3_asetpts_0];[chain0_setpts_0][chain1_asetpts_0][chain2_setpts_0][chain3_asetpts_0]concat=n=2:v=1:a=1[chain4_concat_0][chain4_concat_1]" -map "[chain4_concat_0]" -map "[chain4_concat_1]" "output.mp4" | |
*/ | |
/* | |
// To execute: | |
cmd | |
.execute() | |
.then(({ stderr, stdout }) => { | |
// do something with logs from the run | |
}); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment