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:

$ FILE=somefile.mkv
$ FRAMES=$( ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 "$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:

$ COLS=8
$ ROWS=8
$ SNAPS=$(( COLS * ROWS ))

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):

$ FRAME_FREQ=$(( FRAMES / SNAPS ))

Finally we can tell ffmpeg to make our tumbnail image:

ffmpeg -loglevel error -i "$FILE" -y -frames 1 -q:v 1 -vf "select=not(mod(n\,$FRAME_FREQ)),scale=-1:100,tile=${COLS}x${ROWS}" "${FILE}_prev
iew.jpg"

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