Reputation: 15116
I'm recording screencasts and some part of the recorded screencasts I would like to speed up using a command line tool like ffmpeg.
I know that it is possible to use ffmpeg to speed up an entire video with a command like (source)
ffmpeg -i input.mkv -filter:v "setpts=0.5*PTS" output.mkv
Is it possible to only apply the speedup in certain regions in the video. Eg. from 10 to 15 seconds and again from 50 to 60 seconds? Something similar seems to be possible using the program slowmoVideo.
Upvotes: 26
Views: 18061
Reputation: 67
If you want to use filter_complex, and
If you have a 3 minute video, and you want to 3x speed from 1 minute to 2 minutes.
|0:0-1:0|1:0-2:0, speed×3|2:0-3:0|
Mute video:
ffmpeg -i "input.mp4" -filter_complex "[0:v]trim=start=0\\:0:end=1\\:0,setpts=PTS-STARTPTS[v0];[0:v]trim=start=1\\:0:end=2\\:0,setpts=PTS-STARTPTS,setpts=PTS/3[v1];[0:v]trim=start=2\\:0:end=3\\:0,setpts=PTS-STARTPTS[v2];[v0][v1][v2]concat=n=3:v=1:a=0[out_v]" -map [out_v] -c:v h264 -c:a aac "output.mp4"
Pretty Print:
ffmpeg -i "input.mp4" -filter_complex "
[0:v]trim=start=0\\:0:end=1\\:0,setpts=PTS-STARTPTS[v0]; (start=0:0, end=1:0)
[0:v]trim=start=1\\:0:end=2\\:0,setpts=PTS-STARTPTS,setpts=PTS/3[v1];
[0:v]trim=start=2\\:0:end=3\\:0,setpts=PTS-STARTPTS[v2];
[v0][v1][v2]concat=n=3:v=1:a=0[out_v]"
-map [out_v] -c:v h264 -c:a aac "output.mp4"
Both video and audio:
ffmpeg -i "input.mp4" -filter_complex "[0:v]trim=start=0\\:0:end=1\\:0,setpts=PTS-STARTPTS[v0];[0:v]trim=start=1\\:0:end=2\\:0,setpts=PTS-STARTPTS,setpts=PTS/3[v1];[0:v]trim=start=2\\:0:end=3\\:0,setpts=PTS-STARTPTS[v2];[0:a]atrim=start=0\\:0:end=1\\:0,asetpts=PTS-STARTPTS[a0];[0:a]atrim=start=1\\:0:end=2\\:0,asetpts=PTS-STARTPTS,atempo=3[a1];[0:a]atrim=start=2\\:0:end=3\\:0,asetpts=PTS-STARTPTS[a2];[v0][v1][v2]concat=n=3:v=1:a=0[out_v];[a0][a1][a2]concat=n=3:v=0:a=1[out_a]" -map [out_v] -map [out_a] -c:v h264 -c:a aac "output.mp4"
or
ffmpeg -i "input.mp4" -filter_complex "[0:v]trim=start=0\\:0:end=1\\:0,setpts=PTS-STARTPTS[v0];[0:v]trim=start=1\\:0:end=2\\:0,setpts=PTS-STARTPTS,setpts=PTS/3[v1];[0:v]trim=start=2\\:0:end=3\\:0,setpts=PTS-STARTPTS[v2];[0:a]atrim=start=0\\:0:end=1\\:0,asetpts=PTS-STARTPTS[a0];[0:a]atrim=start=1\\:0:end=2\\:0,asetpts=PTS-STARTPTS,atempo=3[a1];[0:a]atrim=start=2\\:0:end=3\\:0,asetpts=PTS-STARTPTS[a2];[v0][a0][v1][a1][v2][a2]concat=n=3:v=1:a=1[out_v_a]" -map [out_v_a] -c:v h264 -c:a aac "output.mp4"
I also write the simple python file to help triming and speeding up part of video. https://github.com/wayne931121/ffmpeg_trim
Upvotes: 1
Reputation: 629
Example : I want to speed up the first 4 seconds of my video.
ffmpeg -i input.mp4 -t 4 slow.mp4
ffmpeg -i input.mp4 -ss 00:00:04 part-2.mp4
ffmpeg -i slow.mp4 -filter:v "setpts=0.5*PTS" part-1.mp4
ffmpeg -f concat -i <(for f in ./part-*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4
Resources from ffmpeg documentation:
Upvotes: 33
Reputation: 26271
For both audio and video:
In summary, this (just as an example) works well for me (it makes a slo-mo of the 4-second segment starting at 00:00:12.5, and at the same time transcodes to mp4):
ffmpeg -loglevel error \
-ss 12.5 -t 4.0 \
-i orig.MOV \
-filter_complex \
"[0:v]setpts=8.0*PTS[v];[0:a]asetrate=5512.5,aresample=44100[a]" \
-map "[v]" -map "[a]" \
-r 30 -strict -2 \
out.mp4
I found that the combo asetrate=...,aresample=...
works better than atempo
, especially because the latter is limited to [0.5;2.0]
. Note also the final -r 30
that sets the frame rate to 30fps. In some of the videos I processed, for some strange reason, without it, the result would be an enormous file going at 2400fps! It also makes sure I get all the segments of my clip to be at the same frame rate (see below).
The other arguments are as usual: -ss
sets the start of the clip; because it is before the -i
, it works fast, and also correctly (in the timeline of the original). -t
is the duration, also in the original timeline.
Background and Details:
Ok, I had a similar question because my nice slo-mo video from the new iPhone7+ (where I edited just a portion to be slo-mo) didn't seem to be recognized/understood by Google Photos, iPhoto, etc.
After messing around a little (helped by more SO questions and the nice ffmpeg docs), I boiled it down to the following bash script. It does a bit more than the original question, as I wanted to also convert from .MOV
to .mp4
, and also trim some time at the beginning/end. With this, I was able to batch clip & process a whole bunch of the .MOV
files from the iPhone into clips that I could upload, share or play. Example of one of the command lines:
./slo-part.sh IMG_0067.MOV clip_0067.mp4 14.0 23.0 25.5 27.67
Convenience script:
Here is the slo-part.sh
script:
#!/usr/bin/env bash
# Slow down a portion of a movie clip
_usage="Usage : $0 file_in file_out start start_slo stop_slo stop"
function log {
local msg=$1
local t=$(date +"%Y-%m-%d %H:%M:%S")
echo "$t $msg"
}
function calc {
printf "%g" $(bc<<<"$1")
}
IN=$1
OUT=$2
A=$3
B=$4
C=$5
D=$6
: ${IN:?$_usage}
: ${OUT:?$_usage}
: ${A:?$_usage}
: ${B:?$_usage}
: ${C:?$_usage}
: ${D:?$_usage}
RATE=30
AB=$(calc "$B-$A")
BC=$(calc "$C-$B")
CD=$(calc "$D-$C")
# scratch dir
scratch=tmp.$$
mkdir -p $scratch
if (( $(bc <<< "$AB > 0.0") )); then
log "Extracting part-1: from $A to $B ($AB)"
ffmpeg -loglevel error -ss $A -t $AB -i $IN -r $RATE -strict -2 $scratch/part-1.mp4
fi
if (( $(bc <<< "$BC > 0.0") )); then
log "Extracting part-2: from $B to $C ($BC) and slow it down 8x"
ffmpeg -loglevel error -ss $B -t $BC -i $IN -filter_complex "[0:v]setpts=8.0*PTS[v];[0:a]asetrate=5512.5,aresample=44100[a]" -map "[v]" -map "[a]" -r $RATE -strict -2 $scratch/part-2.mp4
fi
if (( $(bc <<< "$CD > 0.0") )); then
log "Extracting part-3: from $C to $D ($CD)"
ffmpeg -loglevel error -ss $C -t $CD -i $IN -r $RATE -strict -2 $scratch/part-3.mp4
fi
log "Concat all parts, output to $OUT"
ffmpeg -loglevel error -y -f concat -i <(for f in $scratch/part-*.mp4; do echo "file '$PWD/$f'"; done) -c copy $OUT
rm -rf $scratch
Upvotes: 8