使用 FFmpeg 智能修剪长视频

使用 FFmpeg 智能修剪长视频

我希望能够使用 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 帧。或者没有达到要求的长度。

我想知道是否可以结合这些方法:

  1. 测量 I 帧的位置,例如:ffprobe -select_streams v -skip_frame nokey -show_frames -show_entries frame=pts_time input.mp4
  2. 从请求的启动到最近的 I 帧进行重新编码修剪,例如:ffmpeg -i input.mp4 -ss 13 -to 13.12345 output1.mp4
  3. 执行从 I-Frame 到 I-Frame 的复制修剪,例如:ffmpeg -i input.mp4 -ss 13.12345 -to 58.12345 -c copy output2.mp4
  4. 从最后请求的 I 帧到末尾进行重新编码修剪,例如:ffmpeg -i input.mp4 -ss 58.12345 -to 59 output3.mp4
  5. 将三个文件连接在一起,例如:
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

相关内容