You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FFmpeg按静音片段分割视频的脚本问题求助

Fixing FFmpeg Video Splitting by Silent Segments on Mac

Problem

I needed to split a video made up of multiple clips, each preceded by a ~5-second silent segment with a title static frame. The goal was to cut the video so every output clip includes that 5-second opening plus the following content until the next opening.

My initial bash script used FFmpeg's silencedetect to grab silent start timestamps, but when running the actual split commands, things went wrong: output clips were broken (some wouldn’t play in QuickTime), none included the title frames, and the timestamps used for splitting got garbled (e.g., 350.107 became 50.107, 1016.07 became 016.07).

Root Cause

The issue boiled down to FFmpeg reading from standard input (stdin) by default, which interfered with the while read loop in the script. As FFmpeg ran, it consumed the remaining timestamp lines from start_timestamps.txt that the loop was supposed to process, causing the timestamp values to get truncated or mixed up entirely.

Fixed Script

Here’s the corrected version of the script—you can use either of the two fixes included:

#!/bin/bash
INPUT=$1
filename=$(basename -- "$INPUT")
extension="${filename##*.}"
filename="${filename%.*}"
SILENCE_DETECT="silence_detect_logs.txt"
TIMESTAMPS="start_timestamps.txt"

if [ ! -f $TIMESTAMPS ]; then
    echo "Probing start timestamps"
    # Adjust silencedetect params to match your 5-second silent segments
    ffmpeg -i "$INPUT" -af "silencedetect=n=-50dB:d=5" -f null - 2> "$SILENCE_DETECT"
    cat "$SILENCE_DETECT" | grep "silence_start: [0-9.]*" -o | grep -E '[0-9]+(?:\.[0-9]*)?' -o > "$TIMESTAMPS"
fi

PREV=0.0
number=0

cat "$TIMESTAMPS" | (
while read ts
do
    printf -v fname -- "$filename-%02d.$extension" "$(( ++number ))"
    DURATION=$( bc <<< "$ts - $PREV")
    # Fix 1: Redirect FFmpeg's input to null device to avoid reading stdin
    ffmpeg -y -ss "$PREV" -i "$INPUT" -t "$DURATION" -c copy "$fname" < /dev/null
    # Alternative Fix: Use -nostdin flag (works for FFmpeg 3.1+)
    # ffmpeg -y -nostdin -ss "$PREV" -i "$INPUT" -t "$DURATION" -c copy "$fname"
    PREV=$ts
done
printf -v fname -- "$filename-%02d.$extension" "$(( ++number ))"
# Apply the same fix to the final segment
ffmpeg -y -ss "$PREV" -i "$INPUT" -c copy "$fname" < /dev/null
# ffmpeg -y -nostdin -ss "$PREV" -i "$INPUT" -c copy "$fname"
)

Key Fixes Explained

  1. Block FFmpeg from reading stdin:

    • Add < /dev/null to each FFmpeg command: This redirects FFmpeg’s input to the null device, so it won’t siphon the timestamp lines the loop relies on.
    • Or use the -nostdin flag (available in FFmpeg 3.1 and later): This explicitly tells FFmpeg not to read from stdin, which is a cleaner solution if your FFmpeg version supports it.
  2. Tune silencedetect for your use case:

    • I changed d=3 to d=5 to match your 5-second silent opening segments—adjust this value if your silent prefixes are shorter or longer.
    • The n=-50dB sets the volume threshold for "silence"; tweak this if your silent segments are louder or quieter than -50dB.

Why This Works

By stopping FFmpeg from reading stdin, the while read ts loop can process every timestamp in start_timestamps.txt correctly. This ensures split timings are accurate, so each output clip includes the full silent opening + title frame and the subsequent content as intended.

内容的提问来源于stack exchange,提问作者fricadelle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 14:12:50