我希望能够使用 FFmpeg 修剪长视频的开头和结尾。我可以使用以下命令执行此操作:
ffmpeg -i input.mp4 -ss 13 -to 59 output.mp4
FFmpeg 将准确查找请求的时间并对输出文件进行完整的重新编码。这将花费相对较长的时间,但会产生很好的效果。
更快的修剪方式是使用复制视频和音频数据-c copy
,例如:
ffmpeg -i input.mp4 -ss 13 -to 59 -c copy output.mp4
但是,由于修剪不是在 I 帧处进行的,因此输出视频经常不连贯:冻结直到第一个 I 帧。或者没有达到要求的长度。
我想知道是否可以结合这些方法:
- 测量 I 帧的位置,例如:
ffprobe -select_streams v -skip_frame nokey -show_frames -show_entries frame=pts_time input.mp4
- 从请求的启动到最近的 I 帧进行重新编码修剪,例如:
ffmpeg -i input.mp4 -ss 13 -to 13.12345 output1.mp4
- 执行从 I-Frame 到 I-Frame 的复制修剪,例如:
ffmpeg -i input.mp4 -ss 13.12345 -to 58.12345 -c copy output2.mp4
- 从最后请求的 I 帧到末尾进行重新编码修剪,例如:
ffmpeg -i input.mp4 -ss 58.12345 -to 59 output3.mp4
- 将三个文件连接在一起,例如:
echo "file 'output1.mp4'" > mylist.txt
echo "file 'output2.mp4'" >> mylist.txt
echo "file 'output3.mp4'" >> mylist.txt
ffmpeg -y -f concat -safe 0 -i mylist.txt -c copy output4.mp4
在我的测试中,这会产生播放效果不佳的视频。帧冻结、音频不同步等。我想是因为我要连接的三个文件格式不完全相同(帧速率、关键帧的位置等)。但是,如果我能让它工作的话,它肯定会更快地从长文件中剪掉一些小片段。
这种方法可能有效吗?如果有效,我该如何让 FFmpeg 生成三个可以完美连接的中间文件?
答案1
简短的回答是,你不会。问题是 - 成功连接需要使用相同参数编码的文件。或者非常接近相同。
您正在编码的剪辑不太可能与原始未编码部分相匹配。当您尝试这样做时,通常会出现问题。这是所谓的无损剪切很少能正常工作的原因之一。
答案2
它可以工作,但会产生多个警告:Non-monotonous DTS in output stream...
#!/bin/bash
f="input.mp4"
IFS=, read VID TBN BRV <<< $(ffprobe -v 0 -select_streams v:0 -show_entries stream=codec_name,time_base,bit_rate -of csv=p=0 "$f")
TBN=${TBN#*/}
echo $VID $TBN $BRV
IFS=, read AUD BRA <<< $(ffprobe -v 0 -select_streams a:0 -show_entries stream=codec_name,bit_rate -of csv=p=0 "$f")
echo $AUD $BRA
PTS=($(ffprobe -v error -show_entries frame=pts_time -of csv=p=0 -select_streams v:0 -skip_frame nokey "$f"))
T01=9.1
T04=22.6
for T02 in ${PTS[@]}; do
if (($(echo "$T01 < $T02" | bc))); then
break
fi
done
echo -ss $T01 -to $T02
for t in ${PTS[@]}; do
if (($(echo "$T04 < $t" | bc))); then
break
fi
T03=$t
done
echo -ss $T03 -to $T04
ffmpeg -ss $T01 -to $T02 -i "$f" -c:v $VID -b:v $BRV -c:a $AUD -b:a $BRA -video_track_timescale $TBN -y 1.mp4 -hide_banner
ffmpeg -ss $T02 -to $T03 -i "$f" -c copy -y 2.mp4 -hide_banner
ffmpeg -ss $T03 -to $T04 -i "$f" -c:v $VID -b:v $BRV -c:a $AUD -b:a $BRA -video_track_timescale $TBN -y 3.mp4 -hide_banner
echo "file '1.mp4'" > list.txt
echo "file '2.mp4'" >> list.txt
echo "file '3.mp4'" >> list.txt
ffmpeg -f concat -i list.txt -c copy -y output.mkv -hide_banner
mpv output.mkv