Journal Un timelapse avec ffmpeg

Posté par  . Licence CC By‑SA.
24
19
nov.
2025

Sommaire

J'ai eu besoin de faire un timelapse sur une longue période de vidéo (3 semaines). Pour ne pas trop me prendre la tête, j'ai utilisé ffmpeg et bash pour automatiser ce que je pouvais.

Je laisse cela ici, en espérant que ça puisse en aider certains (dont moi dans le futur).

Bonne lecture

Timelapse

Pour faire un joli timelapse, j'ai divisé le travail en sous taches :

  1. Acquérir des images,
  2. Moyenner les images acquises,
  3. Fusionner les moyennes en clips,
  4. Fusionner les clips en video,
  5. Accélérer la video,
  6. Ajouter du son.

1. Acquisition des images

On a un script qui enregistre bêtement des images, aussi vite que possible, mais pas trop quand même, au mieux 1 image par seconde.

C'est un script que je laisse tourner sur un serveur (enfin un PC jamais éteint)

#! /bin/bash

# camera IP
IP=172.22.10.34

# Errors and images counts
ERRCNT=0
CNT=0
# Use another IP if asked
if [ $# -eq 1 ]
then
    IP=$1
fi

# Forever
while true
do
    # File to generate
    FILE="$(date "+%F_%H-%M-%S.jpg")"
    # File to store error
    OUT=$(mktemp)

    # increase counter
    CNT=$((CNT+1))
    # get one image thanks to ffmpeg
    # ! warning, here I integrated the credential in the url, it's not necessary on all camera...
    ffmpeg -y -i "rtsp://login:password@$IP/axis-media/media.amp" -vframes 1 "$FILE" 2> "$OUT"
    # Test for error
    if grep --quiet "error" "$OUT"; then
        # increase error count
        ERRCNT=$((ERRCNT+1))
        # Inform user
        echo -n -e "$(date +%T) Bad image, deleting. ($ERRCNT error(s) on $CNT)\r"
        remove image file
        rm "$FILE"
    fi

    # remove error file
    rm "$OUT"
done

2. Génération des moyennes

Ici, on va moyenner les images. On moyenne ensemble les images qui sont dans les 10 secondes.
C'est à dire, les images

  • 2025-12-31-12-00-01.jpg
  • 2025-12-31-12-00-02.jpg
  • 2025-12-31-12-00-05.jpg
  • 2025-12-31-12-00-07.jpg
  • 2025-12-31-12-00-09.jpg

Seront fusionnées dans le fichier 2025-12-31-12-00-0x.jpg

Ce script tourne sur le même serveur que le précédent.

#!/bin/bash

# Directory to stores averages
mkdir -p averages

# Forever
while true
    do
    # Finding all jpg files in current directory
    FILES=$(find . -maxdepth 1 -name "*jpg" -mmin +1 )

    # Wait 10 seconds to be sure that all interval will be complete
    sleep 10

    # For each file...
    for i in $FILES; do
        # Ignore if file has already been processed
        if [ ! -e "$i" ] ; then
            continue
        fi

        # Select one file
        FILE=$(basename "$i")

        # Create a regexp to select all files in the same 10 seconds interval (like 2025-12-30-12-5*.jpg)
        REGEXP=$(echo $FILE | sed 's/\(....-..-.._..-..-.\)./\1*/')
        # Destination file:  (like 2025-12-30-12-5x.jpg)
        DESTFILE=$(echo $FILE | sed 's/\(....-..-.._..-..-.\)./\1x/')

        # create empty image list and temporary files
        IMAGELIST=
        CMD=$(mktemp)
        LIST=$(mktemp)

        # Generate list of image matching the regexp
        find . -maxdepth 1 -name "$REGEXP" > "$LIST"
        # Convert these images names to a list
        while IFS= read -r  j; do
            IMAGELIST="$j $IMAGELIST"
        done < "$LIST"
        # prepare a imagemagick command to average them
        echo "convert -average $IMAGELIST averages/$DESTFILE" > "$CMD"
        chmod +x "$CMD"

        #execute the imagemagick command
        sh -c "$CMD"

        # cleanup
        rm "$CMD"
        rm "$LIST"
        rm $IMAGELIST

    done

    # wait 30 seconds, no rush
    sleep 30
done

3. Génération des clips

On a un paquet d'images, on peut les fusionner simplement dans un clip avec la commande ffmpeg -y -pattern_type glob -i "averages/*.jpg" clip.mp4, où, on peut générer un clip par heure, ce qui facilite grandement le montage derrière (dans mon cas, j'ai 3 semaines d'images)

Ce script ne tourne pas tout seul. Je le lance quand j'en ai besoin, ça peut être un peu long si ça fait longtemps que je l'ai pas fait tourner. Je pourrais l'automatiser, mais la flemme est plus forte.

#! /bin/sh
# Check user called that program correctly
if [ $# -ne 2 ] ; then
    echo "usage $(basename "$0") date hour"
    echo ""
    echo "  date: yyyy-mm-dd format"
    echo "  hour: 24 hour format"
    exit
fi

# create destination directory
mkdir -p by-hour

# make sur that morning hour start with a 0, to simplify sorting
HOUR=$(printf "%02d" $2)

# ask ffmpeg to do its magic
ffmpeg -y -pattern_type glob -i "averages/$1_$HOUR*.jpg" "by-hour/$1_$2".mp4

4. Concaténer les clips

On a plein de petits clips qu'on va vouloir coller ensemble, pour cela, on les liste dans un fichier :

file 2025-11-18_11.mp4
file 2025-11-18_15.mp4
file 2025-11-19_10.mp4

Et on demande gentiement à ffmpeg de les coller entre eux

ffmpeg -y -f concat -i files.txt -c copy timelapse.mp4

5. Accélérer la video

À partir d'ici, j'ai rapatrier les clips qui m'intéressent sur mon poste.

Pour que mon timelapse soit pas trop long, j'ai eu besoin de l'accélérer. Pour cela, on utilise des commandes barbares:

# Extract frames info
ffmpeg -y -i timelapse.mp4 -map 0:v -c:v copy -bsf:v h264_mp4toannexb raw.h264

# Generate faster video (to match audio duration), here 62fps
ffmpeg -y -fflags +genpts -r 62 -i raw.h264 -c:v copy timelapse-fast.mp4

6. Ajouter du son

Un dernier truc à savoir quand on veut faire un film avec du son sans se prendre la tête, c'est d'utiliser le format mkv. Les autres conteneurs que j'ai testé (au moins mp4 et mov) sont plus délicats à utiliser.

ffmpeg -i timelapse.mp4 -i bruit.mp3 -c:v copy -c:a copy timelapse.mkv

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.