如何使用 ffmpeg 生成仅包含几帧的 Web 预览视频?例如,当您将鼠标移到视频上时,它会在加载实际视频之前播放几帧预览?
答案1
TL;DR: 与上述内容等效的 bash 版本:
width=384
height=216
frame_count=$(ffprobe -v error -show_entries format=duration "/input/file.mp4" -of default=noprint_wrappers=1:nokey=1)
frame_target=$( expr ${frame_count%.*} / 10)
ffmpeg -threads 2 -i "/input/file.mp4" -an -qscale:v 1 -vframes 10 -f image2pipe -vcodec ppm \
-vf "fps=1/$frame_target, scale=iw*min($width/iw\,$height/ih):ih*min($width/iw\,$height/ih):flags=lanczos, pad=$width:$height:($width-iw*min($width/iw\,$height/ih))/2:($height-ih*min($width/iw\,$height/ih))/2, unsharp=5:5:0.5:5:5:0.5" -\
| ffmpeg -y -threads 2 -framerate 1 -i pipe:0 -c:v libx264 -profile:v baseline -level 3.0 -tune stillimage -r 30 -pix_fmt yuv420p "/output/preview.mp4"
可调整的 Python 脚本
作为阿泽维多的回答我编写了一个脚本,以便可以使用 unix 环境轻松地制作不同大小的预览。
generate_preview_video.sh
:
tmp_dir=$(mktemp -d -t {tmp_prefix}-XXXXXXXXXX)
echo $tmp_dir
width={width}
height={height}
frame_count=$(ffprobe -v error -show_entries format=duration "{input_filepath}" -of default=noprint_wrappers=1:nokey=1)
frame_target=$( expr ${{frame_count%.*}} / {output_frame_count})
ffmpeg -threads {threads} -i "{input_filepath}" -an -qscale:v 1 -vframes {output_frame_count} -f image2pipe -vcodec ppm \
-vf "fps=1/$frame_target, scale=iw*min($width/iw\,$height/ih):ih*min($width/iw\,$height/ih):flags=lanczos, pad=$width:$height:($width-iw*min($width/iw\,$height/ih))/2:($height-ih*min($width/iw\,$height/ih))/2, unsharp=5:5:0.5:5:5:0.5" - \
| ffmpeg -y -threads {threads} -framerate {framerate} -i pipe:0 -c:v libx264 -profile:v baseline -level 3.0 -tune stillimage -r 30 -pix_fmt yuv420p "$tmp_dir/{output_filename}"
echo $tmp_dir/{output_filename}
preview_gen.py
:
import os
import shutil
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument("tmp_prefix", help="used to generate temporary dir to work in")
parser.add_argument("input", help="path of input file")
parser.add_argument("output_path", help="path to output result")
parser.add_argument("output_filename", help="desired name of output file")
parser.add_argument("-t", "--threads", type=int, default=2)
parser.add_argument("--width", type=int, default=384, help="output width")
parser.add_argument("--height", type=int, default=216, help="output height")
parser.add_argument("--framerate", type=int, default=1, help="output video framerate")
parser.add_argument("--seconds", type=int, default=10, help="length of output video")
args = parser.parse_args()
with open('generate_preview_video.sh', 'r') as file:
generate_script = file.read()
generate_previews_command = str(generate_script).format(
threads=args.threads,
tmp_prefix=args.tmp_prefix,
width=args.width,
height=args.height,
framerate=args.framerate,
output_frame_count=args.framerate*args.seconds,
input_filepath=args.input,
output_filename=args.output_filename
)
stream = os.popen(generate_previews_command)
output = stream.readlines()
tmp_dir = output[0].strip()
file_path = output[2].strip()
shutil.move(file_path, args.output_filename+ args.output_file)
shutil.rmtree(tmp_dir)
if __name__ == "__main__":
main()
答案2
下面的批处理脚本可以实现这一点。它针对 Web 进行了优化,生成一个预览视频,其中包含从原始视频中截取的 10 幅图像。每幅图像显示 1 秒。脚本会相应地获取图像样本,因此预览图像取自整个视频。
只需要 ffmpeg。(以及 ffmpeg 包的一部分 ffprobe)
输出:mp4/h.264 ~ 120Kb(无论原始视频大小)。
完全可配置。
优化了 lanczos 和锐化滤镜。
将原始视频放入 16:9(384x216)的框架中,保持原始视频的宽高比(必要时填充侧面的黑条)。
没有使用临时文件。(从 ffmpeg 到 ffmpeg 的即时管道)
HTML5友好。
这里有两个 gif 示例,仅供预览。 实际输出是mp4/h.264:
16:9 视频源。输出:
4:3 视频源。输出:
在 Windows 上运行,但可以轻松转换为 bash 脚本(Linux)。
该脚本创建一个文件夹./preview
来存储所有生成的预览:
/My videos
movie1.mp4 ~ any size
movie2.mp4 ~ any size
/preview
movie1.mp4 ~ 120Kb
movie2.mp4 ~ 120Kb
它可以被调整以在单个文件上运行。
预览生成器.cmd:
REM ----------------------------------------------
set folder=C:\My videos
set vframes=10
set width=384
set height=216
rem w = h*16/9
set filetypes=*.mp4
REM ----------------------------------------------
setlocal EnableDelayedExpansion
pushd "%folder%"
if not exist preview md preview
for /f "usebackq delims=" %%f in (`dir /b %filetypes%`) do (
if not exist "preview\%%~nf.mp4" (
for /f %%i in ('ffprobe -v error -show_entries format^=duration "%%f" -of default^=noprint_wrappers^=1:nokey^=1') do set length=%%i
set /a length=!length!+0
set /a fps=!length!/%vframes%
ffmpeg -threads 2 -i "%%f" -an -qscale:v 1 -vf "fps=1/!fps!, scale=iw*min(%width%/iw\,%height%/ih):ih*min(%width%/iw\,%height%/ih):flags=lanczos, pad=%width%:%height%:(%width%-iw*min(%width%/iw\,%height%/ih))/2:(%height%-ih*min(%width%/iw\,%height%/ih))/2, unsharp=5:5:0.5:5:5:0.5" -vframes %vframes% -f image2pipe -vcodec ppm - ^
| ffmpeg -y -threads 2 -framerate 1 -i pipe:0 -c:v libx264 -profile:v baseline -level 3.0 -tune stillimage -r 30 -pix_fmt yuv420p "preview\%%~nf.mp4"
)
cls
)
更新:
您可以轻松控制 HTML5 中的播放速率,<video>
如下所示这里
为什么不使用 gif 来代替?
.gif 格式的质量最差(像素化颜色/最多 256 种颜色)
.gif 文件大小至少大 2 倍。
构建后,您无法控制播放速度/暂停 gif 动画。