unix 创建缩略图精灵的ffmpeg命令

eh57zj3b  于 2023-01-17  发布在  Unix
关注(0)|答案(5)|浏览(150)

要从ffmpeg输出单个帧,我可以执行以下操作:

ffmpeg -i input.flv -ss 00:00:14.435 -vframes 1 out.png

要每秒输出一张图像,我可以:

ffmpeg -i input.flv -vf fps=1 out%d.png

是否有一种方法可以从这些输出创建一个缩略图精灵,以帮助创建一个vtt文件的拇指在寻找位置?

tjjdgumg

tjjdgumg1#

下面的示例使用ffmpeg从mp4视频创建jpeg文件(280x180),然后使用PHP gd2 +为视频播放器编写VTT文件将这些缩略图组装成sprite(png格式)。
首先使用ffmpeg每秒创建1张图像:

ffmpeg -i sprite/MyVideoFile.mp4 -r 1 -s 280x180 -f image2 sprite/thumbs/thumb-%%d.jpg

然后创建sprite文件+ vtt文件(例如PHP):

$dirToScan      =   'thumbs/';
$filePrefix     =   'thumb-';
$fileSuffix     =   '.jpg';
$thumbWidth     =   280;
$thumbHeight    =   180;
$imageFiles     =   array();
$spriteFile     =   'sprite.png';
$imageLine      =   20;
$vttFile        =   'sprite.vtt';
$dst_x          =   0;
$dst_y          =   0;

# read the directory with thumbnails, file name in array
foreach (glob($dirToScan.$filePrefix.'*'.$fileSuffix) as $filename) {
    array_push($imageFiles,$filename);
}

natsort($imageFiles);
#calculate dimension for the sprite 
        $spriteWidth =  $thumbWidth*$imageLine;
        $spriteHeight   =   $thumbHeight*(floor(count($imageFiles)/$imageLine)+1);

        # create png file for sprite
        $png = imagecreatetruecolor($spriteWidth,$spriteHeight);

        # open vtt file
            $handle =   fopen($vttFile,'wb+');
            fwrite($handle,'WEBVTT'."\n");

        # insert thumbs in sprite and write the vtt file
            foreach($imageFiles AS $file)   {
                $counter        =   str_replace($filePrefix,'',str_replace($fileSuffix,'',str_replace($dirToScan,'',$file)));
                $imageSrc = imagecreatefromjpeg($file);
                imagecopyresized ($png, $imageSrc, $dst_x , $dst_y , 0, 0, $thumbWidth, $thumbHeight, $thumbWidth,$thumbHeight);

                $varTCstart =   gmdate("H:i:s", $counter-1).'.000';
                $varTCend   =   gmdate("H:i:s", $counter).'.000';

                $varSprite  =   $spriteFile.'#xywh='.$dst_x.','.$dst_y.','.$thumbWidth.','.$thumbHeight;

                fwrite($handle,$counter."\n".$varTCstart.' --> '.$varTCend."\n".$varSprite."\n\n");

create new line after 20 images
                if ($counter % $imageLine == 0) {
                    $dst_x=0;
                    $dst_y+=$thumbHeight;
                }
                else    {
                    $dst_x+=$thumbWidth;
                }

            }
            imagepng($png,$spriteFile);
            fclose($handle);

VTT文件如下所示:

WEBVTT
1
00:00:00.000 --> 00:00:01.000
sprite.png#xywh=0,0,280,180

2
00:00:01.000 --> 00:00:02.000
sprite.png#xywh=280,0,280,180

3
00:00:02.000 --> 00:00:03.000
sprite.png#xywh=560,0,280,180
...
pprl5pva

pprl5pva2#

我不完全确定你需要/意味着什么与sprite的vtt文件,但有一个很好的工具,让你把单个图像组合成一个大的概览图片:
ImageMagick附带了一个名为montage的方便工具
蒙太奇-通过组合几个独立的图像来创建一个合成图像。2这些图像平铺在合成图像上,可以选择用边框、框架、图像名称等进行装饰。
您可以使用以下命令将缩略图放在一个或多个图像上:

montage *.png -tile 4x4 overview.png

它会自动生成所需的图片数量,给予你一个概览。

xpcnnkqh

xpcnnkqh3#

answer given by MR_1204的一点改进建议:如果最后的sprite是PNG,我会避免JPG压缩导致的质量下降,而是将临时缩略图保存为PNG:

ffmpeg -i sprite/MyVideoFile.mp4 -r 1 -s 280x180 -f image2 sprite/thumbs/thumb-%%d.png

此外,保存到PNG通常比保存到JPG快(很小)一点点。
相反,为了保持下载量小,(仅)将最终的sprite图像保存为JPG可能是有意义的,在PHP和GD的情况下,只需要将imagepng(...)替换为imagejpeg(...)。

jobtbby3

jobtbby34#

MR_1204的Python解决方案

import ffmpeg
import logging
from pathlib import Path
from config import temp
import os
from PIL import Image
from datetime import datetime, timedelta
import math
logger = logging.getLogger(__name__)

class ScreenshotExtractor:
    def __init__(self):
        self.screenshot_folder = f"{temp}ss"
        self.gap = 10
        self.size = "177x100"
        self.col = 5
        self.ss_width = 177
        self.ss_height = 100

    def create_sprite_sheet(self, name):
        path, dirs, files = next(os.walk(self.screenshot_folder))
        file_count = len(files)
        img_width = self.ss_width * self.col
        img_height = self.ss_height * math.ceil(file_count/self.col)

        file_name = f"{name}.jpg"
        out_image = Image.new('RGB', (img_width, img_height))
        webvtt = "WEBVTT\n\n"
        for count, img_file in enumerate(files):
            img = Image.open(f"{self.screenshot_folder}/{img_file}")
            st_time = timedelta(seconds=count * self.gap)
            end_time = timedelta(seconds=(count + 1) * self.gap)

            # Adding img to out_file
            x_mod = int(count / self.col)
            x = 0 + (count - (self.col * x_mod)) * self.ss_width
            y = 0 + x_mod * self.ss_height
            out_image.paste(img, (x, y))

            sprite = f"{file_name}#xywh={x},{y},{self.ss_width},{self.ss_height}"
            webvtt += f"{count + 1}\n0{str(st_time)}.000 --> 0{str(end_time)}.000\n{sprite}\n\n"

        out_image.save(f"{temp}{file_name}", quality=90)
        f = open(f"{temp}{name}.vtt", "w")
        f.write(webvtt)
        f.close()
        return True

    def extract_screenshots(self, file_uri, name):
        try:
            Path(self.screenshot_folder).mkdir(parents=True, exist_ok=True)

            vod = ffmpeg.input(file_uri)
            vod.output(f"{self.screenshot_folder}/{name}_%04d.png", r=1/self.gap, s=self.size).run()
            return True
        except Exception as e:
            logger.warning(e)
            return False

    def run(self, file_uri, name):
        # TODO - Actually do logic here
        self.extract_screenshots(file_uri, name)
        self.create_sprite_sheet(name)

希望这能帮助到一些人

6pp0gazn

6pp0gazn5#

所有这些解决方案对我来说都过于复杂了,你唯一需要的是视频输入的帧率和持续时间(ffprobe),简单地做以下的例子(python):

sprites_output = str(job['output_dir'] + '/' + 'sprites.webp')
    tile_rows = math.ceil(job["duration"] / 100)
    frame_modulus = math.ceil(float(job["video"][0]["frame_rate"]) * 10)
    # Create sprites
    command_string = f'{options["path_ffmpeg"]} -i "{job["input"]}" -vf "select=not(mod(n\,{frame_modulus})),scale=192:108,tile=10x{tile_rows}" "{sprites_output}"'
    task_command = subprocess.Popen(command_string, shell=True, stdout=subprocess.PIPE)
    task_command.wait()

这将给予你一个新的帧每10秒跨越宽度的10张图片,所有打包成一个webp文件。如果你想要一个不同的格式,只需改变输出扩展名,ffmpeg将为你处理。此外,这比任何python脚本快得多。

相关问题