当サイトにはアフィリエイト広告が含まれます。なおレビューは私の感想を書いており、内容を指示するご依頼はお断りしています

【python】MP4無音部分トリミング方法

FFmpegとPythonスクリプトを使って、「すべての無音カット&結合」を自動で実行する手順を備忘録として残しておく

1. 入力ファイルの音量ログ「detect.log」を作成する

ffmpeg -i "input.mp4" -af silencedetect=n=-20dB:d=1.0 -f null - 2> detect.log

上記の引数である無音扱いとする音量の閾値「-20dB」や、無音範囲の秒数「1.0」は適宜調整する

2. 音量ログ「detect.log」をもとに、ファイルをトリミングする「trim_slience.py」を作成する

import re
import subprocess


LOG_FILE = "detect.log"
INPUT_FILE = "input.mp4"
OUTPUT_FILE = "input_trimed.mp4"

# ログを解析し、無音区間を抽出する関数
def parse_silence_log(log_file):
    with open(log_file, 'r') as f:
        log_data = f.read()

    # silence_start と silence_end の時間を抽出する正規表現
    start_matches = re.findall(r'silence_start: (\d+\.?\d*)', log_data)
    end_matches = re.findall(r'silence_end: (\d+\.?\d*)', log_data)

    silence_intervals = []
    # startとendのリストの短い方に合わせて処理
    for start, end in zip(start_matches, end_matches):
        silence_intervals.append((float(start), float(end)))
        
    return silence_intervals

# 非無音区間(残す部分)のリストを生成する関数
def get_non_silence_intervals(silence_intervals, total_duration):
    non_silence_intervals = []
    current_time = 0.0
    
    for start, end in silence_intervals:
        # 現在の時間から無音開始までが、残すべき「音あり」区間
        if start > current_time:
            non_silence_intervals.append((current_time, start))
        
        # 次の「音あり」区間の開始時間を、無音終了時間にする
        current_time = end

    # 最後の無音終了時間から動画の最後までも「音あり」区間として追加
    if current_time < total_duration:
        non_silence_intervals.append((current_time, total_duration))

    return non_silence_intervals

# 動画の総時間を取得する関数 (FFprobeを使用)
def get_total_duration(input_file):
    # FFprobeで duration のメタデータを取得
    cmd = [
        'ffprobe', '-v', 'error', '-show_entries', 'format=duration', 
        '-of', 'default=noprint_wrappers=1:nokey=1', input_file
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        return float(result.stdout.strip())
    except Exception as e:
        print(f"Error getting duration: {e}")
        return None

# メイン処理
if __name__ == "__main__":
    total_duration = get_total_duration(INPUT_FILE)
    if total_duration is None:
        exit(1)

    silence_intervals = parse_silence_log(LOG_FILE)
    non_silence_intervals = get_non_silence_intervals(silence_intervals, total_duration)

    if not non_silence_intervals:
        print("No non-silent intervals found. Exiting.")
        exit(0)

    # 結合・カット処理を行うFFmpegコマンドを生成 (select/concatフィルター)
    
    # 複数の区間を結合するための select フィルターの式を生成
    select_filters = []
    for start, end in non_silence_intervals:
        # 'between(t,開始,終了)' を使って、指定区間を選択
        select_filters.append(f"between(t,{start},{end})")
    
    # 全ての区間をORで結合
    select_expr = '+'.join(select_filters)
    
    # 最終的なFFmpegコマンド
    final_command = [
        'ffmpeg', '-i', INPUT_FILE, 
        '-filter_complex', f'[0:v]select=\'{select_expr}\',setpts=N/FRAME_RATE/TB[v];[0:a]aselect=\'{select_expr}\',asetpts=N/SR/TB[a]',
        '-map', '[v]', '-map', '[a]',
        '-c:v', 'libx264', '-crf', '23', '-preset', 'fast',
        '-c:a', 'aac', '-b:a', '192k',
        OUTPUT_FILE
    ]

    print("--- 実行するFFmpegコマンド ---")
    print(" ".join(final_command))
    print("--------------------------")
    
    # 結合コマンドの実行
    print("Starting trimming and concatenation...")
    try:
        subprocess.run(final_command, check=True)
        print(f"\n✅ Success! Output saved to: {OUTPUT_FILE}")
    except subprocess.CalledProcessError as e:
        print(f"\n❌ FFmpeg command failed with error: {e}")

3. 実行する

python trim_slience.py

完了すると無音箇所がカットされて結合されたファイルinput_trim.mp4が作成される