midtiby
midtiby

Reputation: 15116

Speed up part of video using ffmpeg

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

Answers (3)

謝咏辰
謝咏辰

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

youyoup
youyoup

Reputation: 629

Example : I want to speed up the first 4 seconds of my video.

Cut your video

ffmpeg -i input.mp4 -t 4 slow.mp4
ffmpeg -i input.mp4 -ss 00:00:04 part-2.mp4

Speed up the part

ffmpeg -i slow.mp4 -filter:v "setpts=0.5*PTS" part-1.mp4

Concatenate

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

Pierre D
Pierre D

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

Related Questions