我正在尝试使用ffmpeg连接两个mp4文件。我需要这是一个自动过程,因此我选择了ffmpeg。我将这两个文件转换为.ts文件,然后将它们连接起来,然后尝试对连接的.ts文件进行编码。文件是h264和aac编码的,我希望保持质量相同或尽可能接近原始。

ffmpeg -i part1.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part1.ts
ffmpeg -i part2.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part2.ts
cat part1.ts part2.ts > parts.ts
ffmpeg -y -i parts.ts -acodec copy -ar 44100 -ab 96k -coder ac -vbsf h264_mp4toannexb parts.mp4

不幸的是,我在编码过程中从ffmpeg返回以下错误消息:

[h264 @ 0x1012600]sps_id out of range
[h264 @ 0x1012600]non-existing SPS 0 referenced in buffering period
[h264 @ 0x1012600]sps_id out of range
[h264 @ 0x1012600]non-existing SPS 0 referenced in buffering period
[NULL @ 0x101d600]error, non monotone timestamps 13779431 >= 13779431kbits/s    
av_interleaved_write_frame(): Error while opening file

这种情况大约发生在编码的一半,这让我认为不能将两个.ts文件合并在一起并使其正常工作。


当前回答

这是我使用命令替换和concat视频过滤器(这将重新编码)来加入一个满是MP4文件的目录的方法-我认为其他人会从这一行代码中得到一些用处,特别是如果你有很多文件(我刚刚一下子加入了17个文件):

ffmpeg $(for f in *.mp4 ; do echo -n "-i $f "; done) -filter_complex \
"$(i=0 ; for f in *.mp4 ; do echo -n "[$i:v] [$i:a] " ; i=$((i+1)) ; done \
&& echo "concat=n=$i:v=1:a=1 [v] [a]")" -map "[v]" -map "[a]" output.mp4

注意:此命令按照文件的命名顺序(即,如果运行ls*.mp4,则与文件的显示顺序相同)连接文件-在我的情况下,它们都有一个曲目号,因此效果很好。

其他回答

这对我(在窗户上)有用

ffmpeg -i "concat:input1|input2" -codec copy output

一个例子。。。

ffmpeg -i "concat:01.mp4|02.mp4" -codec copy output.mp4

蟒蛇

使用一些python代码来处理文件夹中的多个mp4(从python.org安装python,复制并粘贴此代码并将其保存到名为mp4.py的文件中,然后从文件夹中打开的cmd中使用python-mp4.py运行它,文件夹中的所有mp4将被连接)

import glob
import os

stringa = ""
for f in glob.glob("*.mp4"):
    stringa += f + "|"
os.system("ffmpeg -i \"concat:" + stringa + "\" -codec copy output.mp4")

Python版本2

摘自我在博客上的帖子,这是我在python中的做法:

import os
import glob

def concatenate():
    stringa = "ffmpeg -i \"concat:"
    elenco_video = glob.glob("*.mp4")
    elenco_file_temp = []
    for f in elenco_video:
        file = "temp" + str(elenco_video.index(f) + 1) + ".ts"
        os.system("ffmpeg -i " + f + " -c copy -bsf:v h264_mp4toannexb -f mpegts " + file)
        elenco_file_temp.append(file)
    print(elenco_file_temp)
    for f in elenco_file_temp:
        stringa += f
        if elenco_file_temp.index(f) != len(elenco_file_temp)-1:
            stringa += "|"
        else:
            stringa += "\" -c copy  -bsf:a aac_adtstoasc output.mp4"
    print(stringa)
    os.system(stringa)

concatenate()

FFmpeg有三种连接方法:

1.凹面视频过滤器

如果您的输入没有相同的参数(宽度、高度等),或者格式/编解码器不相同,或者您想要执行任何过滤,请使用此方法。

注意,该方法对所有输入执行重新编码。如果要避免重新编码,可以只对不匹配的输入进行重新编码,以便它们共享相同的编解码器和其他参数,然后使用concat解复用器避免对所有内容进行重新编码。

ffmpeg -i opening.mkv -i episode.mkv -i ending.mkv \
-filter_complex "[0:v] [0:a] [1:v] [1:a] [2:v] [2:a] \
concat=n=3:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" output.mkv

2.凹面分离器

如果您希望避免重新编码,并且您的格式不支持文件级连接(一般用户使用的大多数文件不支持文件级别连接),请使用此方法。

$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'
    
$ ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4

对于Windows:

(echo file 'first file.mp4' & echo file 'second file.mp4' )>list.txt
ffmpeg -safe 0 -f concat -i list.txt -c copy output.mp4

3.concat协议

将此方法用于支持文件级连接的格式(MPEG-1、MPEG-2ps、DV)。不要与MP4一起使用。

ffmpeg -i "concat:input1|input2" -codec copy output.mkv

该方法不适用于许多格式,包括MP4,因为这些格式的性质以及该方法执行的简单连接。


如果对使用哪种方法有疑问,请尝试concat解复用器。

另请参见

FFmpeg常见问题解答:如何加入视频文件?FFmpeg Wiki:如何连接(连接、合并)媒体文件

关于ffmpeg中各种连接方式的详细文档可以在这里找到。

您可以使用“Concat filter”进行快速连接。

它执行重新编码。当输入具有不同的视频/音频格式时,此选项最佳。

对于合并2个文件:

ffmpeg -i input1.mp4 -i input2.webm \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0] concat=n=2:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" output.mp4

对于连接3个文件:

ffmpeg -i input1.mp4 -i input2.webm -i input3.mp4 \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0] [2:v:0] [2:a:0] concat=n=3:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" output.mp4

这适用于相同的以及多种输入文件类型。

这里有一个脚本(适用于任意数量的指定文件(不仅仅是工作目录中的所有文件),没有附加文件,也适用于.mov;在macOS上测试):

#!/bin/bash

if [ $# -lt 1 ]; then
    echo "Usage: `basename $0` input_1.mp4 input_2.mp4 ... output.mp4"
    exit 0
fi

ARGS=("$@") # determine all arguments
output=${ARGS[${#ARGS[@]}-1]} # get the last argument (output file)
unset ARGS[${#ARGS[@]}-1] # drop it from the array
(for f in "${ARGS[@]}"; do echo "file '$f'"; done) | ffmpeg -protocol_whitelist file,pipe -f concat -safe 0 -i pipe: -vcodec copy -acodec copy $output
ffmpeg \
  -i input_1.mp4 \
  -i input_2.mp4 \
  -filter_complex '[0:v]pad=iw*2:ih[int];[int][1:v]overlay=W/2:0[vid]' \
  -map [vid] \
  -c:v libx264 \
  -crf 23 \
  -preset veryfast \
  output.mp4