基于FFmpeg实现MP4转WebM及时长自适应的10秒剪辑生成
Fixed Timestamp Solution
Let’s start with the exact scenario you outlined: pulling 4 x 2.5-second clips from specific time points in your 12-minute input.mp4 and merging them into output.webm.
Option 1: Single Command (No Temporary Files)
You can handle both extraction and merging in one go using FFmpeg’s concat filter. Here’s the full command:
ffmpeg -i input.mp4 \ -filter_complex "\ [0:v]trim=start=150:end=152.5,setpts=PTS-STARTPTS[v0]; \ [0:a]atrim=start=150:end=152.5,asetpts=PTS-STARTPTS[a0]; \ [0:v]trim=start=300:end=302.5,setpts=PTS-STARTPTS[v1]; \ [0:a]atrim=start=300:end=302.5,asetpts=PTS-STARTPTS[a1]; \ [0:v]trim=start=450:end=452.5,setpts=PTS-STARTPTS[v2]; \ [0:a]atrim=start=450:end=452.5,asetpts=PTS-STARTPTS[a2]; \ [0:v]trim=start=600:end=602.5,setpts=PTS-STARTPTS[v3]; \ [0:a]atrim=start=600:end=602.5,asetpts=PTS-STARTPTS[a3]; \ [v0][a0][v1][a1][v2][a2][v3][a3]concat=n=4:v=1:a=1[v][a]" \ -map "[v]" -map "[a]" output.webm
Quick breakdown:
trim/atrimisolate the video/audio segments (times converted to seconds: 2.5min = 150s, end time = start + 2.5s)setpts/asetptsreset timestamps so each clip starts at 0 (required for smooth concatenation)- The
concatfilter combines all 4 clips into a single stream
Option 2: Modular Workflow (Easier Debugging)
If you prefer step-by-step control, extract clips first then merge:
- Pull individual clips:
ffmpeg -ss 150 -i input.mp4 -t 2.5 -c copy clip1.webm ffmpeg -ss 300 -i input.mp4 -t 2.5 -c copy clip2.webm ffmpeg -ss 450 -i input.mp4 -t 2.5 -c copy clip3.webm ffmpeg -ss 600 -i input.mp4 -t 2.5 -c copy clip4.webm
- Create a
clips.txtfile listing your clips:
file 'clip1.webm' file 'clip2.webm' file 'clip3.webm' file 'clip4.webm'
- Merge with the concat demuxer:
ffmpeg -f concat -safe 0 -i clips.txt -c copy output.webm
Note: -c copy copies streams directly (fast), but if your input codecs aren’t WebM-compatible, remove -c copy to let FFmpeg re-encode (or specify -c:v libvpx-vp9 -c:a opus for optimal WebM quality).
Auto-Adjusting Timestamps Based on Input Duration
Absolutely! You can dynamically calculate start times using ffprobe to get the video’s total duration, then use a script to spread the clips evenly across the video.
Bash Script Example
This script automatically spaces 4 clips across any input video, avoiding the very start/end to prevent partial clips:
#!/bin/bash INPUT="input.mp4" OUTPUT="output.webm" CLIP_DURATION=2.5 NUM_CLIPS=4 # Get total video duration in seconds (rounded to 2 decimals) TOTAL_DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$INPUT" | awk '{printf "%.2f", $0}') # Calculate even spacing with buffer at start/end BUFFER=$(echo "$CLIP_DURATION / 2" | bc -l) AVAILABLE_DURATION=$(echo "$TOTAL_DURATION - $CLIP_DURATION" | bc) INTERVAL=$(echo "$AVAILABLE_DURATION / ($NUM_CLIPS - 1)" | bc -l) # Build filter complex string FILTER_COMPLEX="" for i in $(seq 0 $((NUM_CLIPS - 1))); do START_TIME=$(echo "$BUFFER + ($i * $INTERVAL)" | bc -l) END_TIME=$(echo "$START_TIME + $CLIP_DURATION" | bc -l) FILTER_COMPLEX+="[0:v]trim=start=$START_TIME:end=$END_TIME,setpts=PTS-STARTPTS[v$i]; " FILTER_COMPLEX+="[0:a]atrim=start=$START_TIME:end=$END_TIME,asetpts=PTS-STARTPTS[a$i]; " done # Add concat filter FILTER_COMPLEX+=$(printf "[v%d][a%d]" $(seq 0 $((NUM_CLIPS - 1))) | tr '\n' ' ') FILTER_COMPLEX+="concat=n=$NUM_CLIPS:v=1:a=1[v][a]" # Run FFmpeg ffmpeg -i "$INPUT" -filter_complex "$FILTER_COMPLEX" -map "[v]" -map "[a]" "$OUTPUT"
How it works:
ffprobefetches the exact duration of your input video- Calculates start times so clips are evenly distributed (with a small buffer to avoid cutting off at the edges)
- Dynamically builds the filter complex and runs FFmpeg
Customization Tips
- To remove the buffer (start clips right at the beginning), set
BUFFER=0and adjustAVAILABLE_DURATIONto$TOTAL_DURATION - $CLIP_DURATION - For Windows, adapt this using PowerShell or WSL
- Adjust
NUM_CLIPSorCLIP_DURATIONto fit your needs
内容的提问来源于stack exchange,提问作者EmJeiEn

