Making video indexes with FFmpeg
I'm working on digitising some of my DVD collection at the moment, which means creating some pretty large files. Trying to view these over the network to discover what they are is pretty tedious (lots of buffering!), so I've been working on dumping an "index" image file that will helpfully give me enough information to determine what it is without actually loading/playing the file.
I'm using FFmpeg extensively already for modifying and processing videos. It is capable of extracting a number of useful bits of information from a video file already, including frame snapshots (which is essentially what I want). Unfortunately it works on the basis of how many frames to take a snapshot, not a proportion of the way through the length of the file.
The first thing that's needed, therefore is to find the number of frames in our file:
Next thing I need to know is how many images I want to extract. I want to make a grid of, e.g., 8 x 8 snapshots, so let's stick that in some variables and calculate the number of thumbnails to capture:
Now we need to know how many frames apart we need to capture our thumbnails (bash will truncate non-interger results from integer devision, which effectively rounds-down if there's a fractional number):
Finally we can tell ffmpeg to make our tumbnail image:
Or, as a script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash
if [[ $# -le 1 ]]
then
echo "Usage: $0 file_to_make_preview [...]" >&2
exit 1
fi
# Global sizes
PREVIEW_HEIGHT=100
COLS=8
ROWS=8
SNAPS=$(( COLS * ROWS ))
while [[ ! -z $1 ]]
do
FILE="$1"
shift
echo "Processing $FILE..."
OUTFILE="${FILE}_preview.jpg"
if [[ -e $OUTFILE ]]
then
echo "$OUTFILE already exists, skipping $FILE" >&2
continue
fi
FRAMES=$( ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 "$FILE" )
FRAME_FREQ=$(( FRAMES / SNAPS ))
ffmpeg -loglevel error -i "$FILE" -y -frames 1 -q:v 1 -vf "select=not(mod(n\,$FRAME_FREQ)),scale=-1:$PREVIEW_HEIGHT,tile=${COLS}x${ROWS}" "${OUTFILE}"
echo "$FILE done."
done