ffmpeg


Cut a video based on start and end time using FFmpeg

You probably don’t have a keyframe at the specified second mark if you can’t cut a video at a particular moment.
Non-keyframes need all of the data beginning with the previous keyframe because they encode variations from other frames.

Using an edit list, it is possible to cut at a non-keyframe with the mp4 container without re-encoding.
In other words, if the closest keyframe before 3s is at 0s, the video will be copied starting at 0s, and FFmpeg will use an edit list to tell the player to begin playing 3 seconds in.

If you’re using the latest version of FFmpeg from git master, it’ll use an edit list when you run it with the command you give.
If this does not work for you, it is you are likely using an older version of FFmpeg or that your player does not support edit lists.
Some players can disregard the edit list and play the entire file from beginning to end, regardless of the edit list.

If you want to cut specifically at a non-keyframe and have it play at the desired point on a player that doesn’t support edit lists, or if you want to make sure the cut section isn’t in the output file (for example, if it includes sensitive information), you can do so by re-encoding so that a keyframe is present at the desired start time.
If you don’t mention copy, re-encoding is the norm.
Consider the following scenario:

ffmpeg -i input.mp4 -ss 00:00:07 -t 00:00:18 -async 1 output.mp4
  • The -t option defines a length rather than an end time.
  • The command above will encode 18 seconds of video beginning at 7 seconds.
  • Use -t 8 to start at 7 seconds and end at 15 seconds.
  • If you’re using a recent version of FFmpeg, you can also use -to instead of -t in the above command to make it end at the required time.

ffmpeg: Create a video countdown – new post (2021)

The code below was used to generate the video countdown timers that are available in the following playlist using ffmpeg. These counters show seconds and fractions of seconds only. They do not bother with formating for minutes nor hours, etc.

#!/bin/bash

# This code will create a countdown video.
# If no command line arguments are provided, it will default to creating a 3-second video, with two fractional digits at 100 frames per second.
# It will print the elapsed and remaining times using two decimals accuracy.
defaultSeconds=3;
# If command line argument 1 is empty, the default value will be used.
seconds="${1:-$defaultSeconds}";

# Calculating how many digits are used to compose the seconds variable.
# We will use this information for zero-padding to avoid having the text move a lot.
# We used the shell parameter expansion to get the length of the variable value.
integerDigits="${#var}";

defaultFractionalDigits=2;
# If command line paremeter 2 is empty, the default value will be used.
fractionalDigits="${2:-$defaultFractionalDigits}";

#Computing how many frames per second are needed to maintain the accuracy of time based on the fractional digits.
fps=$((10 ** $fractionalDigits));

countDownFont=600;
countUpFont=100;
#Using a fixed width and fixed height font, to avoid having the text move around.
font='./Led.ttf';

#We are using the n variable: the frame number starting from 0 rather than the t variable, which is the timestamp expressed in seconds. We will get better accuracy on the decimals.
ffmpeg -loop 1 -i ~/Pictures/Black-Background.png -c:v libx264 -r $fps -t $seconds -vf "fps=$fps,
drawtext=fontfile=$font:fontcolor=yellow:fontsize=$countDownFont:x=(main_w-text_w)/2:y=(main_h-text_h)/2:text='%{eif\:($seconds-(n/$fps))\:d\:$integerDigits}.%{eif\:(mod($fps - mod(n, $fps), $fps))\:d\:$fractionalDigits}',
drawtext=fontfile=$font:fontcolor=yellow:fontsize=$countUpFont:x=(main_w-text_w)/2:y=((main_h-text_h)/2)+$countDownFont:text='Elapsed\: %{eif\:(n/$fps)\:d\:$integerDigits}.%{eif\:(mod(n, $fps))\:d\:$fractionalDigits}'" "$seconds seconds countdown timer with $fractionalDigits fractional digits accuracy.mp4";

Notes

  • We used a single black frame for the background that defined the video frame’s size as well.
  • Using the fps variable, we defined the number of Frames per Second for the video.
  • The seconds variable defined the number of seconds the duration of the video should be.
  • The fractionalDigits variable defines how many decimal digits should be shown after the dot.
  • countDownFont and countUpFont define the fonts’ size in the upper row and the lower one, respectively.
  • We used the drawtext directive twice to write to the frames.
  • font variable defines a fixed-width font to avoid having the text moving around.

Notes on the first drawtext

  • x=(main_w-text_w)/2 defines the X-coordinate of the location for the text on the frame, here we center the text horizontally on the frame.
  • (main_h-text_h)/2 defines the Y-coordinate of the location for the text on the frame, here we center the text vertically on the frame.
  • text='%{eif\:($seconds-(n/$fps))\:d\:$integerDigits}.%{eif\:(mod($fps - mod(n, $fps), $fps))\:d\:$fractionalDigits}' We print the remaining seconds for the video to finish with specific decimal digit accuracy.

Notes on the second drawtext

  • x=(main_w-text_w)/2 defines the X-coordinate of the location for the text on the frame, here we center the text horizontally on the frame.
  • y=((main_h-text_h)/2)+$countDownFont defines the Y-coordinate of the location for the text on the frame, here shift the text from the vertical center of the frame enough to move it under the main text.
  • text='Elapsed\: %{eif\:(n/$fps)\:d\:$integerDigits}.%{eif\:(mod(n, $fps))\:d\:$fractionalDigits}' We print the elapsed seconds since the video started with specific decimal digit accuracy.

ffmpeg: Cut out a part of a video

Following is a snippet we used to remove a few seconds from a video using ffmpeg

ffmpeg -i 'input.mkv' -filter_complex \
  "[0:v]trim=end=553,setpts=N/FRAME_RATE/TB[v0]; \
   [0:a]atrim=end=553,asetpts=N/SR/TB[a0]; \
   [0:v]trim=start=559,setpts=N/FRAME_RATE/TB[v1]; \
   [0:a]atrim=start=559,asetpts=N/SR/TB[a1]; \
   [v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]" \
  -map "[v]" -map "[a]" 'output.mkv'

Specifically, we removed the seconds 09:13 (second 553) to 09:19 (second 559).