下面提供一个基于 Python 的综合程序,整合了 场景变化分割 和 静音片段分割 两种智能分割逻辑,可自动检测分割点并批量切割视频。程序封装了 FFmpeg 调用、时间戳解析和视频切割流程,无需手动处理中间文件。

程序功能

  • 支持两种分割模式:scene(场景突变分割)、silence(静音片段分割)
  • 自动检测分割时间点,无需手动提取
  • 批量切割视频为多个片段,保持原编码(快速切割)
  • 可自定义检测阈值(场景敏感度 / 静音分贝)

程序代码(Python)

python

运行

import os
import re
import subprocess
import argparse
from typing import List, Tuple


def check_dependencies() -> None:
    """检查是否安装 ffmpeg 和 ffprobe"""
    try:
        subprocess.run(["ffmpeg", "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        subprocess.run(["ffprobe", "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
    except (subprocess.CalledProcessError, FileNotFoundError):
        raise RuntimeError("请先安装 ffmpeg 并确保其在环境变量中(https://ffmpeg.org/download.html)")


def get_video_duration(input_path: str) -> float:
    """用 ffprobe 获取视频总时长(秒)"""
    cmd = [
        "ffprobe", "-v", "error",
        "-show_entries", "format=duration",
        "-of", "default=noprint_wrappers=1:nokey=1",
        input_path
    ]
    result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    return float(result.stdout.strip())


def detect_scene_changes(input_path: str, threshold: float = 0.3, min_duration: float = 0.1) -> List[float]:
    """检测场景变化时间点(返回分割时间戳列表)"""
    cmd = [
        "ffmpeg", "-i", input_path,
        "-filter:v", f"scenedetect=threshold={threshold}:duration={min_duration}, metadata=print:file=-",
        "-f", "null", "-"
    ]
    result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    output = result.stdout

    # 从输出中提取 pts_time(场景切换时间)
    time_pattern = re.compile(r"pts_time:(\d+\.\d+)")
    times = [float(m.group(1)) for m in time_pattern.finditer(output)]
    
    # 去重并排序(确保时间递增)
    times = sorted(list(set(times)))
    return times


def detect_silence(input_path: str, noise_threshold: str = "-50dB", min_silence: float = 1.0) -> List[float]:
    """检测静音片段,返回静音结束时间作为分割点"""
    cmd = [
        "ffmpeg", "-i", input_path,
        "-filter:a", f"silencedetect=noise={noise_threshold}:d={min_silence}",
        "-f", "null", "-"
    ]
    result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    output = result.stderr  # silencedetect 输出在 stderr

    # 提取静音结束时间(silence_end)
    end_pattern = re.compile(r"silence_end: (\d+\.\d+)")
    times = [float(m.group(1)) for m in end_pattern.finditer(output)]
    
    # 去重并排序
    times = sorted(list(set(times)))
    return times


def split_video(input_path: str, output_prefix: str, split_times: List[float], duration: float) -> None:
    """根据分割时间戳切割视频"""
    # 确保分割时间包含起点(0)和终点(总时长)
    split_times = [0.0] + split_times + [duration]
    # 去重并再次排序(避免重复时间)
    split_times = sorted(list(set(split_times)))

    # 循环切割每个片段
    for i in range(len(split_times) - 1):
        start = split_times[i]
        end = split_times[i + 1]
        if end - start < 0.5:  # 跳过短于0.5秒的片段
            continue
        
        output_path = f"{output_prefix}_{i:03d}.mp4"
        cmd = [
            "ffmpeg", "-ss", str(start),  # 开始时间
            "-i", input_path,
            "-to", str(end),  # 结束时间
            "-c", "copy",  # 直接复制编码(快速)
            "-y",  # 覆盖已有文件
            output_path
        ]
        print(f"切割片段 {i+1}/{len(split_times)-1}:{start:.2f}s -> {end:.2f}s -> {output_path}")
        subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)


def main():
    parser = argparse.ArgumentParser(description="FFmpeg 智能分割视频(场景变化/静音检测)")
    parser.add_argument("input", help="输入视频文件路径(如 input.mp4)")
    parser.add_argument("-m", "--mode", choices=["scene", "silence"], required=True, 
                        help="分割模式:scene(场景变化)/ silence(静音片段)")
    parser.add_argument("-o", "--output", default="output", help="输出片段前缀(默认:output)")
    # 场景检测参数
    parser.add_argument("--scene-threshold", type=float, default=0.3, 
                        help="场景变化阈值(0-1,越小越敏感,默认0.3)")
    parser.add_argument("--scene-min-duration", type=float, default=0.1, 
                        help="忽略短于该时长的场景变化(秒,默认0.1)")
    # 静音检测参数
    parser.add_argument("--silence-noise", default="-50dB", 
                        help="静音分贝阈值(默认-50dB,值越小越严格)")
    parser.add_argument("--silence-min", type=float, default=1.0, 
                        help="最小静音时长(秒,默认1.0)")
    
    args = parser.parse_args()

    # 检查依赖和输入文件
    check_dependencies()
    if not os.path.exists(args.input):
        raise FileNotFoundError(f"输入文件不存在:{args.input}")

    # 获取视频总时长
    duration = get_video_duration(args.input)
    print(f"视频总时长:{duration:.2f}秒")

    # 检测分割时间点
    print(f"开始{args.mode}检测...")
    if args.mode == "scene":
        split_times = detect_scene_changes(
            args.input, 
            threshold=args.scene_threshold, 
            min_duration=args.scene_min_duration
        )
    else:  # silence
        split_times = detect_silence(
            args.input, 
            noise_threshold=args.silence_noise, 
            min_silence=args.silence_min
        )

    if not split_times:
        print("未检测到分割点,不进行切割")
        return

    print(f"检测到 {len(split_times)} 个分割点:{[f'{t:.2f}s' for t in split_times]}")

    # 切割视频
    split_video(args.input, args.output, split_times, duration)
    print("分割完成!")


if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"错误:{str(e)}")
        exit(1)

使用方法

前提条件
  1. 安装 Python 3.x(https://www.python.org/downloads/
  2. 安装 FFmpeg 并添加到环境变量(https://ffmpeg.org/download.html,确保ffmpegffprobe可在命令行直接调用)
基本命令
1. 基于场景变化分割(默认参数)

bash

运行

python split_video.py input.mp4 -m scene -o scene_output
  • 功能:检测视频中场景突变的时间点,切割为多个片段
  • 输出文件:scene_output_000.mp4scene_output_001.mp4...
2. 基于静音片段分割(默认参数)

bash

运行

python split_video.py input.mp4 -m silence -o silence_output
  • 功能:检测视频中持续 1 秒以上的静音片段,在静音结束处分割
  • 输出文件:silence_output_000.mp4silence_output_001.mp4...
自定义参数
调整场景检测敏感度

bash

运行

python split_video.py input.mp4 -m scene --scene-threshold 0.2 --scene-min-duration 0.5 -o scene_output
  • --scene-threshold 0.2:降低阈值(更敏感,检测更多场景变化)
  • --scene-min-duration 0.5:忽略短于 0.5 秒的场景变化(减少误检)
调整静音检测严格度

bash

运行

python split_video.py input.mp4 -m silence --silence-noise -60dB --silence-min 2.0 -o silence_output
  • --silence-noise -60dB:更严格的静音标准(只有更安静的片段才视为静音)
  • --silence-min 2.0:只检测持续 2 秒以上的静音片段

注意事项

  1. 切割速度:使用-c copy直接复制编码,速度极快,但可能在非关键帧处切割导致片段开头 / 结尾有轻微卡顿(如需精确可去掉-c copy,但会重新编码,速度较慢)。
  2. 阈值调整:不同视频(如明暗对比、音量大小)需要调整阈值,建议先小范围测试。
  3. 短片段过滤:程序会自动跳过短于 0.5 秒的片段,避免生成无效文件。

通过这个程序,可以快速实现基于内容的智能视频分割,无需手动处理中间步骤。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐