33

Is there a relatively simple way to speed up a video (mp4, with aac encoded audio), by say "x1.25" or "x1.3" while keeping the original audio-video sync, but also retaining the original pitch of the audio ?

I've read some discussions about extracting audio track into separate file, then use rubberband to lower the pitch by the requisite value, and then merge the audio video tracks using ffmpeg with speed-up, which raises the pitch of the audio back again to original. However, it appears that this is a bit of hit-n-miss, because rubberband appears to use number of octaves as the parameter to raise or lower pitch, and this may not always align perfectly with the video, as musical notes / octaves have specific ratios. Also, this is cumbersome.

So, is there any simpler ways using ffmpeg alone ?

bdutta74
  • 639
  • 2
    rubberband offers a tempo option which takes in a decimal number. – Gyan May 21 '18 at 06:03
  • Thanks @Gyan. Sounds like a valid answer, even if it is not as simple as I'd hope it to be, but removes the issue of rubberband being (wrongly presumed to be) limited to pitch change based on number of notes (of a chromatic scale). – bdutta74 May 21 '18 at 06:20
  • 1
    rubberband can be compiled as a ffmpeg filter, so no need to extract audio. – Gyan May 21 '18 at 06:42

3 Answers3

44

It can be done with ffmpgeg using a complex filter:

  ffmpeg -i input.mkv -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" output.mkv

Documentation: https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video

The command above works if you want to multiply by 2 the speed. If you want to multiply by any <x>, the parameters become:

  ffmpeg -i input.mkv -filter_complex "[0:v]setpts=<1/x>*PTS[v];[0:a]atempo=<x>[a]" -map "[v]" -map "[a]" output.mkv

For instance, if you want to multiply by 1.15, the command is:

  ffmpeg -i input.mkv -filter_complex "[0:v]setpts=0.87*PTS[v];[0:a]atempo=1.15[a]" -map "[v]" -map "[a]" output.mkv
cssyphus
  • 1,172
10

You can speed up video by 2.5X for example by doing this:

ffmpeg -i "input_video.mp4" -vf "setpts=(PTS-STARTPTS)/2.5" -af atempo=2.5 "input_video_2.5X.mp4"

Here is a bash script you can use for convenience (say convert_video.sh):

#!/bin/bash

Arguments

FILE_RAW=$1 SPEED_FACTOR=${2:-1.0} # Default is 1.0 X speed

Prepare variables

BASE_PATH=$(dirname $(readlink -f $FILE_RAW)) FILENAME_EXT="$(basename "${FILE_RAW}")" FILENAME_ONLY="${FILENAME_EXT%.}" EXT_ONLY="${FILENAME_EXT#.}" FILENAME_ONLY_PATH="${BASE_PATH}/${FILENAME_ONLY}"

Speed up/slow down video

ffmpeg -i "${FILENAME_ONLY_PATH}.${EXT_ONLY}" -vf "setpts=(PTS-STARTPTS)/${SPEED_FACTOR}" -af atempo=$SPEED_FACTOR "${FILENAME_ONLY_PATH}_${SPEED_FACTOR}X.${EXT_ONLY}"

Note: Make script executable: chmod +x convert_video.sh

Usage (Output File: <PATH_TO_INPUT_VIDEO>_<OPTIONAL_SPEED>X.mp4)

. <PATH_TO_THIS_SCRIPT>/convert_video.sh <PATH_TO_INPUT_VIDEO> <OPTIONAL_SPEED>

Example 1: Speed up video to 2.5X (Output: ~/Videos/input_video_2.5X.mp4)

. ~/convert_video.sh ~/Videos/input_video.mp4 2.5

Example 2: Slow down video to 0.5X (Output: ~/Videos/input_video_0.5X.mp4)

. ~/convert_video.sh ~/Videos/input_video.mp4 0.5

Hope it helps.

sagunms
  • 291
2

The code I used.

speed_change(){
    input=$1
    output=$2
    a_speed=$3
    v_speed=$(awk -v a_speed=$a_speed 'BEGIN{print 1/a_speed}')
    echo -e "\nSpeed up ${a_speed}X, para to atempo is ${a_speed}, para to setpts is ${v_speed}.\n"
    ffmpeg -i $input -c:v hevc_videotoolbox -vf "setpts=$v_speed*PTS" -af atempo=$a_speed $output
}
HuoJnx
  • 21
  • 1
    Code without any explanation is useless. Can you elaborate on this a little more? – Toto Oct 11 '22 at 17:07