6月1日 23:56
FFmpeg 怎么批量转码?Shell 脚本和 Python 并行处理实战
单个文件用一条 ffmpeg 命令就行,但处理几十上百个文件就需要脚本了。批量处理的核心思路:用 Shell 循环遍历文件,用 GNU Parallel 做并行加速,用 Python 处理更复杂的逻辑。
Shell 脚本:最简单的批量转码
bash#!/bin/bash # 批量把 AVI 转成 MP4 for file in *.avi; do ffmpeg -i "$file" -c:v libx264 -crf 23 -c:a aac "${file%.avi}.mp4" done
${file%.avi} 是 Shell 的字符串截断——去掉 .avi 后缀,换成 .mp4。这个模式够用 80% 的批量场景。
递归处理子目录里的文件:
bashfind /path/to/videos -name "*.mkv" | while read file; do ffmpeg -i "$file" -c:v libx264 -crf 23 -c:a aac "${file%.mkv}.mp4" done
find 递归搜索,while read 逐行读取文件路径。比 -exec 更灵活,可以在循环里加更多逻辑。
并行加速:别让 CPU 闲着
单线程循环太慢——4 核 CPU 只用了 1 核。用 GNU Parallel 让多个 ffmpeg 同时跑:
bash# 4 个 ffmpeg 并行转码 find . -name "*.avi" | parallel -j 4 ffmpeg -i {} -c:v libx264 -crf 23 -c:a aac {.}.mp4
{} 是输入文件名,{.} 是去掉后缀的文件名。-j 4 限制并发数——设成 CPU 核心数就行,太多会抢 IO 反而变慢。
没有 GNU Parallel?用 xargs 也能并行:
bashfind . -name "*.avi" | xargs -P 4 -I {} sh -c 'ffmpeg -i "$1" -c:v libx264 -crf 23 -c:a aac "${1%.avi}.mp4"' _ {}
-P 4 控制并发数,-I {} 指定占位符。
Python:更灵活的批量处理
Shell 脚本能做的事 Python 都能做,而且更容易处理错误和复杂逻辑:
pythonimport subprocess, os, glob input_dir = "raw" output_dir = "compressed" os.makedirs(output_dir, exist_ok=True) for path in glob.glob(f"{input_dir}/*.mp4"): name = os.path.basename(path) output = os.path.join(output_dir, name) cmd = ["ffmpeg", "-i", path, "-c:v", "libx264", "-crf", "23", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", output] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode == 0: print(f"OK {name}") else: print(f"FAIL {name}: {result.stderr[:200]}")
Python 的优势:可以记录成功/失败、跳过已处理文件、按条件选不同参数。Shell 脚本做这些需要大量 if-else,可读性差。
进阶:按条件选择不同参数
不同类型的视频用不同的压缩策略——教学录屏用高 CRF + stillimage,电影用低 CRF + film:
bash#!/bin/bash for file in *.mp4; do duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$file" | cut -d. -f1) if [ "$duration" -lt 300 ]; then ffmpeg -i "$file" -c:v libx264 -crf 28 -tune stillimage -c:a aac "compressed_$file" else ffmpeg -i "$file" -c:v libx264 -crf 23 -tune film -c:a aac "compressed_$file" fi done
跳过已处理文件
批量中断后重跑,不想重复处理已经转好的文件:
bashfor file in *.mp4; do output="output/$file" [ -f "$output" ] && continue ffmpeg -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" done
一行判断搞定断点续传。Python 版可以用 os.path.exists() 做同样的事。