Say your input file is input.mp4
and output is output.mp4
. You want output.mp4
to be approximately 30 seconds long.
Here's a one-liner:
ffmpeg -i input.mp4 -filter:v "setpts=PTS/$(($(ffprobe -v error -show_format -show_streams -i input.mp4 | grep 'duration=' | head -n 1 | sed 's/duration=//')/30))" output.mp4
Let's break it down. First, we find the file duration with:
ffprobe -v error -show_format -show_streams -i input.mp4 | grep 'duration=' | head -n 1 | sed 's/duration=//'
We are getting the duration with ffprobe
, then extracting the information with grep
, head
, and sed
.
Next, we divide this quantity by 30 using:
$(( $(ffprobe -v error -show_format -show_streams -i input.mp4 | grep 'duration=' | head -n 1 | sed 's/duration=//') / 30 ))
We plug this into the -filter:v "setpts=PTS/..."
to give us the correct ratio for speedup or slowdown.
Generic way
Now, assuming we define the input file as INPUT_FILE
, the output file as OUTPUT_FILE
, and the required duration as OUTPUT_DURATION
, here's the generic way to write this:
ffmpeg -i $INPUT_FILE -filter:v "setpts=PTS/$(($(ffprobe -v error -show_format -show_streams -i $INPUT_FILE | grep 'duration=' | head -n 1 | sed 's/duration=//')/$OUTPUT_DURATION))" $OUTPUT_FILE
Interpretable way
Lastly, here's the more interpretable way to write all of this:
INPUT_FILE=input.mp4
OUTPUT_FILE=output.mp4
OUTPUT_DURATION=30
CURRENT_DURATION=$(ffprobe -v error -show_format -show_streams -i $INPUT_FILE | grep 'duration=' | head -n 1 | sed 's/duration=//')
SPEEDUP=$(( $CURRENT_DURATION / $OUTPUT_DURATION ))
ffmpeg -i $INPUT_FILE -filter:v "setpts=PTS/$SPEEDUP" $OUTPUT_FILE
ffprobe -v error -show_format -show_streams XXXX.mp4
Get the duration of the video stream. Divide the target time by duration of the existing video Which should go like this then:ffmpeg -i XXXX.mp4 -filter:v "setpts=(30/$SOURCEDURATION)*PTS" output.mp4
I had hoped to be able to do it in a 1 shot line a batch file.