web-dev-qa-db-ja.com

LinuxでMP4ファイルを結合する

2つのmp4ファイルを結合して1つのファイルを作成したいと思います。ビデオストリームはh264でエンコードされ、オーディオはaacでエンコードされます。計算上の理由により、ビデオを別の形式に再エンコードできません。また、GUIプログラムは使用できません。すべての処理はLinuxコマンドラインユーティリティで実行する必要があります。 FFmpegはmpeg4ファイルに対してこれを行うことができないため、代わりにMP4Boxを使用しました。

MP4Box -add video1.mp4 -cat video2.mp4 newvideo.mp4

残念ながら、音声はすべて混同されます。問題はオーディオがaacにあることだと思ったので、mp3でトランスコードし、再びMP4Boxを使用しました。この場合、前半はnewvideo.mp4 (対応する video1.mp4)ですが、それらはオーディオではなく、ビデオでもナビゲートできません。

次に考えたのは、オーディオストリームとビデオストリームには、長さに若干の不一致があり、修正する必要があるということでした。そのため、入力ビデオごとにビデオストリームとオーディオストリームを分割し、FFmpegの-shortestオプションを使用してそれらを結合しました。

したがって、私が実行した最初のビデオについて:

 avconv -y -i video1.mp4 -c copy -map 0:0 videostream1.mp4
 avconv -y -i video1.mp4 -c copy -map 0:1 audiostream1.m4a
 avconv -y -i videostream1.mp4 -i audiostream1.m4a  -c copy -shortest  video1_aligned.mp4

同様に2番目のビデオについても、以前と同様にMP4Boxを使用しました。残念ながらこれもうまくいきませんでした。私が得た唯一の成功は、ビデオストリーム(つまり、videostream1.mp4とvideostream2.mp4)とオーディオストリーム(すなわち、audiostream1.m4aとaudiostream2.m4a)を別々に結合して、ビデオとオーディオを最終的なファイルに結合したときでした。ただし、ビデオの後半では同期が失われます。具体的には、オーディオとビデオの1秒の遅延があります。どんな提案でも大歓迎です。

34
Jose Armando

これを行う最善の方法は、現在 concat demuxer を使用することです。まず、次のようなフォーマットのinputs.txtというファイルを作成します。

file '/path/to/input1.mp4'
file '/path/to/input2.mp4'
file '/path/to/input3.mp4'

次に、このffmpegコマンドを実行します。

ffmpeg -f concat -i inputs.txt -c copy output.mp4

ffmpeg FAQの連結 も参照してください。


古いバージョンのffmpegを使用している人のために、以下を維持します。

最新バージョンのffmpegでこれを行うことができます。まずファイルをmpegトランスポートストリームに再多重化する必要があります(コンテナーの形式を変更するだけなので、かなりプロセッサーライトが必要です)。

ffmpeg -i input.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb middle.ts

それがh264に関するエラー[ 1 ]をスローする場合、次を使用する必要があるかもしれません:

ffmpeg -i input.mp4 -map 0 -c copy -f mpegts -bsf:v h264_mp4toannexb middle.ts

これは、各入力ファイルで個別に行う必要があります。ファイルを連結するには、以下を使用します。

ffmpeg -i "concat:middle1.ts|middle2.ts|middle3.ts" -c copy output.mp4

それがaacに関するエラーをスローする場合、使用する必要があるかもしれません

ffmpeg -i "concat:middle1.ts|middle2.ts|middle3.ts" -c copy -absf aac_adtstoasc output.mp4

システムが名前付きパイプをサポートしている場合は、中間ファイルを作成せずにこれを行うことができます。

mkfifo temp0 temp1

3つの別々の仮想端末で以下を実行する必要があります。

ffmpeg -i input0.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb -y temp0
ffmpeg -i input1.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb -y temp1
ffmpeg -f mpegts -i "concat:temp0|temp1" -c copy -absf aac_adtstoasc output.mp4

Output.mp4がすでに存在する場合、3番目のffmpegはそれを上書きするかどうかを尋ねますが、FIFOにアクセスした後にこれを実行します、これにより、最初にffmpegsが終了します。したがって、出力ファイルには未使用の名前を選択してください。

入力ファイルが異なる場合、これは機能しない可能性があります。ビットレートの違いは問題ないと思いますが、フレームサイズ、フレームレートなどは一致する必要があります。

34
evilsoup

Mp4の場合、私が見つけた唯一の実用的なソリューションはgpacパッケージのMP4Boxでした

#!/bin/bash
filesList=""
for file in $(ls *.mp4|sort -n);do
    filesList="$filesList -cat $file"
done
MP4Box $filesList -new merged_files_$(date +%Y%m%d_%H%M%S).mp4

またはコマンドは

MP4Box -cat file1.mp4 -cat file2.mp4 -new mergedFile.mp4

mencoderとavconvでは、それを機能させることができませんでした:-(

7

ここに、それをはるかに簡単にするワンライナーがあります。最終的に1つのビデオファイルを提供するために必要なすべてのことを実行します。乾杯!

find *.mp4 | sed 's:\ :\\\ :g'| sed 's/^/file /' > list.txt; ffmpeg -f concat -i list.txt -c copy output.mp4; rm list.txts
4
Anonymous

Mac(macOS 10.13 High Sierra)では、以下を使用して成功しました:

for f in *.m4a; do echo "file '$f'" >> mylist.txt; done

必要に応じて上記と同様に並べ替えます(./が削除されていることに注意してください-パスが原因で、ffmpegが安全でないファイル名エラーをスローしていました)。

次に、デフォルトのMacPorts ffmpegに指定されたコーデックとビットレートを削除して、それを気に入った:

ffmpeg -f concat -i mylist.txt  output.m4a
2
Robert Cole

スクリプトdvoをありがとう。それは私にとって素晴らしい出発点でした。名前付きパイプの使用には問題がなかったので、代わりにスクリプトを使用して名前付きパイプを使用しました。さらに、スペースを含むファイル名で機能するように修正しました。名前付きパイプへの出力は、それらからの読み取りを開始するまで「実行」されないため、avconvへの最後の呼び出しの前に、&を使用して初期remuxタスクのバックグラウンドを設定する必要があります。さらに、-c copyを使用することを忘れないでください。そうしないと、全体を再エンコードすることになります。

#!/bin/bash
for FILE in "$@"; do
   TAG=$(echo "$FILE" | sed -e s/[^A-Za-z0-9.]/_/g)
   mkfifo $TAG.mp4concatpipe
   avconv -loglevel "quiet" -i "$FILE"  -f mpegts -c copy -bsf h264_mp4toannexb -y $TAG.mp4concatpipe &
   IN=$IN\|$TAG.fifo
done

IN=${IN#\|}
avconv -i concat:$IN -c copy out.mp4
rm *.mp4concatpipe
2
Mark

Evilsoupに感謝します。これは、最新のavconvを使用してプロセスを自動化する小さなスクリプトです。
ところで、名前付きパイプが機能しませんでした(それらへの出力が行き詰まりました)。

   #!/bin/bash

    for FILE in $@; do
      avconv -i $FILE -map 0 -f mpegts -bsf h264_mp4toannexb -c:v copy -y $FILE.tmp
      IN=$IN\|$FILE.tmp
    done

    IN=${IN#\|}
    avconv -i concat:"$IN" out.mp4

    for FILE in $@; do
      rm $FILE.tmp
    done
1
dvo

で大成功を収めてきました

for f in ./*.m4a; do echo "file '$f'" >> mylist.txt; done

mylist.txtを少し並べ替える必要があるかもしれません

ffmpeg -f concat -i mylist.txt -c:a libfdk_aac -b:a 128k output.m4a

ffmpegバージョン2.2.3

0
Benjamin