How to Create a Preview Video Thumbnail of a Video with FFmpeg & PHP
In this tutorial, we will learn how to create a video thumbnail preview from a full video using FFmpeg & PHP. It will be a single low-resolution file containing a sequence of 2-second clips from various parts of the original video.
Before we go any further, here is an example of what the final output will look like:
Get the Duration of the Video in Seconds
The first step is to get the duration of the input video in seconds so we can determine where to get clips from it. This is done using ffprobe
and rounding the output to an integer.
$video_path = 'path/to/example_preview/input.mp4';
$dur = shell_exec("ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 '$video_path'");
$seconds = round($dur);
Setting the Seconds from the Video to Create Clips from
Now we will create four intervals from where each clip will start from using PHP gmdate()
function.
$thumb_0 = gmdate('H:i:s', $seconds / 8);
$thumb_1 = gmdate('H:i:s', $seconds / 4);
$thumb_2 = gmdate('H:i:s', $seconds / 2 + $seconds / 8);
$thumb_3 = gmdate('H:i:s', $seconds / 2 + $seconds / 4);
Creating The Clips
The next step is to create the clips. To do this we will loop four times and create a 2-second clip at each interval.
The path to each file will is stored in a list.txt
file, which we will need to reference when joining these clips together. We will also store a $preview_array
of the clips so we can delete them later.
In each iteration, the FFmpeg command seeks to the thumb interval value and creates a 2-second video at a resolution of 320wx180h.
$path_clip = 'path/to/example_preview/;
$preview_list = fopen($path_clip . 'list.txt', "w");
$preview_array = [];
for ($i=0; $i <= 3; $i++) {
$thumb = ${'thumb_'.$i};
shell_exec("ffmpeg -i '$video_path' -an -ss $thumb -t 2 -vf 'scale=320:180:force_original_aspect_ratio=decrease,pad=320:180:(ow-iw)/2:(oh-ih)/2,setsar=1' -y $path_clip/$i.p.mp4");
$output = $path_clip . $i.'.p.mp4';
if (file_exists($output)) {
fwrite($preview_list, "file '". $output . "'\n");
array_push($preview_array, $output);
}
}
fclose($preview_list);
Concatenating the Clips
Now we can concatenate the clips into one file like this:
shell_exec("ffmpeg -f concat -safe 0 -i $path_clip/list.txt -y $path_clip/preview.mp4");
Removing the Clips and File List
Finally, remove the clips and the list.txt
to clean up files we will no longer need:
if (!empty($preview_array)) {
foreach ($preview_array as $v) {
unlink($v);
}
}
// remove preview list
unlink($path_clip . 'list.txt');
Full Example
Here is a full example of the code:
$video_path = 'path/to/example_preview/input.mp4';
$dur = shell_exec("ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 '$video_path'");
$seconds = round($dur);
$thumb_0 = gmdate('H:i:s', $seconds / 8);
$thumb_1 = gmdate('H:i:s', $seconds / 4);
$thumb_2 = gmdate('H:i:s', $seconds / 2 + $seconds / 8);
$thumb_3 = gmdate('H:i:s', $seconds / 2 + $seconds / 4);
$path_clip = 'path/to/example_preview/;
$preview_list = fopen($path_clip . 'list.txt', "w");
$preview_array = [];
for ($i=0; $i <= 3; $i++) {
$thumb = ${'thumb_'.$i};
shell_exec("ffmpeg -i '$video_path' -an -ss $thumb -t 2 -vf 'scale=320:180:force_original_aspect_ratio=decrease,pad=320:180:(ow-iw)/2:(oh-ih)/2,setsar=1' -y $path_clip/$i.p.mp4");
$output = $path_clip . $i.'.p.mp4';
if (file_exists($output)) {
fwrite($preview_list, "file '". $output . "'\n");
array_push($preview_array, $output);
}
}
fclose($preview_list);
shell_exec("ffmpeg -f concat -safe 0 -i $path_clip/list.txt -y $path_clip/preview.mp4");
if (!empty($preview_array)) {
foreach ($preview_array as $v) {
unlink($v);
}
}
// remove preview list
unlink($path_clip . 'list.txt');