学校这两天搞活动,收集来的素材音量全部不统一……我又懒得重新剪辑,遂想到使用 ffmpeg
的 loudnorm
滤镜对视频进行响度均衡。
于是写(用 deepseek 搓)了个小脚本来处理:
import ffmpeg
import os
import re
import json
from pathlib import Path
def analyze_loudness(input_path):
"""使用ffmpeg-python分析音频响度参数"""
try:
# 执行分析命令并捕获输出
stdout, stderr = (
ffmpeg
.input(input_path)
.audio.filter('loudnorm', print_format='json')
.output('pipe:', format='null')
.global_args('-loglevel', 'info')
.run(capture_stdout=True, capture_stderr=True)
)
output = stderr.decode() # 成功时从返回的stderr获取输出
except ffmpeg.Error as e:
# 失败时从异常对象获取输出
output = e.stderr.decode()
json_match = re.search(r'\{.*\}', output, re.DOTALL)
if not json_match:
raise ValueError("未找到响度分析数据")
try:
return json.loads(json_match.group())
except json.JSONDecodeError:
raise ValueError("响度数据解析失败")
def normalize_video(input_path, output_path, target_lufs=-19):
"""执行标准化处理"""
# 分析音频特征
loudness_data = analyze_loudness(input_path)
# 构建处理命令
input_stream = ffmpeg.input(input_path)
audio_stream = input_stream.audio.filter(
'loudnorm',
linear='true',
I=target_lufs,
measured_I=loudness_data['input_i'],
measured_LRA=loudness_data['input_lra'],
measured_tp=loudness_data['input_tp'],
measured_thresh=loudness_data['input_thresh'],
offset=loudness_data['target_offset'],
print_format='summary'
)
# 保留所有视频流和其他数据流
output = ffmpeg.output(
input_stream.video,
audio_stream,
output_path,
vcodec='copy', # 复制视频流
acodec='aac', # 重新编码音频
audio_bitrate='192k',
ar='48000',
y='-y' # 允许覆盖输出文件
)
try:
output.run()
except ffmpeg.Error as e:
raise RuntimeError(f"处理失败: {e.stderr.decode()}")
def batch_normalize():
"""批量处理当前目录下的视频文件"""
video_exts = ['.mp4', '.mkv', '.mov', '.avi', '.flv']
current_dir = Path.cwd()
for file in current_dir.iterdir():
if file.suffix.lower() in video_exts and '_normalized' not in file.stem:
output_name = f"{file.stem}_normalized{file.suffix}"
output_path = file.with_name(output_name)
print(f"正在处理: {file.name}")
try:
normalize_video(str(file), str(output_path))
print(f"完成: {output_name}")
except Exception:
print(f"处理 {file.name} 失败: {str(Exception)}")
if output_path.exists():
output_path.unlink() # 删除可能生成的不完整文件
if __name__ == "__main__":
# 检查ffmpeg是否可用
try:
ffmpeg.probe('')
except ffmpeg.Error:
print("错误: 请先安装ffmpeg并添加到系统路径")
exit(1)
batch_normalize()