我正在尝试使用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文件合并在一起并使其正常工作。


当前回答

关于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

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

其他回答

这对我(在窗户上)有用

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()

经过多次尝试后,以下脚本在windows 10 powershell上对我有效。

    $files=Get-ChildItem -path e:\ -Filter *.mp4


    $files| ForEach-Object  {"file '$($_.FullName)'"}| Out-File -FilePath e:\temp.txt -Encoding ASCII


    if (-not (test-path "e:\ffmpeg\bin\ffmpeg.exe")) {throw "e:\ffmpeg\bin\ffmpeg.exe needed"}

    E:\ffmpeg\bin\ffmpeg.exe -safe 0 -f concat -i "e:\temp.txt" -c copy -bsf:v hevc_mp4toannexb -an e:\joined.mp4

    # Conversion Cleanup
    Remove-Item e:\temp.txt

这里,前两行创建一个文本文件temp.txt,其中包含以下内容

file 'e:\first.mp4'
file 'e:\second.mp4'

第三、第四行检查ffmpeg在路径上是否可用,并创建“joind.mp4”

与其他答案的主要区别如下

usage  of -bsf:v hevc_mp4toannexb -an

对于我上面的mp4文件,您可能需要根据您的视频编码使用以下其他选项。

h264_mp4toannexb

所有这些可能的比特流过滤器都可以在https://ffmpeg.org/ffmpeg-bitstream-filters.html

这里有两个纯bash解决方案,只使用ffmpeg,不使用

中间文件perl、python或javascript

使用ls的单线解决方案

ls video1.mp4 video2.mp4 | while read line; do echo file \'$line\'; done | ffmpeg -protocol_whitelist file,pipe -f concat -i - -c copy output.mp4

采用0或2+参数的函数

#######################################
# Merge mp4 files into one output mp4 file
# usage:
#   mergemp4 #merges all mp4 in current directory
#   mergemp4 video1.mp4 video2.mp4
#   mergemp4 video1.mp4 video2.mp4 [ video3.mp4 ...] output.mp4 
#######################################
function mergemp4() {
  if [ $# = 1 ]; then return; fi

  outputfile="output.mp4"

  #if no arguments we take all mp4 in current directory as array
  if [ $# = 0 ]; then inputfiles=($(ls -1v *.mp4)); fi
  if [ $# = 2 ]; then inputfiles=($1 $2); fi  
  if [ $# -ge 3 ]; then
    outputfile=${@: -1} # Get the last argument
    inputfiles=(${@:1:$# - 1}) # Get all arguments besides last one as array
  fi
  
  # -y: automatically overwrite output file if exists
  # -loglevel quiet: disable ffmpeg logs
  ffmpeg -y \
  -loglevel quiet \
  -f concat \
  -safe 0 \
  -i <(for f in $inputfiles; do echo "file '$PWD/$f'"; done) \
  -c copy $outputfile

  if test -f "$outputfile"; then echo "$outputfile created"; fi
}

注意:在这个线程中尝试了一些解决方案,但没有一个让我满意

这里是我制作的一个脚本,将几个GoPro mp4连接成720p mp4。希望能有所帮助。

#!/bin/sh
cmd="( "
for i; do
    cmd="${cmd}ffmpeg -i $i -ab 256000 -vb 10000000 -mbd rd -trellis 2 -cmp 2 -subcmp 2 -g 100 -f mpeg -; "
done
cmd="${cmd} ) | ffmpeg -i - -vb 10000000 -ab 256000 -s 1280x720 -y out-`date +%F-%H%M.%S`.mp4"
echo "${cmd}"
eval ${cmd}

这里有一种快速(不到1分钟)且无损的方法,无需中间文件即可完成此操作:

ls Movie_Part_1.mp4 Movie_Part_2.mp4 | \
perl -ne 'print "file $_"' | \
ffmpeg -f concat -i - -c copy Movie_Joined.mp4

“ls”包含要加入的文件“perl”在管道中动态创建连接文件“-i-”部分告诉ffmpeg从管道读取

(注意——我的文件中没有空格或奇怪的东西——如果你想用“硬”文件实现这个想法,你需要适当的shell转义)。