FFmpeg: 如何高效地分割视频?

115

我希望将一个大的AVI视频分成两个较小的连续视频。我正在使用FFmpeg。

一种方法是运行两次FFmpeg:

ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 output1.avi
ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:30:00 -t 00:30:00 output2.avi

根据ffmpeg的manpage,我可以使用一行代码从一个输入文件中生成多个输出文件:

ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 output1.avi \
   -vcodec copy -acodec copy -ss 00:30:00 -t 00:30:00 output2.avi

我的问题是,后一种方法是否节省计算时间和内存?

5
使用“-vcodec copy -acodec copy”非常快速 :) - Savas Adar
第一个示例按顺序执行,而第二个示例使用线程。两者都会做同样的事情,不应该出现明显的加速。但为了简化事情,您可以使用ffmpeg流段复用器:http://www.ffmpeg.org/ffmpeg-all.html#segment_002c-stream_005fsegment_002c-ssegment - Mladen B.
1
这个例子看起来非常错误!ffmpeg文档关于-ss选项的说明是:“当作为输入选项(在-i之前)使用时,将在此输入文件中查找位置。当作为输出选项(在输出文件名之前)使用时,解码但丢弃输入,直到时间戳达到位置。”你正在输出位置使用它,因此你的第二行浪费了解码和丢弃前30分钟的努力。 - Lucian Wischik
如果TS文件包含多个节目(通过DVB-T捕获的电视节目),该如何分割呢?例如,我有一个包含足球、烹饪和卡通的TS文件。我怎样能将这个TS文件分割成3个mpg文件? - Dr.jacky
1
@LucianWischik 但在第二个分割中使用“-ss”作为输入选项可能不准确,因此您可能无法获得干净的分割。在我看来,OP的示例更正确。 - jiggunjer
显示剩余4条评论
12个回答

93

FFmpeg维基百科在提到“如何高效分割视频”时链接回了这个页面。我对这个页面是否能回答这个问题表示怀疑,所以我按照@AlcubierreDrive的建议进行了比较。

前言(2023年):“快速”定位

以下比较是使用输出定位进行编写的。通过在输入文件之前添加-ss进行“快速定位”(输入定位),它们可以更加快速

在FFmpeg 2.1之前,这种类型的定位可能不准确,但现在不再是这样了

比较:一个命令与两个独立命令

echo "Two commands" 
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 -sn test1.mkv
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:30:00 -t 01:00:00 -sn test2.mkv
echo "One command" 
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 -sn test3.mkv \
  -vcodec copy -acodec copy -ss 00:30:00 -t 01:00:00 -sn test4.mkv

哪个输出...

Two commands
real    0m16.201s
user    0m1.830s
sys 0m1.301s

real    0m43.621s
user    0m4.943s
sys 0m2.908s

One command
real    0m59.410s
user    0m5.577s
sys 0m3.939s

我测试了一个SD和HD文件,经过几次运行和一点数学计算。
Two commands SD 0m53.94 #2 wins  
One command  SD 0m49.63  

Two commands SD 0m55.00  
One command  SD 0m52.26 #1 wins 

Two commands SD 0m58.60 #2 wins  
One command  SD 0m58.61 

Two commands SD 0m54.60  
One command  SD 0m50.51 #1 wins 

Two commands SD 0m53.94  
One command  SD 0m49.63 #1 wins  

Two commands SD 0m55.00  
One command  SD 0m52.26 #1 wins 

Two commands SD 0m58.71  
One command  SD 0m58.61 #1 wins

Two commands SD 0m54.63  
One command  SD 0m50.51 #1 wins  

Two commands SD 1m6.67s #2 wins  
One command  SD 1m20.18  

Two commands SD 1m7.67  
One command  SD 1m6.72 #1 wins

Two commands SD 1m4.92  
One command  SD 1m2.24 #1 wins

Two commands SD 1m1.73  
One command  SD 0m59.72 #1 wins

Two commands HD 4m23.20  
One command  HD 3m40.02 #1 wins

Two commands SD 1m1.30  
One command  SD 0m59.59 #1 wins  

Two commands HD 3m47.89  
One command  HD 3m29.59 #1 wins  

Two commands SD 0m59.82  
One command  SD 0m59.41 #1 wins  

Two commands HD 3m51.18  
One command  HD 3m30.79 #1 wins  

SD文件 = 1.35GB的DVB传输流
HD文件 = 3.14GB的DVB传输流

结论

如果你处理的是高清文件,使用单一命令更好,这符合手册中关于在输入文件后使用-ss进行“慢速搜索”的建议。标清文件之间几乎没有差别。


如果TS文件包含多个节目(由DVB-T捕获的电视节目),该如何拆分?例如,我有一个包含足球+烹饪+卡通的TS文件。我该如何将这个TS文件拆分成3个mpg文件? - Dr.jacky
我在这里的问题中使用了你的答案(http://stackoverflow.com/questions/41983940/ffmpeg-disassemble-video-into-parts),但它没有起作用,你认为我做错了什么? - utdev
2
在我的情况下,结果文件只有音频而没有视频。经过一些实验,我找到了解决方案。需要像这样指定“不修改质量”的关键字 -q:v 0 - oklas
这篇帖子相当古老,所以我在其中加入了一个关于“快速寻找”(输入寻找)的前言,这在原始答案中只是一瞥。它实际上将我的分割速度提高了十倍,同时保持了准确性。原始答案建议使用组合寻找来加快速度,但自FFMpeg 2.1以来,这已不再需要,也不建议这样做。 - undefined

24

这是一个有用的脚本,它可以帮助您自动分割视频:使用ffmpeg分割视频的脚本

#!/bin/bash
 
# Written by Alexis Bezverkhyy <alexis@grapsus.net> in 2011
# This is free and unencumbered software released into the public domain.
# For more information, please refer to <http://unlicense.org/>
 
function usage {
        echo "Usage : ffsplit.sh input.file chunk-duration [output-filename-format]"
        echo -e "\t - input file may be any kind of file reconginzed by ffmpeg"
        echo -e "\t - chunk duration must be in seconds"
        echo -e "\t - output filename format must be printf-like, for example myvideo-part-%04d.avi"
        echo -e "\t - if no output filename format is given, it will be computed\
 automatically from input filename"
}
 
IN_FILE="$1"
OUT_FILE_FORMAT="$3"
typeset -i CHUNK_LEN
CHUNK_LEN="$2"
 
DURATION_HMS=$(ffmpeg -i "$IN_FILE" 2>&1 | grep Duration | cut -f 4 -d ' ')
DURATION_H=$(echo "$DURATION_HMS" | cut -d ':' -f 1)
DURATION_M=$(echo "$DURATION_HMS" | cut -d ':' -f 2)
DURATION_S=$(echo "$DURATION_HMS" | cut -d ':' -f 3 | cut -d '.' -f 1)
let "DURATION = ( DURATION_H * 60 + DURATION_M ) * 60 + DURATION_S"
 
if [ "$DURATION" = '0' ] ; then
        echo "Invalid input video"
        usage
        exit 1
fi
 
if [ "$CHUNK_LEN" = "0" ] ; then
        echo "Invalid chunk size"
        usage
        exit 2
fi
 
if [ -z "$OUT_FILE_FORMAT" ] ; then
        FILE_EXT=$(echo "$IN_FILE" | sed 's/^.*\.\([a-zA-Z0-9]\+\)$/\1/')
        FILE_NAME=$(echo "$IN_FILE" | sed 's/^\(.*\)\.[a-zA-Z0-9]\+$/\1/')
        OUT_FILE_FORMAT="${FILE_NAME}-%03d.${FILE_EXT}"
        echo "Using default output file format : $OUT_FILE_FORMAT"
fi
 
N='1'
OFFSET='0'
let 'N_FILES = DURATION / CHUNK_LEN + 1'
 
while [ "$OFFSET" -lt "$DURATION" ] ; do
        OUT_FILE=$(printf "$OUT_FILE_FORMAT" "$N")
        echo "writing $OUT_FILE ($N/$N_FILES)..."
        ffmpeg -i "$IN_FILE" -vcodec copy -acodec copy -ss "$OFFSET" -t "$CHUNK_LEN" "$OUT_FILE"
        let "N = N + 1"
        let "OFFSET = OFFSET + CHUNK_LEN"
done

3
对于SEARAS所说的评论,同样适用于您。编写批处理文件只有在绝对必要且没有其他选择时才有用,因为它不具备可移植性。例如,在Windows上无法运行相同的脚本。当存在更通用/可移植的方法时,应避免使用批处理脚本以节省转换所需的时间。由于ffmpeg有多种解决此问题的方法,正确的方法是单独使用ffmpeg,而不需要脚本的帮助。 - Mladen B.
不错的脚本。是你写的吗?它在GitHub上吗?我想提出一些建议 :) - voices
1
对于 MP4 文件,场景已经损坏。最好使用以下命令替换副本:-vcodec libx264 -acodec aac。 - kishu
链接已损坏。 - Felix Jassler
这里最好修复一行代码:#let "DURATION = ( DURATION_H * 60 + DURATION_M ) * 60 + DURATION_S" DURATION=$( echo "($DURATION_H * 60 + $DURATION_M ) * 60 + $DURATION_S" | bc)我在这里有一个错误,这是更好的计算方法。 - pavlovma007

10

根据我的经验,不要使用ffmpeg进行分割/合并操作。MP4Box比ffmpeg更快、更轻量级。请尝试一下。 例如,如果您想将一个1400mb的MP4文件分成两个700mb的部分,可以使用以下命令行: MP4Box -splits 716800 input.mp4 例如,要将两个文件连接起来,可以使用以下命令:

MP4Box -cat file1.mp4 -cat file2.mp4 output.mp4

如果你需要按时间分割,可以使用 -splitx StartTime:EndTime

MP4Box -add input.mp4 -splitx 0:15 -new split.mp4


1
如何使用Mp4Box裁剪视频并生成多个文件? 例如,我需要在以下秒数范围内分割子剪辑: 5 - 8 , 21 - 30 , 12 - 18 因此,我需要生成三个输出视频文件。 - Majid Abdolhosseini
使用 EndTime-t 更容易。 - vmassuchetto
1
MP4Box的效果稍微好一些,但我不推荐使用。有用户写道:“当我用ffmpeg剪辑时,片段没有从关键帧开始。”这可能是由于使用了-c copy。如果您想让视频从正确的帧开始,可能需要重新编码它。因此,在OP中不要使用-c copy-vcodec copy。例如:ffmpeg -i in.mp4 -ss 00:00:00.46 out.mp4 -y。https://dev59.com/marka4cB1Zd3GeqPgqPu 指出这可能与“视频压缩的工作原理(P和B帧不能独立存在)”有关。 - Rublacava
这对我从我的输入.mp4的开头分离出一个块时有效,但是当从输入的6.5秒开始分离块时,结果是几秒钟的全绿色,并覆盖了一些“delta伪影”。我想知道这是否可能是因为根据man MP4Box:“输入文件必须具有足够的随机访问点才能进行拆分。这在某些视频文件中可能不是这种情况,其中仅视频轨道的第一个样本是关键帧。为了拆分这样的文件,您将不得不使用真正的视频编辑器并重新编码内容。” - Jonathan Hartley
极大的注意事项:这仅适用于mp4文件。即使问题本身不使用mp4文件,因此这在OP的示例中将无法工作。 - NeuroXc
显示剩余2条评论

6

2

这是一个简单的Windows批处理文件,可以将输入文件分成50个部分。每个部分的长度为1分钟。很抱歉这个脚本很愚蠢。我希望拥有一个愚蠢的Windows脚本比没有更好。也许它能帮助某些人。 (基于此网站上的“bat文件循环”)

set var=0
@echo off
:start
set lz=
if %var% EQU 50 goto end
if %var% LEQ 9 set lz=0
echo part %lz%%var%
ffmpeg -ss 00:%lz%%var%:00 -t 00:01:00 -i %1 -acodec copy -vcodec copy %2_%lz%%var%.mp4
set /a var+=1
goto start

:end
echo var has reached %var%.
exit

这本质上是问题中第一个代码片段(连续的ffmpeg命令)重写为循环。这并没有回答那种方法或单个命令会“节省计算时间和内存”的问题。 - Lance U. Matthews

2
后一种方法会节省计算时间和内存吗?
您提供的这两个示例之间没有太大区别。第一个示例按顺序分割视频,分为两个步骤,而第二个示例使用线程同时进行操作。不会有特别明显的加速。您可以阅读更多关于使用FFmpeg创建多个输出的内容。
此外,在最近的FFmpeg中,您可以使用流切片器复用器,它可以:
将输出流输出到几乎固定时长的多个单独文件中。输出文件名模式可以设置为类似于image2的方式。

1

我没有测试过,但这看起来很有前途:

基本流分段器

显然它将AVI文件分成相同大小的片段,这意味着这些块不会失去质量或增加内存或必须重新计算。

它还使用了编解码器副本 - 这是否意味着它可以处理非常大的流?因为这是我的问题,我想拆分我的AVI文件,以便我可以使用一个滤镜来消除扭曲。但整个AVI运行数小时。


0

从以下位置分割视频:
-i - 输入视频文件
-ss - 开始时间
-t - 分割视频的持续时间
-c - (编解码器)
-c - 复制(不更改编解码器)

ffmpeg -i input.mp4 -ss 00:00:50.0 -codec copy -t 30 output.mp4

0

这个命令将把源视频分成两个部分。

ffmpeg -i input.avi -t 00:00:50 -c copy output1.avi -ss 00:00:50 -codec copy output2.avi


0

我建议指定视频编解码器为H.264,因为帧率和视频编解码器是不同且未知的。仅使用复制可能会使ffmpeg跳过空白帧并缩小到所需时长以下的片段

ffmpeg -y -i {sourcePath} -ss 30 -t 15 -c:v libx264 -c:a copy {outputPath}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接