学校这两天搞活动,收集来的素材音量全部不统一……我又懒得重新剪辑,遂想到使用 ffmpegloudnorm 滤镜对视频进行响度均衡。

于是写(用 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()
最后修改:2025 年 05 月 17 日
如果觉得我的文章对你有用,请随意赞赏