protonemedia / laravel-ffmpeg

This package provides an integration with FFmpeg for Laravel. Laravel's Filesystem handles the storage of the files.
https://protone.media/en/blog/how-to-use-ffmpeg-in-your-laravel-projects
MIT License
1.63k stars 194 forks source link

[Functionality request] Generating preview thumbnails #335

Closed Barahten closed 2 years ago

Barahten commented 3 years ago

It would be nice to add a method for generating preview thumbnails: a .vtt file with markup of the timedes and coordinates of the sprite and the sprite itself in the form of a tile. Now all modern js-players can show a preview when hovering over the timeline. As a parameter, you could set the width and height of the thumbnail, as well as the number in one row

pascalbaljet commented 3 years ago

Do you know if there's an existing FFmpeg command or solution that we can use?

Barahten commented 3 years ago

Do you know if there's an existing FFmpeg command or solution that we can use?

I solved this problem as follows: I broke the whole process of creating VTT-Tumbnails into two steps

$media = FFMpeg::fromDisk('public')->open('tmp/test.mp4');

// Duration
$duration  = $media->getDurationInSeconds();

// Dimension
$dimension = $media->getVideoStream()->getDimensions();
$width = $dimension->getWidth();
$height = $dimension->getHeight();

// Interval
$interval = 5 // In seconds

// Thumb tile size
$ratio  =  $width / $height;
$thumb_width = 160;
$thum_height = round($thumb_width/$ratio);
  1. First I create the sprites with the following command
    $media
        ->addFilter('-vf', 'select=isnan(prev_selected_t)+gte(t-prev_selected_t\,' . $interval . '),scale=' . $thumb_width . ':' . $thum_height . ',tile=5x5')
        ->addFilter('-qscale:v', 3)
        ->addFilter('-vsync', 'vfr')
        ->export()
        ->onProgress(function ($percentage) {
            // Some code, if need
        })
        ->toDisk('public')
        ->save('video/thumbnails/sprite_%d.jpg')
        ->cleanupTemporaryFiles();

This command will create sprites in which 160px wide thumbnails will be assembled into tiles (5 rows of 5 in each row). This is how it looks

sprite-1
  1. Then I create a .vtt file with the following command
$vtt = "WEBVTT\n";
$counter = 0;

$max_sprites = ceil(($duration / $interval)/(5 * 5)); // Where 5 * 5 is FFMPEG video filter params tile, see above
for($jpg = 1; $jpg <= $max_sprites; $jpg++){

    for($col = 0; $col < 5; $col++){ // 5 - the number of miniatures in one row

        for($row = 0; $row < 5; $row++){ // 5 - the number of rows in the final sprite

            $vtt .= "\n" 
            . sprintf('%02d:%02d:%02d.000', ($counter * $interval) / 3600,  ($counter * $interval) / 60 % 60, ($counter * $interval) % 60) . " --> " 
            . sprintf('%02d:%02d:%02d.000', (($counter + 1) * $interval) / 3600,  (($counter + 1) * $interval) / 60 % 60, (($counter + 1) * $interval) % 60) . "\n" 
            . "sprite_" . $jpg . ".jpg#xywh=" . ($row * $thumb_width) . "," . ($col * $thum_height) . "," . $thumb_width . "," . $thum_height . "\n";
            $counter++;

        }
    }
}
// Save vtt file
Storage::disk('public')->put('video/thumbnails/thumbnail.vtt', $vtt);

As a result, we get the file thumbnail.vtt with the following content

WEBVTT

00:00:00.000 --> 00:00:05.000
sprite_1.jpg#xywh=0,0,160,90

00:00:05.000 --> 00:00:10.000
sprite_1.jpg#xywh=160,0,160,90

00:00:10.000 --> 00:00:15.000
sprite_1.jpg#xywh=320,0,160,90

...

00:05:50.000 --> 00:05:55.000
sprite_3.jpg#xywh=0,360,160,90

00:05:55.000 --> 00:06:00.000
sprite_3.jpg#xywh=160,360,160,90

00:06:00.000 --> 00:06:05.000
sprite_3.jpg#xywh=320,360,160,90

00:06:05.000 --> 00:06:10.000
sprite_3.jpg#xywh=480,360,160,90

00:06:10.000 --> 00:06:15.000
sprite_3.jpg#xywh=640,360,160,90

The whole operation to create sprites and vtt file for a more then 6 minute video took about 8 seconds on a laptop with a processor Intel Core i7-9750H (6 cores / 12 logical processors)

pascalbaljet commented 3 years ago

Impressive work! I'm a bit preoccupied momentarily, but I'll try to find a place for this in the package soon :)

pascalbaljet commented 2 years ago

WIP! https://github.com/protonemedia/laravel-ffmpeg/pull/368

pascalbaljet commented 2 years ago

This is now available in v7.7.0