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


当前回答

如果您想连接文件,而不必创建一个烦人的单独文本文件,只需包含文件名,您可以这样做:

echo -e "file 'in1.mp4'\nfile 'in2.mp4'" | ffmpeg -f concat -safe 0 -i /dev/stdin -c copy out.mp4

事实上,每次命令需要传入文件时,您都可以回显一个字符串并将其通过管道发送到命令,只需使用/dev/stdin作为文件名。

其他回答

对于那些需要连接许多用H.264编码的MP4视频的人,我提出了一个Python脚本mp4concat.py,它可以自动执行Concat协议/使用ffmpeg文档中的中间文件段落。

从上面的链接或使用

wget https://gist.githubusercontent.com/mwouts/115956d3fc7ece55cbce483a7a5148bd/raw/4bc5e03952f1b981dac3f97f4daf82c907774b17/mp4concat.py

然后将其用作

python3 mp4concat.py input1.mp4 ... inputN.mp4 --output output.mp4

这里有两个纯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
}

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

如果您想连接文件,而不必创建一个烦人的单独文本文件,只需包含文件名,您可以这样做:

echo -e "file 'in1.mp4'\nfile 'in2.mp4'" | ffmpeg -f concat -safe 0 -i /dev/stdin -c copy out.mp4

事实上,每次命令需要传入文件时,您都可以回显一个字符串并将其通过管道发送到命令,只需使用/dev/stdin作为文件名。

对于.mp4文件,我发现使用开源命令行工具mp4box工作得更好更快。然后你可以这样使用它:

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

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

根据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个文件的总和。应该不会有任何损失。正是我需要的