Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Image Montage using ffmpeg

See bash script below, adapted from here

#!/usr/bin/env bash

set -euo pipefail

SECONDS_PER_IMAGE=5

TMP_DIR=".tmp"
#!/usr/bin/env bash

set -euo pipefail

# Configuration
SECONDS_PER_IMAGE=5
AUDIO_FILE="${1:-}"  # First argument: path to audio file (optional)

TMP_DIR=".tmp"
FILES_LIST="$TMP_DIR/.files"
OUTPUT="output.mp4"

VF="scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30"

# Clean up previous outputs
rm -r "$TMP_DIR" "$OUTPUT" || true
mkdir "$TMP_DIR"

# Convert all videos to standard format
find . -maxdepth 1 -regex '.*\(mp4\|MP4\)' | while read i; do
  ffmpeg -y -i "$i" -vf "$VF" -ar 44100 "$TMP_DIR/$i" < /dev/null
  echo "file '$i'" >> "$FILES_LIST"
done

# Convert all images to videos
find . -maxdepth 1 -regex '.*\(jpe?g\|png\|JPG\|bmp\)' | while read i; do
  ffmpeg -y -loop 1 -i "$i" -t "$SECONDS_PER_IMAGE" -vf "$VF" \
    "$TMP_DIR/$i.mp4" < /dev/null
  echo "file '$i.mp4'" >> "$FILES_LIST"
done

# Shuffle the media files
shuf -o "$FILES_LIST" "$FILES_LIST"

# Find first video with audio and move to top
first=
while read f; do
  fn="$(echo "$f" | sed "s/^file '//; s/'$//")"
  if ffprobe "$fn" 2>&1 | grep -q 'Audio:'; then
    first="$fn"
  fi
done < "$FILES_LIST"
if [ -n "$first" ]; then
  { echo "file '$first'"; cat "$FILES_LIST"; } | awk '!seen[$0]++' > files
  mv files "$FILES_LIST"
fi

# Concatenate videos and optionally add audio overlay
if [ -n "$AUDIO_FILE" ]; then
  if [ ! -f "$AUDIO_FILE" ]; then
    echo "Error: Audio file '$AUDIO_FILE' not found"
    exit 1
  fi

  echo "Adding audio overlay from: $AUDIO_FILE"

  # First, create the concatenated video without final audio
  TMP_VIDEO="$TMP_DIR/concat_video.mp4"
  ffmpeg -y -f concat -safe 0 -i "$FILES_LIST" -c copy "$TMP_VIDEO"

  # Then overlay the audio track
  # -stream_loop -1: loop audio if shorter than video
  # -shortest: end output when shortest input ends (video duration)
  # -map 0:v:0: take video from first input (concatenated video)
  # -map 1:a:0: take audio from second input (audio file)
  # -c:v copy: copy video without re-encoding
  # -c:a aac: encode audio as AAC
  ffmpeg -y -stream_loop -1 -i "$TMP_VIDEO" -i "$AUDIO_FILE" \
    -map 0:v:0 -map 1:a:0 -shortest -c:v copy -c:a aac -b:a 192k "$OUTPUT"

  echo "Montage created with audio overlay: $OUTPUT"
else
  # Original behavior: concatenate without audio overlay
  ffmpeg -y -f concat -safe 0 -i "$FILES_LIST" "$OUTPUT"
  echo "Montage created: $OUTPUT"
fi

echo "Done!"