Skip to content

Instantly share code, notes, and snippets.

@DJStompZone
Created April 2, 2024 03:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DJStompZone/0695625a3d34c4bb7a114d2102c1fe28 to your computer and use it in GitHub Desktop.
Save DJStompZone/0695625a3d34c4bb7a114d2102c1fe28 to your computer and use it in GitHub Desktop.
Convert a video to a looping GIF image using ffmpeg
#!/bin/bash
# Usage:
# ./mkgif.sh video.mkv [anim.gif] [frame_rate]
video_input="$1"
gif_output="$2"
print_error_and_exit() {
echo "Error: $1" >&2
exit 1
}
if [ -z "$video_input" ]; then
print_error_and_exit "No input video provided."
fi
# Output filename?
if [ -n "$2" ] && [[ "$2" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
frame_rate="$2"
gif_output="${video_input%.*}.gif"
elif [ -n "$2" ]; then
gif_output="$2"
else
gif_output="${video_input%.*}.gif"
fi
if [ -n "$3" ]; then
frame_rate=$3
else
original_frame_rate=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate "$video_input" | awk -F'/' '{print $1 / $2}')
if [ -z "$original_frame_rate" ]; then
print_error_and_exit "Failed to retrieve frame rate from video."
fi
# Linear interpolation for gif framerate
min_fps=10
max_fps=60
if (( $(echo "$original_frame_rate <= $min_fps" | bc -l) )); then
divisor=1
elif (( $(echo "$original_frame_rate >= $max_fps" | bc -l) )); then
divisor=4
else
divisor=$(echo "1 + ($original_frame_rate - $min_fps) * (3 / ($max_fps - $min_fps))" | bc -l)
fi
frame_rate=$(echo "$original_frame_rate / $divisor" | bc -l)
fi
echo "Encoding GIF at $frame_rate FPS..."
# TODO: Variable output width
ffmpeg -v warning -i "$video_input" -vf "fps=$frame_rate,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 -y "$gif_output" || print_error_and_exit "FFmpeg failed to encode GIF."
echo "GIF successfully created at $gif_output"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment