Communitygithub.com

ffmpeg-pipeline(视频编辑流水线)

用于脚本化视频编辑的生产级 ffmpeg 流水线:探测、旋转与 VFR 修复、HDR/HLG/Dolby Vision 到 SDR 的色调映射、Apple Log、逐片段提取、无损拼接、xfade 转换、叠加时序、PNG/alpha 叠加、字幕烧录、变速、超分以及 Instagram、TikTok、YouTube 和母版导出预设。每当使用 ffmpeg 或 Python 构建脚本进行编辑渲染时均可使用。

ffmpeg-pipeline(视频编辑流水线) 是什么?

这是一个专为脚本化视频编辑设计的 ffmpeg 流水线,旨在解决手机拍摄素材中的常见陷阱并提供生产级质量。它支持探测视频属性(包括 HDR 类型)、自动处理旋转和可变帧率(VFR)、色调映射 HDR/HLG/Dolby Vision 到 SDR,以及 Apple Log 的 LUT 转换。流水线采用逐片段提取、分级、无损拼接和过渡(xfade)的模块化架构,支持叠加 PNG/ProRes 4444/WebM 透明元素、烧录 ASS 字幕、变速(包括平滑慢动作)和多种导出预设(Instagram/TikTok/YouTube/母版/预览)。所有工具均为 Python 脚本,可方便集成到构建流程中。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Prateekiiitg56/video-editing-skills/tree/HEAD/skills/ffmpeg-pipeline

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

ffmpeg 流水线

辅助脚本位于此文件旁边的 scripts/ 目录中。使用 python <skill_dir>/scripts/<name>.py 运行它们。

  • probe.py IN... — 每个文件的一行摘要(大小、帧率、旋转、HDR、时长、音频)。
  • export.py IN OUT --preset instagram|tiktok|shorts|youtube|master|preview — 使用正确设置的最终编码。
  • frames.py IN OUTDIR --times 0.5,3,7.2 或 --every 1 — 抓取检查帧。

1. 首先探测并归一化

手机拍摄的陷阱:

  • 旋转:iPhone 存储横向像素 + 旋转标志。ffmpeg 在解码时自动旋转(-noautorotate 禁用)。OpenCV VideoCapture 可能不会自动旋转 — 检查 frame.shape 并自行旋转。
  • VFR:手机录制可变帧率。提取时转换为 CFR:-vf fps=30(或 -r 30 输出),以便帧计算精确。
  • HDR:color_transfer=arib-std-b67(HLG)或 smpte2084(PQ)。未经处理时,在 SDR 中看起来灰暗/发白。进行色调映射:
# HDR(HLG 或 PQ)-> SDR Rec.709,CPU zscale 路径
-vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p"

hable = 电影感;mobius 保持更多饱和度。如果 zscale 缺失,使用 libplacebo(-vf libplacebo=tonemapping=bt.2390:colorspace=bt709:color_primaries=bt709:color_trc=bt709)。

  • Apple Log / 相机 Log:首先应用制造商的 Log → Rec.709 LUT(lut3d=AppleLog_to_Rec709.cube),然后进行调色。
  • 10-bit 源:在重度调色时通过中间文件保持 10-bit(-pix_fmt yuv422p10le ProRes 或 yuv420p10le x265);仅在最终为社交平台导出时转换为 8-bit yuv420p。

2. 构建脚本的架构

sources/ (从不修改)
edit/
  build.py          # 从源文件和数据文件重新生成所有内容
  edl.json          # [{src, in, out, speed, grade, transition}]
  segments/         # 每个 EDL 条目对应一个经过调色和归一化的文件(近似无损)
  overlays/         # PNG / PNG 序列 / ProRes 4444 / WebM alpha
  preview.mp4, final.mp4

规则:

  1. 逐片段提取,一次完成所有剪辑操作(修剪、变速、缩放/裁剪、调色、帧率)→ 中间文件使用 -crf 10(x264)或 ProRes 422 HQ。
  2. 拼接片段:无需过渡时使用 concat demuxer 和 -c copy(所有片段必须共享编码器、分辨率、帧率、像素格式、时基)。
  3. 叠加和文本:在拼接后的视频上一次性完成,然后按交付规格编码一次。
  4. 音频单独构建(音乐 + 音效 + 对话 → 一个 WAV),然后在最终传递时混入。

精确修剪

# -ss 在 -i 之前时,在重新编码时快速且帧精确(ffmpeg >= 2.1)
ffmpeg -ss 12.40 -i IN.mov -t 2.333 -vf "fps=30,scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" \
  -c:v libx264 -crf 10 -preset medium -pix_fmt yuv420p -an seg_003.mp4

切勿对必须帧精确的修剪使用 -c copy(它会跳转到关键帧)。

从 16:9 填充到 9:16

  • 裁剪(封面):scale=-2:1920,crop=1080:1920:(iw-1080)*X:0,其中 X 为 0..1,用于选择主体位置(如果主体移动则跟踪)。
  • 模糊背景(包含):split[a][b];[a]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=40:2[bg];[b]scale=1080:-2[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2

拼接

# list.txt:  file 'segments/seg_001.mp4'  (每行一个)
ffmpeg -f concat -safe 0 -i list.txt -c copy joined.mp4

使用 xfade 进行过渡

ffmpeg -i a.mp4 -i b.mp4 -filter_complex \
 "[0:v][1:v]xfade=transition=fade:duration=0.5:offset=4.5,format=yuv420p[v]; \
  [0:a][1:a]acrossfade=d=0.5[a]" -map "[v]" -map "[a]" out.mp4

offset = 第一个片段的时长 − 过渡时长。类型:fade、fadeblack、fadewhite、wipeleft、slideup、smoothleft、circleopen、radial、zoomin、hblur、pixelize、dissolve……对于多个片段,链式偏移量需要累积(每个 xfade 会使总时长减少其持续时间)。

叠加

# 静态 PNG(全帧、透明)用于整个视频 — 无需 -loop
[0:v][1:v]overlay=0:0:format=auto
# 时间限制
overlay=0:0:enable='between(t,2.0,5.5)'
# 从输出中的 T 时刻开始的动画覆盖片段
[1:v]setpts=PTS-STARTPTS+T/TB[ov];[0:v][ov]overlay=0:0:eof_action=pass

Alpha 叠加:PNG 序列(-framerate 30 -i ov/%04d.png)、ProRes 4444(-c:v prores_ks -profile:v 4444 -pix_fmt yuva444p10le)或 VP9 WebM(-c:v libvpx-vp9 -pix_fmt yuva420p)。 对于长时间视频,不要对 4K RGBA PNG 使用 -loop 1;它会消耗大量 RAM。单个图像输入会被覆盖过滤器自动重复。

从 Python(PIL/OpenCV 合成)通过管道将原始帧传入 ffmpeg:

p = subprocess.Popen(["ffmpeg","-y","-f","rawvideo","-pix_fmt","rgb24","-s",f"{W}x{H}","-r","30","-i","-",
                      "-c:v","libx264","-crf","12","-pix_fmt","yuv420p","out.mp4"], stdin=subprocess.PIPE)
p.stdin.write(frame.tobytes())   # numpy uint8 HxWx3,每帧

以相同方式读取源帧(-f rawvideo -pix_fmt rgb24 -),这可以避免 OpenCV 的旋转和编解码器问题。

变速

  • 恒定:setpts=PTS/2(2 倍速)+ atempo=2.0(atempo 范围 0.5-100;链式处理更多)。
  • 从 30 fps 的平滑慢动作:minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:vsbmc=1,然后 setpts=2*PTS。检查边缘/手部是否扭曲。
  • 变速渐变:从时间映射逐帧渲染(参见 beat-sync),切勿堆叠多个 setpts 表达式。

字幕(最后做)

-vf "subtitles=captions.ass:fontsdir=fonts"     # ASS 提供完整的样式控制

3. 导出预设

目标视频音频备注
Instagram Reels / TikTok / Shorts1080x1920(或 2160x3840)H.264 High,30 fps,CRF 16-18,-maxrate 25M -bufsize 50M,yuv420pAAC 48 kHz 256-320k-movflags +faststart;平台会重新编码,因此提供高质量文件
YouTube 16:93840x2160 或 1920x1080,H.264 CRF 16 或 HEVC CRF 18,源帧率AAC 48 kHz 320k即使内容为 1080p,也上传 4K 以获得更好的 VP9/AV1 编码
母版ProRes 422 HQ 或 x264 CRF 10 / x265 CRF 14 -tag:v hvc1PCM 24 位 48 kHz存档
预览720p,CRF 28,-preset veryfastAAC 128k快速迭代

始终为社交平台添加 -pix_fmt yuv420p(10-bit 或 4:2:2 可能失败或上传后显示错误),并添加 -color_primaries bt709 -color_trc bt709 -colorspace bt709 以正确标记 SDR。

4. 验证

ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt -show_entries format=duration -of compact out.mp4
ffmpeg -i out.mp4 -af ebur128=peak=true -f null - 2>&1 | tail -12
ffmpeg -i out.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep pts_time   # 查看实际剪切位置

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

相关技能