使用ffmpeg根据开始和结束时间剪切视频

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

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

现在它工作得很好。

814149 次浏览

您可能在3秒标记处没有关键帧。因为非关键帧编码与其他帧不同,它们需要从上一个关键帧开始的所有数据。

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

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

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

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

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

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

    ffmpeg -i movie.mp4 -vf trim=3:8 cut.mp4

除了从第3秒到第8秒之外,放弃所有内容。

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

使用-c复制立即制作。在这种情况下,ffmpeg不会重新编码视频,只会根据大小进行剪切。

尝试使用这个。这是我发现的最快和最好的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:这是修剪视频开始的时间。
-to:指定从开始(00:01:40)到结束(00:02:12)的持续时间。
00:02:00:这是修剪视频结束的时间。
-c复制:这是一个通过流复制修剪的选项。(注意:非常快)

定时格式为:hh: mm: ss

请注意,当前高度赞成的答案已经过时,修剪将非常缓慢。有关更多信息,请查看此官方ffmpeg文章

新答案(快)

您可以让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秒的剪辑。这种方法是理想的,即使从一个大文件的深处进行剪辑,因为搜索没有被禁用,不像旧的答案。是的,我已经验证了它的框架完美。

注:开始时间为包括,结束时间通常为独家,因此为+0.01,以使其具有包容性。

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


老答案与解释(慢)

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

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

这将产生从0:45到1:00的15秒剪辑。

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

它比查找慢,因为省略的片段在被丢弃之前仍然会被处理,但据我所知,这是唯一的方法。如果你是从大文件的深处裁剪,更谨慎的做法是做数学并使用-ss作为输入。

这是我使用的,并且只需要几秒钟即可运行:

ffmpeg -i input.mp4 -ss 01:19:27 -to 02:18:51 -c:v copy -c:a copy output.mp4

参考:使用FFmpeg修剪视频文件 byAlexander Refsum Jensenius


生成的mp4文件也可以在iMovie中使用。有关使用get_duration(input_video)模型获取完整持续时间的更多信息。

如果你想连接多个过场动画,你可以使用以下Python脚本:

#!/usr/bin/env python3
import subprocess
def get_duration(input_video):cmd = ["ffprobe", "-i", input_video, "-show_entries", "format=duration","-v", "quiet", "-sexagesimal", "-of", "csv=p=0"]return subprocess.check_output(cmd).decode("utf-8").strip()

def main():name = "input.mkv"times = []times.append(["00:00:00", "00:00:10"])times.append(["00:06:00", "00:07:00"])# times = [["00:00:00", get_duration(name)]]if len(times) == 1:time = times[0]cmd = ["ffmpeg", "-i", name, "-ss", time[0], "-to", time[1], "-c:v", "copy", "-c:a", "copy", "output.mp4"]subprocess.check_output(cmd)else:open('concatenate.txt', 'w').close()for idx, time in enumerate(times):output_filename = f"output{idx}.mp4"cmd = ["ffmpeg", "-i", name, "-ss", time[0], "-to", time[1], "-c:v", "copy", "-c:a", "copy", output_filename]subprocess.check_output(cmd)
with open("concatenate.txt", "a") as myfile:myfile.write(f"file {output_filename}\n")
cmd = ["ffmpeg", "-f", "concat", "-i", "concatenate.txt", "-c", "copy", "output.mp4"]output = subprocess.check_output(cmd).decode("utf-8").strip()print(output)

if __name__ == "__main__":main()

示例脚本将剪切和合并00:00:00 - 00:00:1000:06:00 - 00:07:00之间的场景。


如果您想剪切完整的视频(如果您想将mkv格式转换为mp4),只需取消注释以下行:

# times = [["00:00:00", get_duration(name)]]

虽然我晚了6年,但我认为上面所有的答案都没有正确地解决0所问的问题。下面的bash脚本将处理以下格式的文本文件:

URL | start_time | end_time | filename

例如

https://www.youtube.com/watch?v=iUDURCrvrMI|00:02:02|00:03:41|1

并循环遍历文件,下载youtube-dl支持的文件,计算start_timeend_time之间的持续时间并将其传递给ffmpeg,因为-t实际上是持续时间,而不是真正的end_time

打电话给我,如果你有任何问题。

    for i in $(<video.txt);doURL=`echo $i | cut -d "|" -f 1`;START=`echo $i | cut -d "|" -f 2`;END=`echo $i | cut -d "|" -f 3`;FILE=`echo $i | cut -d "|" -f 4`;
SEC1=`echo $START | sed 's/^/((/; s/:/)*60+/g' | bc`SEC2=`echo $END | sed 's/^/((/; s/:/)*60+/g' | bc`
DIFFSEC=`expr ${SEC2} - ${SEC1}`
ffmpeg $(youtube-dl -g $URL | sed "s/.*/-ss $START -i &/") -t $DIFFSEC -c copy $FILE".mkv";ffmpeg -i $FILE".mkv" -f mp3 -ab 192000 -vn $FILE".mp3";rm $FILE".mkv";done;

使用-to代替-t:-to指定结束时间,-t指定持续时间

随意使用这个工具https://github.com/rooty0/ffmpeg_video_cutter我写了一段时间前几乎就是ffmpeg的cli前端……你只需要创建一个你想剪掉的yaml……像这样的东西

cut_method: delete  # we're going to delete following video fragments from a videotimeframe:- from: start   # waiting for people to join the conferenceto: 4m- from: 10m11s  # awkward silenceto: 15m50s- from: 30m5s   # Off-Topic Discussionto: end

然后运行一个工具来得到结果

我的设置:

$ ffmpegffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developersbuilt 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

首先,应该使用ffvhuff编码器对视频进行编码,以便视频可以准确地在开始和结束时间被剪切。通常,使用上面给出的其他命令,视频可能不会被剪切到特定的持续时间,因为不是每一帧都是帧内编码帧。

ffmpeg -ss 00:03:00 -i input_file.mkv -t 00:01:19 -c:v ffvhuff -pix_fmt yuv420p -y output_file.mkv

我对基于毫秒的切割时间感兴趣,并注意到可以为切割指定秒(及其分数)如下:

ffmpeg -i input.mp4 -ss 17.5 -to 20.5 output.mp4

对于持续时间而不是结束时间,使用-t而不是-to

ffmpeg -i input.mp4 -ss 17.5 -t 3 output.mp4

使用ffmpeg-python库我是这样做的…但您必须在安装ffmpeg-Library之前卸载ffmpeg…有关更多详细信息,请参阅我的github。https://github.com/johntsunami/ffmpeg-python-video-trimmer/blob/main/trimmer.py

sys.path.append(r"C:\Users\johnc\Desktop\ffmpeg\bin")stream = ffmpeg.input(r"C:/Users/johnc/Desktop/krystal.mp4",ss=57, t=55)  # ss is start time,  t IS DURATION
## SEPARATES AUDIO AND VIDEO INDEPENDENTLY BECAUSE SOMETIMES IT DOESNT HAVE AUDIO UNLESS YOU DO THIS>
audio = stream.audiovideo = stream.video   #.trim(start=60,end=115).filter('setpts','PTS-STARTPTS') ## CURRENTLY IS GOOD BUT DOESNT CUT DURATION AFTER 55 seconds
#overwrite_output overwrites it automatically if i dont want it just use outputstream = ffmpeg.output(audio, video, r'C:\Users\johnc\Desktop\output.mp4')

ffmpeg.run(stream)

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

  • transcode.sh处理转码,包括提取剪辑。
  • ff-segmenter.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开关解释。