我尝试使用以下命令使用视频的开始和结束时间来剪切视频

ffmpeg -ss 00:00:03 -t 00:00:08 -i movie.mp4 -acodec copy -vcodec copy -async 1 cut.mp4

通过使用上面的命令,我想把视频从00:00:03剪辑到00:00:08。但它并没有在这两个时间之间剪切视频,而是剪切了前11秒的视频。有人能帮我解决这个问题吗?

编辑1:

我尝试使用mark4o建议的以下命令进行切割

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4

但是显示了以下错误。

编码器“aac”是实验性的,但实验性编解码器未启用

因此,我在命令中添加了-strict -2,即,

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 -strict -2 cut.mp4

现在它工作得很好。


当前回答

我创建了一对shell脚本来处理我的代码转换需求,包括剪切广告。我想做毫秒级的精确剪辑,不介意付出重新编码的代价。

sh处理代码转换,包括提取片段。 一个可以指定广告(或片段)开始和停止的前端。

把两个脚本都放在你的路径上……和…

# Cut out everything from (3min 12.345s to 4min 1s) and (1hour 48min 0.2s to 1hour 52min 43.0s)
ff-segmenter.sh -i "input.mp4" -o "output.mp4" -x -s 3:12.345-4:01 -s 1:48:00.2-1:52:43

如果没有-x开关,选择是反向的。因此删除列表之外的所有内容。-h开关解释了这一点。

其他回答

你可能在3秒时没有关键帧。因为非关键帧编码与其他帧不同,所以它们需要从前一个关键帧开始的所有数据。

使用mp4容器,可以在不使用编辑列表重新编码的情况下切割非关键帧。换句话说,如果3s之前最接近的关键帧是0s,那么它将从0s开始复制视频,并使用编辑列表告诉玩家开始播放3秒。

如果您使用的是git master的最新ffmpeg,那么在使用您提供的命令调用时,它将使用编辑列表来完成此操作。如果这对你不起作用,那么你可能使用了旧版本的ffmpeg,或者你的播放器不支持编辑列表。有些播放器会忽略编辑列表,总是从头到尾播放文件中的所有媒体。

如果您希望精确地从非关键帧开始剪切,并希望它在不支持编辑列表的播放器上从所需的点开始播放,或者希望确保剪切的部分实际上不在输出文件中(例如,如果它包含机密信息),那么您可以通过重新编码来实现,以便在所需的开始时间精确地出现关键帧。如果不指定复制,则重新编码是默认值。例如:

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4

当重新编码时,您可能还希望包含其他与质量相关的选项或特定的AAC编码器。详细信息,视频请参见ffmpeg的x264编码指南,音频请参见AAC编码指南。

此外,-t选项指定持续时间,而不是结束时间。上面的命令将从3秒开始编码8秒的视频。从3s开始到8s结束,使用-t 5。如果您使用的是当前版本的ffmpeg,还可以在上面的命令中用-to替换-t以在指定的时间结束。

我的设置:

$ ffmpeg
ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers
  built with Apple clang version 12.0.5 (clang-1205.0.22.9)

我发现开关的顺序很重要。 如果-i不在-ss和-t之间,那么结果中有大约3秒的空白。

所以无论你使用什么额外的开关,确保这三个开关的顺序是正确的,就像这样

ffmpeg -ss 00:00:03 -i movie.mp4 -t 00:00:08 cut.mp4

新答案(快速)

您可以让bash为您计算,它以毫秒为单位工作。

toSeconds() {
    awk -F: 'NF==3 { print ($1 * 3600) + ($2 * 60) + $3 } NF==2 { print ($1 * 60) + $2 } NF==1 { print 0 + $1 }' <<< $1
}

StartSeconds=$(toSeconds "45.5")
EndSeconds=$(toSeconds "1:00.5")
Duration=$(bc <<< "(${EndSeconds} + 0.01) - ${StartSeconds}" | awk '{ printf "%.4f", $0 }')
ffmpeg -ss $StartSeconds -i input.mpg -t $Duration output.mpg

这和以前的答案一样,会产生一个15秒的片段。即使是从一个大文件的深处剪切,这个方法也是理想的,因为与旧的答案不同,查找并没有被禁用。是的,我已经验证了它是完美的框架。

注意:开始时间是包含的,结束时间通常是EXCLUSIVE,因此+0.01,使其包含。

如果你使用mpv,你可以在OSD中使用——OSD -fractions启用毫秒时间码


有解释的老答案(慢)

为了根据源视频的开始和结束时间进行剪切,并避免进行数学运算,可以将结束时间指定为输入选项,将开始时间指定为输出选项。

ffmpeg -t 1:00 -i input.mpg -ss 45 output.mpg

这将产生从0:45到1:00的15秒切割。

这是因为当给出-ss作为输出选项时,丢弃的时间仍然包含在从输入读取的总时间中,-t使用它来知道何时停止。然而,如果-ss作为输入选项,则会查找开始时间而不计算,这就是造成混淆的原因。

它比寻找慢,因为省略的段在被丢弃之前仍然被处理,但据我所知,这是唯一的方法。如果从一个大文件的深处进行剪辑,那么更谨慎的做法是只做数学运算,并使用-ss作为输入。

试试这个。这是最快和最好的ffmpeg方式,我已经找到:

ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -c copy output.mp4

这个命令在几秒钟内修剪你的视频!

命令使用说明:

-i:指定输入文件。在这种情况下,它是(input.mp4)。 -ss:与-i一起使用,在输入文件(input.mp4)中查找位置。00:01:00:这是你修剪过的视频开始的时间。 - -从开始(00:01:40)到结束(00:02:12)的时间段。00:02:00:这是你修剪过的视频的结束时间。 -c copy:这是一个通过流复制来修剪的选项。(注意:非常快)

定时格式为:hh:mm:ss

请注意,目前高度好评的答案是过时的,修剪会非常缓慢。有关更多信息,请参阅这篇ffmpeg官方文章。

我创建了一对shell脚本来处理我的代码转换需求,包括剪切广告。我想做毫秒级的精确剪辑,不介意付出重新编码的代价。

sh处理代码转换,包括提取片段。 一个可以指定广告(或片段)开始和停止的前端。

把两个脚本都放在你的路径上……和…

# Cut out everything from (3min 12.345s to 4min 1s) and (1hour 48min 0.2s to 1hour 52min 43.0s)
ff-segmenter.sh -i "input.mp4" -o "output.mp4" -x -s 3:12.345-4:01 -s 1:48:00.2-1:52:43

如果没有-x开关,选择是反向的。因此删除列表之外的所有内容。-h开关解释了这一点。