如何使用FFmpeg连接两个MP4文件?

时间:2011-09-07 11:40:01

标签: ffmpeg h.264 mp4

我正在尝试使用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文件连在一起并让它工作。

23 个答案:

答案 0 :(得分:538)

FFmpeg有三种连接方法。

1。 concat video filter

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。 concat demuxer

$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'

$ ffmpeg -f concat -i mylist.txt -c copy output

for 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 protocol

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

由于这些格式的性质以及此方法执行的简单连接,此方法不适用于包括MP4在内的多种格式。

使用哪一个

  • concat过滤器:如果您的输入没有相同的参数(宽度,高度等),或者格式/编解码器不相同,或者您想要执行任何操作,请使用过滤。 (您可以仅重新编码不匹配的输入,以便它们共享相同的编解码器和其他参数,然后使用concat解复用器来避免重新编码其他输入。)

  • concat demuxer :当您想要避免重新编码和格式时使用 不支持文件级别连接(一般用户使用的大多数文件不支持文件级别连接)。

  • concat协议:与支持文件级别连接的格式一起使用 (MPEG-1,MPEG-2 PS,DV)。不要与MP4一起使用。

如果有疑问,请尝试使用concat demuxer。

另见

答案 1 :(得分:49)

FOR MP4 FILES

对于 .mp4 文件(我从DailyMotion.com获取:一个50分钟的电视剧集,只能分三部分下载,作为三个.mp4视频文件),以下是< strong> Windows 7 ,并且 NOT 涉及重新编码文件。

我重命名了文件(如 file1.mp4 file2.mp4 file3.mp4 ),以便部件正确无误订购查看完整的电视剧。

然后我创建了一个简单的批处理文件(concat.bat),其中包含以下内容:

:: Create File List
echo file file1.mp4 >  mylist.txt 
echo file file2.mp4 >> mylist.txt
echo file file3.mp4 >> mylist.txt

:: Concatenate Files
ffmpeg -f concat -i mylist.txt -c copy output.mp4

批处理文件 ffmpeg.exe 必须与要加入的 .mp4文件放在同一个文件夹中。然后运行批处理文件。运行通常不到十秒钟 。

附录(2018/10/21) -

如果您要查找的是一种在当前文件夹中指定所有mp4文件而无需进行大量重新输入的方法,请在Windows 批处理文件中尝试此操作(必须包含选项 -safe 0 ):

:: Create File List
for %%i in (*.mp4) do echo file '%%i'>> mylist.txt

:: Concatenate Files
ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4

这适用于Windows 7中的批处理文件。 不要尝试在命令行中使用它,因为它只能在批处理文件中使用!

答案 2 :(得分:48)

这里有一个快速(不到1分钟)和无损的方式来做到这一点,而不需要中间文件:

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

&#34; ls&#34;包含要加入的文件 &#34; perl&#34;在运行中将连接文件创建到管道中 &#34; -i - &#34; part告诉ffmpeg从管道读取

(注意 - 我的文件中没有空格或奇怪的内容 - 如果你想用&#34; hard&#34;文件来实现这个想法,你需要适当的shell转义。

答案 3 :(得分:33)

表示MP4文件:

如果它们不完全相同(100%相同的编解码器,相同的分辨率,相同的类型)MP4文件,那么您必须首先将它们转码为中间流:

ffmpeg -i myfile1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp1.ts
ffmpeg -i myfile2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp2.ts
// now join
ffmpeg -i "concat:temp1.ts|temp2.ts" -c copy -bsf:a aac_adtstoasc output.mp4

注意: 输出将像第一个文件(而不是第二个文件)

答案 4 :(得分:30)

我最终使用mpg作为中间格式并且它有效(注意这是一个危险的例子,-qscale 0将重新编码视频...)

ffmpeg -i 1.mp4 -qscale 0 1.mpg
ffmpeg -i 2.mp4 -qscale 0 2.mpg
cat 1.mpg 2.mpg | ffmpeg -f mpeg -i - -qscale 0 -vcodec mpeg4 output.mp4

答案 5 :(得分:30)

我试图将三个.mp3音频文件连接到一个.m4a文件中,这个ffmpeg命令可以正常工作。

输入命令:

ffmpeg -i input1.mp3 -i input2.mp3 -i input3.mp3 -filter_complex concat=n=3:v=0:a=1 -f MOV -vn -y input.m4a

意义: 的&#34; -filter_complex concat = n = 3:v = 0:a = 1&#34;

  

concat 表示使用媒体连接(加入)功能    n 表示确认输入文件的总数    v 表示有视频?使用0 =没有视频,1 =包含视频    a 表示有音频?使用0 =无音频,1 =包含音频    -f 表示强制设置文件格式(要查看所有支持的格式,请使用ffmpeg -formats
   -vn 表示禁用视频(如果不需要,-an也会禁用音频)
   -y 表示覆盖输出文件(如果输出文件已存在)。

了解更多信息:使用ffmpeg -h full 打印所有选项(包括所有格式和编解码器特定选项,非常长)

答案 6 :(得分:13)

此处仅使用ffmpeg而不使用

的2种纯bash解决方案
  • 中间文件
  • 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
}

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

答案 7 :(得分:11)

有关ffmpeg中各种连接方式的详细文档可以是found here

您可以使用'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

这适用于同一个以及多个输入文件类型。

答案 8 :(得分:6)

根据rogerdpack和Ed999的回复,我创建了我的.sh版本

#!/bin/bash

[ -e list.txt ] && rm list.txt
for f in *.mp4
do
   echo "file $f" >> list.txt
done

ffmpeg -f concat -i list.txt -c copy joined-out.mp4 && rm list.txt

它将当前文件夹中的所有*.mp4个文件加入joined-out.mp4

在mac上测试。

结果文件大小是我测试的60个文件的精确总和。不应该有任何损失。正是我需要的东西

答案 9 :(得分:4)

从此处的文档中:https://trac.ffmpeg.org/wiki/Concatenate

  

如果您有MP4文件,可以先将它们转码为MPEG-2传输流,然后无损将它们合并在一起。对于H.264视频和AAC音频,可以使用以下内容:

ffmpeg -i input1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
ffmpeg -i input2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc output.mp4

此方法适用于所有平台。

我需要将其封装在跨平台脚本中的能力,因此我使用了fluent-ffmpeg并提出了以下解决方案:

const unlink = path =>
  new Promise((resolve, reject) =>
    fs.unlink(path, err => (err ? reject(err) : resolve()))
  )

const createIntermediate = file =>
  new Promise((resolve, reject) => {
    const out = `${Math.random()
      .toString(13)
      .slice(2)}.ts`

    ffmpeg(file)
      .outputOptions('-c', 'copy', '-bsf:v', 'h264_mp4toannexb', '-f', 'mpegts')
      .output(out)
      .on('end', () => resolve(out))
      .on('error', reject)
      .run()
  })

const concat = async (files, output) => {
  const names = await Promise.all(files.map(createIntermediate))
  const namesString = names.join('|')

  await new Promise((resolve, reject) =>
    ffmpeg(`concat:${namesString}`)
      .outputOptions('-c', 'copy', '-bsf:a', 'aac_adtstoasc')
      .output(output)
      .on('end', resolve)
      .on('error', reject)
      .run()
  )

  names.map(unlink)
}

concat(['file1.mp4', 'file2.mp4', 'file3.mp4'], 'output.mp4').then(() =>
  console.log('done!')
)

答案 10 :(得分:3)

这是我用几个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}

答案 11 :(得分:3)

迟到的答案,但这是唯一对我有用的选项:

(echo file '1.mp4' & echo file '2.mp4' & echo file '3.mp4' & echo file '4.mp4') | ffmpeg -protocol_whitelist file,pipe -f concat -safe 0 -i pipe: -vcodec copy -acodec copy "1234.mp4"

答案 12 :(得分:3)

在接受的答案中,使用选项3在Mac上连接多个MP4时,我发现管道操作员对我不起作用。

以下单线可在Mac(High Sierra)上连接mp4,而无需创建中间文件。

ffmpeg -f concat -safe 0 -i <(for f in ./*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4

答案 13 :(得分:2)

这对我有用(在Windows上)

1366x768

特别是

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

的Python

使用一些python代码与文件夹中的mp4一样多 导入glob import os

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

答案 14 :(得分:1)

对于.mp4文件,我发现使用开源命令行工具mp4box的效果更好,更快。然后您可以通过以下方式使用它:

mp4box.exe -add video1.mp4 -cat video2.mp4 destvideo.mp4

在此处下载适用于大多数平台:https://gpac.wp.imt.fr/mp4box/

答案 15 :(得分:1)

合并当前目录中的所有mp4文件

我个人不喜欢不创建我之后必须删除的外部文件,所以我的解决方案是包含编号列表的文件(如file_1_name, file_2_name, file_10_name, file_20_name, file_100_name,...)

#!/bin/bash
filesList=""
for file in $(ls -1v *.mp4);do #lists even numbered file
    filesList="${filesList}${file}|"
done
filesList=${filesList%?} # removes trailing pipe
ffmpeg -i "concat:$filesList" -c copy $(date +%Y%m%d_%H%M%S)_merged.mp4

答案 16 :(得分:1)

这是一个脚本(适用于任意数量的指定的文件(不仅适用于工作目录中的所有文件),没有其他文件,也适用于.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

答案 17 :(得分:1)

不需要中间文件的单行代码片段:

ffmpeg -safe 0 -f concat -i <(查找“ $ PWD” -name'* .mp4'-printf“ file'%p'\ n” |排序)-c复制output.mp4

在这里,可以说<()语法实际上是在“后台”创建一个临时文件

答案 18 :(得分:1)

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

答案 19 :(得分:0)

在Windows 10 Powershell上,以下脚本进行了各种尝试后为我工作。

select
    .... ,
    substr(time, 1, 2) as HOUR, 
    substr(time, 4, 2) as MINUTE
from 
    table1
where 
    date = to_char(sysdate - interval '1' day,'yyyymmdd')

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

    $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

第3、4行检查ffmpeg在路径中是否可用,并创建“ joined.mp4”

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

file 'e:\first.mp4'
file 'e:\second.mp4'
对于上面的mp4文件,

可以工作,根据您的视频编码,您可能需要使用以下其他替代方法。

usage  of -bsf:v hevc_mp4toannexb -an

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

中找到

答案 20 :(得分:0)

这是我的一种方法,该方法使用命令替换和concat视频过滤器(包含完整的MP4文件)来连接包含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时显示的顺序)将文件连接在一起-在我的情况下,它们每个都有一个轨道号,因此效果很好。

答案 21 :(得分:0)

此处描述的concat协议; https://trac.ffmpeg.org/wiki/Concatenate#protocol

在使用命名管道实现以避免中间文件时

非常快(读取:即时),没有丢帧,并且运行良好。

请记住删除命名的管道文件,并记住检查视频是否为H264和AAC(仅使用ffmpeg -i filename.mp4就可以进行检查(检查是否有h264和AAC提及)

答案 22 :(得分:0)

以可重复使用的PowerShell脚本的形式接受的答案

- name: "Define new disk structure"
  set_fact:
    vm_disks: >-
      [
      {% for disk in (template_disk.guest_disk_info|dictsort) %}
      {{ "" if loop.first else "," }}
      {
        "size_kb": {{ disk[1].capacity_in_kb }},
        "datastore": "{{ datastore_name }}"
      }
      {% endfor %}
      {% for disk in additional_disks|default([]) %}
      {{ "," if template_disk.guest_disk_info else "" }}
      {
        {% if disk.size_gb is defined %}"size_gb": {{ disk.size_gb }},{% endif %}
        {% if disk.size_mb is defined %}"size_mb": {{ disk.size_mb }},{% endif %}
        {% if disk.size_kb is defined %}"size_kb": {{ disk.size_kb }},{% endif %}
        "datastore": "{{ datastore_name }}"
        }
      {% endfor %}
      ]