要从 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 文件?
这里是一个使用 ffmpeg 从 mp4 视频创建 jpeg 文件 (280x180) 的示例,然后使用 PHP gd2 + 为视频播放器编写一个 VTT 文件将此缩略图组装成精灵(png 格式)。
首先使用 ffmpeg 每秒创建 1 个图像:
ffmpeg -i sprite/MyVideoFile.mp4 -r 1 -s 280x180 -f image2 sprite/thumbs/thumb-%%d.jpg
然后创建精灵文件+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
...
我不完全确定你需要/意味着 vtt 文件的精灵是什么,但是有一个很好的工具可以让你将单个图像组合成一个大的概述图片:
ImageMagick 附带一个名为
montage
的方便工具
蒙太奇 - 通过组合多个单独的图像来创建合成图像。图像平铺在合成图像上,可以选择装饰边框、框架、图像名称等。
您可以使用以下命令将缩略图放在一张或多张图像上:
montage *.png -tile 4x4 overview.png
它会自动生成所需数量的图片,为您提供概览。
对MR_1204给出的答案的一点改进建议:如果最终的精灵是PNG,我会避免JPG压缩导致的质量下降,而将临时缩略图保存为PNG:
ffmpeg -i sprite/MyVideoFile.mp4 -r 1 -s 280x180 -f image2 sprite/thumbs/thumb-%%d.png
此外,保存为 PNG 通常比保存为 JPG 快一点点。
相反,为了保持下载量较小,将(仅)最终精灵图像保存为 JPG 可能是有意义的,在 PHP 和 GD 的情况下,只需要用 imagejpeg(...) 替换 imagepng(...) 。
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)
希望这对某人有帮助
我知道这是一个非常老的问题。 但没有更新的答案。所以,
您可以通过以下方式完成此操作。
ffmpeg -ss 00:00:10 -i movie.avi -frames 1 -vf "select=not(mod(n\,1000)),scale=320:240,tile=2x3" out.png
此搜索需要 10 秒,并每 1000 帧进行一次拾取。
同样的方法,
ffmpeg -ss 00:00:10 -i movie.avi -frames 1 -vf "fps=1/5,scale=320:240,tile=2x3" out.png
搜索 10 秒后每 5 秒选择一帧。
这两个命令都会为给定参数创建缩放图块。