Capturing multiple thumbnails from a movie using ffmpeg

When working with video processing scripts, a common requirement is to create thumbnails from a video. Fortunately, with ffmpeg we can make still captures from a a video, and i will show you 2 of the methods in this article.

First of all, in order to generate thumbnails. we need to get the duration of the movie. You can use the the ffmpeg-php library for that. If you can’t to install the ffmpeg-php extension on your server, you can download the OO PHP port of the ffmpeg extension – it is written in pure PHP so you won’t have the problems which comes when installing new extensions.

Here is how you can get the duration of the video:

if (extension_loaded('ffmpeg')){
	$movie = new ffmpeg_movie('/path/to/movie');
	$duration = $movie->getDuration();
} else {
	require_once 'FFmpegMovie.php';
	require_once 'FFmpegFrame.php';
	require_once 'FFmpegAnimatedGif.php';
	$movie = new ffmpegMovie('/path/to/movie');
	$duration = $movie->getDuration();
}

Now that we have the total duration of the video, let’s see how we can make a thumbnail

At first i was creating a thumbnail from a video with this simple command.

$command = "ffmpeg -i /path/to/videos/myvideo.avi -ss ".intval($duration/2)." -vframes 1 -f image2 -s 320x240 /path/to/thumbs/thumb.jpg";

I used the following options in this command

- ss to specify at which time the video processing should start (ie the thumbnail time)
-f to specify the format, which for jpg can be image2 or mjpeg, and for png is png
-s to specify the size (width x height of the converted video)

(in english,this means : capture a picture from a /path/to/videos/myvideo.avi, at the half of the movie, and save it to /path/to/thumbs/thumb.jpg)

Unfortunately, although this command is acceptable when you create thumbnails at the beginning of the video, becomes very slow when we want to create thumbnails in the middle or near the end of the video, and even slower when we have to create a series of thumbnails, on defined interval, because ffmpeg processes all the video to get to the thumbnail position.

So how to solve this problem?

1) Adding the -r option (frame rate).

If we use a value lower than 1 for the frame rate, we can “trick” ffmpeg into processing only “from time to time”.

So:

$command = "ffmpeg -i /path/to/videos/myvideo.avi  -r 1 -s 320x240 -f image2 /path/to/thumbs/thumbs%03d.jpg";

mean “capture a frame every second”,

$command = "ffmpeg -i /path/to/videos/myvideo.avi  -r 0.5 -s 320x240 -f image2 /path/to/thumbs/thumbs%03d.jpg";

mean capture a frame every 2 seconds,

$command = "ffmpeg -i /path/to/videos/myvideo.avi -r 0.25 -s 320x240 -f image2 /path/to/thumbs/thumbs%03d.jpg";

mean capture a frame every 4 seconds
and so on

There is some problems with this approach though:

First, ffmpeg will double the capture at the begining, so we need to make sure we strip that from our output
Second, ffmpeg doesn’t allow a value lower than 0.05 as a value for the frame rate (-r option), so the maximum interval between 2 thumbs cannot be larger than 1/0.05 = 20s. We could solve this problemby looking for the greatest divisor in the 1-20 interval, so, if our video require us to have a thumbnail , let’s every say 30s, we need to find the greatest divisor in the 1-20 s interval (in our case 15), create more thumbnails and keep only the ones we need.

2) Using -ss in front of the input

Later on i discover that using -ss in front of the input, can actually be a lot faster, ffmpeg going straight at that position to capture the thumbnail.

So a solution like

$command = "ffmpeg -ss ".$capture_time." -i /path/to/videos/myvideo.avi -vframes 1 -f image2 -s 320x240 /path/to/thumbs/thumb.jpg"

seemed feasible again, being both faster and easier to use.

I wanted to see though which solutions is faster and in which cases.So i run some tests on a 700 MB video (5120 seconds), generating 40, 80, 160, 240 and 320 thumbnails using the two methods, recording the total execution time of the script:

40 thumbnails 80 thumbnails 160 thumbnails 240 thumbnails 320 thumbnails
-ss in front 22 seconds 54 seconds 96 seconds 164 seconds 289 seconds
-r < 1 190 seconds 200 seconds 221 seconds 226 seconds 240 seconds

You can see that, for getting few thumbnails, ffmpeg -ss is a lot faster than ffmpeg -r, but this advantage diminishes when the number of thumbnails we want to create, rises, making the -r < 1 method indicated for getting many thumbnails. This is because of the overhead added  by executing multiple commands in a loop, compared to executing only one command, and processing the files afterwards.
In conclusion, if you want to create few thumbnails, go with the first method as it is more simpler and faster, but if you want to create a more complex application, to create a thumnail every second or two, you are better of using the second method.

Related posts:

  1. Ffmpeg 0.6 release – now with html5 support
  2. Running multiple processes in PHP

2 Responses to “Capturing multiple thumbnails from a movie using ffmpeg”

  1. Alex says:

    Thank you for this article!
    Especially the comparison was very helpful to me.

    The data in the table is based on a 700 MB video, what about smaller videos e.g. 40 MB?

    Greetings from Germany! ;-)

Leave a Reply