【ffmpeg】解像度や形式が違う動画をconcat filterで結合するテクニック

はい、承知いたしました。ffmpegのconcat filterを使用して解像度や形式が異なる動画を結合するテクニックについて、詳細な説明を含む約5000語の記事を作成します。


【ffmpeg完全ガイド】解像度・形式が違う動画をconcat filterで華麗に結合するテクニック

導入

FFmpegは、コマンドラインで動作する非常に強力なマルチメディアフレームワークです。動画や音声の変換、編集、ストリーミングなど、およそ考えられるあらゆる処理を自由自在に行うことができます。その多機能さゆえに初学者には少し敷居が高く感じられるかもしれませんが、一度その使い方をマスターすれば、高価な動画編集ソフトウェアがなくても、複雑なタスクを効率的に自動化できるようになります。

動画編集において最も基本的ながら、しばしば困難を伴う作業の一つが「動画ファイルの結合」です。例えば、スマートフォンで撮影した縦長の動画、デジタルカメラで撮影した横長の4K動画、そしてインターネットからダウンロードしたHD画質の動画。これら仕様がバラバラの素材を一つに繋ぎ合わせたいと思ったことはありませんか?

多くの人が最初に試すであろう単純なファイル結合では、解像度やコーデックの違いが原因でエラーが発生したり、再生がうまくいかなかったりします。ここで登場するのが、FFmpegのconcat機能です。

しかし、FFmpegにはconcatと名のつく機能が2種類存在します。「concat demuxer(またはprotocol)」と「concat filter」です。前者は非常に高速ですが、結合できるのはコーデックや解像度などが完全に一致するファイルに限られます。一方、後者のconcat filterは、デコードと再エンコードを伴うため処理に時間はかかりますが、その代償として絶大な柔軟性を誇ります。解像度、フレームレート、アスペクト比、さらにはコーデックが異なる動画ファイルでさえも、滑らかに一つの動画として結合することが可能です。

この記事では、その高機能なconcat filterに徹底的に焦点を当てます。基本的な使い方から、解像度やアスペクト比の違いを吸収するためのテクニック、フレームレートやオーディオ仕様の不一致を解決する方法、さらには実践的なトラブルシューティングまで、網羅的に解説していきます。この記事を読み終える頃には、あなたはFFmpegを駆使して、どんなに厄介な動画素材のコレクションでも、思い通りの一本の映像作品にまとめ上げるスキルを身につけていることでしょう。

第1章: なぜ concat filter なのか? demuxer との比較

FFmpegで動画を結合する方法を調べると、必ずconcat demuxerとconcat filterという2つの選択肢に行き着きます。これらは名前が似ていますが、動作原理も得意なことも全く異なります。なぜ私たちがconcat filterを選ぶべきなのかを理解するために、まずは両者の違いを明確にしておきましょう。

concat demuxer (protocol) の仕組みと限界

concat demuxerは、ファイルレベルでの非常に単純な連結を行います。イメージとしては、複数の動画ファイルのバイナリデータをそのまま順番に繋ぎ合わせるようなものです。この処理は、動画の内容を解釈(デコード)したり、再圧縮(エンコード)したりすることなく行われます。

メリット:
* 高速: 再エンコードが不要なため、処理は一瞬で終わります。
* 無劣化: copyモード(-c copy)で動作するため、元の動画の画質や音質が一切劣化しません。

デメリット:
* 極めて厳しい制約: 結合するすべての動画ファイルは、以下の項目が完全に一致している必要があります。
* ビデオコーデック (例: H.264)
* オーディオコーデック (例: AAC)
* コンテナフォーマット (例: MP4) ※厳密には一致していなくても動く場合があるが、推奨される
* 解像度 (例: 1920×1080)
* フレームレート (例: 29.97fps)
* ピクセルフォーマット (例: yuv420p)
* タイムベース (内部的な時間の単位)
* その他、多くのメディアパラメータ

一つでもこれらのパラメータが異なると、コマンドがエラーで失敗するか、運良くファイルが生成されても再生時に映像が乱れたり、音が途切れたり、途中で再生が止まったりといった問題が発生します。

使用例:
concat demuxerを使用するには、まず結合したいファイルの一覧を記述したテキストファイル(例: mylist.txt)を作成します。

mylist.txt:
file 'part1.mp4'
file 'part2.mp4'
file 'part3.mp4'

そして、以下のコマンドを実行します。

bash
ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4

  • -f concat: concat demuxerを使用することを明示します。
  • -safe 0: ファイルパスに特殊文字が含まれていても安全に処理するためのオプションです(必須ではありませんが、付けておくと無難です)。
  • -i mylist.txt: 入力としてテキストファイルを指定します。
  • -c copy: ビデオとオーディオのストリームを再エンコードせず、そのままコピーします。

どんな時に使うか?
この方法は、デジタルカメラやビデオカメラが長時間撮影時に自動的にファイルを分割した場合など、完全に同一の機材・設定で撮影されたファイルを結合する場合に最適です。

concat filter の優位性

一方、concat filterは、FFmpegの強力なフィルタリングシステムの一部として機能します。その動作原理はdemuxerとは全く異なります。

動作原理:
1. デコード: 入力されたすべての動画・音声ストリームを、一旦非圧縮の生データ(ビデオなら一連の画像フレーム、オーディオならPCMデータ)にデコードします。
2. フィルタリング: デコードされた生データをフィルタグラフに渡し、concat filterがそれらを時系列に連結します。この段階で、解像度やフレームレートなどの不一致が(最初の入力ファイルを基準に)自動的に調整されます。
3. エンコード: 結合された生データを、指定されたコーデックで再度エンコードし、最終的な一つのファイルとして出力します。

メリット:
* 圧倒的な柔軟性: 解像度、アスペクト比、フレームレート、コーデック、ピクセルフォーマット、サンプリングレートなどが異なる動画・音声ファイルを問題なく結合できます。
* 高度な制御: 他のフィルター(scale, pad, fpsなど)と組み合わせることで、結合前の各素材に対して個別の調整を加えられます。

デメリット:
* 処理時間: 全てのフレームをデコードし、再エンコードするため、demuxer方式に比べて処理に時間がかかります。
* 画質・音質の劣化: 再エンコードは非可逆圧縮であるため、原理的に若干の品質劣化が発生します。ただし、エンコード設定(-crf値など)を適切に行うことで、劣化を人間が知覚できないレベルに抑えることが可能です。

どんな時に使うか?
この記事のテーマである、出自の異なる様々な仕様の動画を一つにまとめたい場合は、concat filterが唯一かつ最良の選択肢となります。

まとめ:demuxer vs. filter

項目 concat demuxer concat filter
動作原理 ファイルレベルのバイナリ連結 デコード → フィルタリング → 再エンコード
処理速度 非常に高速 時間がかかる
画質/音質 無劣化(-c copy時) 再エンコードによる劣化の可能性あり
柔軟性 非常に低い(全パラメータの一致が必要) 非常に高い(異なる仕様のファイルを結合可能)
主な用途 同一機材で分割されたファイルの結合 異なるソースからの動画ファイルの結合

このように、目的が「異なる形式の動画を結合する」である以上、私たちが学ぶべきはconcat filterのテクニックです。次章から、その具体的な使い方を掘り下げていきましょう。

第2章: concat filter の基本構文と動作原理

concat filterを使いこなす鍵は、FFmpegの-filter_complexオプションと、ストリーム指定子の概念を理解することにあります。少し複雑に見えるかもしれませんが、一度仕組みを理解すれば、驚くほど直感的に扱えるようになります。

基本的なコマンドの構造

まずは、3つの動画ファイル(input1.mp4, input2.mp4, input3.mp4)を結合する基本的なコマンドを見てみましょう。

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

このコマンドを分解して、各要素が何をしているのかを詳しく見ていきましょう。

  • -i inputX.mp4: 結合したい入力ファイルを、結合したい順番に指定します。FFmpegは内部的に、最初の-i0番、次を1番、その次を2番…とインデックスを割り振ります。

  • -filter_complex "...": 複数の入力ストリームや出力ストリームを扱う複雑なフィルタグラフを定義するためのオプションです。""の中にフィルタ処理の内容を記述します。

  • [0:v]: これは「ストリーム指定子」です。

    • 0: 0番目の入力ファイル(input1.mp4)を指します。
    • :v: そのファイル内のビデオストリームを指します。
    • つまり、[0:v]input1.mp4のビデオストリームを意味します。同様に[0:a]input1.mp4のオーディオストリームを指します。
  • [0:v][1:v][2:v]: 3つの入力ファイルのビデオストリームを、concat filterへの入力として指定しています。[0:v]input1.mp4のビデオ、[1:v]input2.mp4のビデオ、[2:v]input3.mp4のビデオです。

  • concat=n=3:v=1:a=1: これがconcat filter本体です。

    • concat: 使用するフィルターの名前。
    • n=3: 結合するセグメント(動画)の数を指定します。この数は、入力として指定したストリームの数(この場合は[0:v], [1:v], [2:v]の3つ)と一致させる必要があります。
    • v=1: 出力するビデオストリームの数を指定します。通常は1つのビデオストリームにまとめるので1です。
    • a=1: 出力するオーディオストリームの数を指定します。同様に、通常は1です。このコマンド例では、ビデオとオーディオを同時に結合しています。(オーディオの結合については後述します)
  • [vout][aout]: concat filterからの出力に名前を付けています。

    • [vout]: v=1で指定したビデオ出力にvoutという名前を付けます。
    • [aout]: a=1で指定したオーディオ出力にaoutという名前を付けます。
    • これらの名前は任意で、[v_final], [a_final]など、自分でわかりやすい名前を付けることができます。
  • -map "[vout]": フィルタグラフ内で[vout]と名付けたストリームを、最終的な出力ファイル(output.mp4)にマッピング(割り当て)します。

  • -map "[aout]": 同様に、[aout]と名付けたオーディオストリームを出力ファイルにマッピングします。

  • output.mp4: 出力ファイル名です。

動作原理の深掘り

このコマンドが実行されると、FFmpegの内部では以下のような処理フローが構築されます。

  1. 入力とデコード:

    • input1.mp4, input2.mp4, input3.mp4が読み込まれます。
    • 各ファイルのビデオストリームとオーディオストリームがデコードされ、生のフレームデータ(ピクセルの集合)とPCMオーディオデータに変換されます。
  2. フィルタグラフの処理:

    • -filter_complexで定義されたグラフにデータが送られます。
    • ビデオストリームは[0:v], [1:v], [2:v]として識別され、concat filterに投入されます。
    • オーディオストリームも同様に[0:a], [1:a], [2:a]として識別され、concat filterに投入されます。(※この基本形ではオーディオストリームも暗黙的に同様に処理されます)
    • concat filterは、まず最初の入力ストリーム([0:v][0:a])のプロパティを基準として採用します。例えば、解像度、アスペクト比、フレームレート、ピクセルフォーマット、サンプリングレートなどです。
    • 次に、2番目以降のストリーム([1:v], [2:v]…)を、その基準に合わせて自動的に変換しようと試みます。例えば、input2.mp4の解像度がinput1.mp4と異なれば、input1.mp4の解像度に合うように拡大または縮小されます。
    • すべてのストリームが共通のフォーマットに揃えられた後、時系列に連結されます。
    • 連結された結果は、ビデオストリームが[vout]、オーディオストリームが[aout]として出力されます。
  3. マッピングとエンコード:

    • -mapオプションの指示に従い、[vout][aout]が出力ファイルへと送られます。
    • 送られてきた生のビデオフレームとオーディオデータは、output.mp4というMP4コンテナに適したコーデック(デフォルトではビデオはlibx264、オーディオはAAC)でエンコードされます。
    • エンコードされたデータがコンテナに格納され、最終的なoutput.mp4ファイルが完成します。

この「最初の入力が基準になる」というルールが、concat filterを使いこなす上で最も重要なポイントです。このデフォルトの挙動を理解し、意図通りに動作しない場合に他のフィルタを組み合わせて制御することが、次の章以降のテーマとなります。

第3章: 実践!解像度やアスペクト比が違う動画の結合

ここからが本番です。仕様の異なる動画を結合する際に直面する最も一般的な問題、すなわち解像度とアスペクト比の違いを解決するテクニックを見ていきましょう。

問題シナリオ:解像度が違う動画の結合 (アスペクト比は同じ)

手元に以下の2つの動画があるとします。
* 1080p.mp4: 1920×1080 (16:9)
* 720p.mp4: 1280×720 (16:9)

これを、前章で学んだ基本コマンドで結合してみましょう。1080p.mp4を最初に指定します。

bash
ffmpeg -i 1080p.mp4 -i 720p.mp4 \
-filter_complex "[0:v][1:v]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
output_default.mp4

結果はどうなるか?
concat filterの「最初の入力が基準になる」というルールに従い、出力されるoutput_default.mp4の解像度は1920×1080になります。そして、2番目の動画720p.mp4は、再生される際に自動的に1920×1080にアップスケール(拡大)されます。

多くの場合、この自動スケーリングは画質の劣化を招き、意図した結果とは異なるかもしれません。特に、高解像度の動画を基準に低解像度の動画をアップスケールすると、映像がぼやけて見えがちです。

解決策1: すべての動画を特定の解像度に統一してから結合

より高品質な結果を得るためには、concat filterに渡す前に、すべての動画の解像度を自分でコントロールするのが賢明です。ここでは、FFmpegのscale filterを組み合わせます。

目標: すべての動画をHD解像度(1280×720)に統一してから結合する。

bash
ffmpeg -i 1080p.mp4 -i 720p.mp4 \
-filter_complex \
"[0:v]scale=1280:720[v0]; \
[1:v][v0]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
-c:v libx264 -crf 22 -preset medium \
output_scaled_to_720p.mp4

※注意: [1:v][v0]の順番が逆ですね。正しくは [v0][1:v] です。
bash
ffmpeg -i 1080p.mp4 -i 720p.mp4 \
-filter_complex \
"[0:v]scale=1280:720[v0]; \
[v0][1:v]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
-c:v libx264 -crf 22 -preset medium \
output_scaled_to_720p.mp4

(思考:おっと、コード例が間違っている。1080p.mp4[0:v]720p.mp4[1:v]。結合順は 1080p -> 720p1080pをスケールダウンして[v0]とし、720pはそのまま。結合するストリームは[v0][1:v]になる。このままだとconcatの基準が[v0]のスケール後1280x720になり、[1:v]は元々1280x720なので問題ない。これでOK。いや、もっと良い書き方がある。両方を明示的にスケールする方が可読性が高いかも。でも今回は片方だけで十分なので、これで説明しよう)
※記事本文では正しい順序で書きます。

コマンド解説:
* [0:v]scale=1280:720[v0]:
* [0:v]1080p.mp4のビデオ)を入力とします。
* scale=1280:720フィルターを適用し、解像度を1280×720にダウンスケール(縮小)します。
* その結果に[v0]という新しい名前を付けます。
* ;: 複数のフィルターチェーンを区切る記号です。
* [v0][1:v]concat=n=2...:
* concat filterの入力として、先ほどスケールした[v0]と、元の[1:v]720p.mp4のビデオ、元々1280×720なので処理不要)を使用します。
* 結合順は [v0] が先、[1:v] が後なので、1080p.mp4 (縮小済) → 720p.mp4 の順で結合されます。
* -c:v libx264 -crf 22 -preset medium: 再エンコード時の品質を指定するオプションです。
* -c:v libx264: ビデオコーデックにH.264を使用。
* -crf 22: 品質基準のレート制御。値が低いほど高品質(18がほぼ無劣化、23がデフォルト、28あたりから劣化が目立つ)。
* -preset medium: エンコード速度と圧縮率のバランス。veryslowが最高品質、ultrafastが最速。

このように、concatの前に他のフィルタを挟むことで、各素材を望む形に整えてから結合するという、より高度な制御が可能になります。

解決策2: アスペクト比を維持してレターボックス/ピラーボックスを追加

次に、さらに複雑なケースを考えます。アスペクト比が異なる動画の結合です。

  • movie_16x9.mp4: 1920×1080 (16:9)
  • photo_slideshow_4x3.mp4: 1280×960 (4:3)

これらを単純に結合すると、concatは最初の動画(16:9)のアスペクト比を基準にするため、4:3の動画は16:9に引き伸ばされてしまい、被写体が横に太った不自然な映像になります。

目標: 出力は1920×1080(16:9)とし、4:3の動画はアスペクト比を維持したまま、左右に黒帯(ピラーボックス)を追加して中央に配置する。

この処理には、scaleフィルターとpadフィルターを組み合わせるという、FFmpegの定石テクニックを使います。

bash
ffmpeg -i movie_16x9.mp4 -i photo_slideshow_4x3.mp4 \
-filter_complex \
"[1:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1[v1]; \
[0:v][v1]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
output_padded.mp4

この複雑なフィルターチェーンを一つずつ解き明かしましょう。処理対象は2番目の動画[1:v]です。

  1. scale=1920:1080:force_original_aspect_ratio=decrease:

    • scale=1920:1080: 最終的なキャンバスサイズを幅1920, 高さ1080に指定します。
    • force_original_aspect_ratio=decrease: 元のアスペクト比(4:3)を維持したまま、指定した1920×1080のボックスに収まるようにリサイズします。decreaseは「小さくなる方に合わせる」という意味です。
    • この場合、1280x960の動画は、高さが1080を超えないようにリサイズされます。計算すると、幅は 1080 * (4/3) = 1440 となり、結果として1440x1080の解像度になります。
  2. pad=1920:1080:(ow-iw)/2:(oh-ih)/2:

    • pad=1920:1080: 幅1920, 高さ1080の背景(デフォルトは黒)を作成します。
    • (ow-iw)/2:(oh-ih)/2: パディングする動画(前のscaleフィルターから渡された1440x1080の映像)を、この背景の中央に配置するための数式です。
      • ow: 出力幅 (output width) = 1920
      • iw: 入力幅 (input width) = 1440
      • oh: 出力高さ (output height) = 1080
      • ih: 入力高さ (input height) = 1080
      • X座標: (1920-1440)/2 = 240
      • Y座標: (1080-1080)/2 = 0
    • つまり、1920x1080のキャンバスの(240, 0)の位置に1440x1080の映像を配置します。これにより、左右に240pxずつの黒帯が追加され、中央に配置されます。
  3. setsar=1:

    • これは非常に重要なフィルターです。scalepadを適用すると、SAR (Sample Aspect Ratio)という内部的なアスペクト比情報が意図せず変更されることがあります。これが狂うと、プレーヤーで再生した際にアスペクト比が崩れる原因となります。
    • setsar=1(またはsetsar=1/1)は、ピクセルが正方形であること(SAR=1:1)を明示的に宣言し、アスペクト比の問題を未然に防ぎます。

これらの処理を終えた結果に[v1]と名前を付け、それを元の16:9動画[0:v]と結合することで、アスペクト比を維持したままシームレスに繋がった動画が完成します。

第4章: フレームレートやピクセルフォーマットが違う動画の結合

解像度と同様に、フレームレート(fps)やピクセルフォーマットの違いも結合時の問題となり得ます。これらもconcat filterの自動調整に任せるのではなく、明示的に制御することで、より高品質な結果を得られます。

フレームレート (FPS) の違い

問題シナリオ:
* film_look.mp4: 23.976fps (映画のような質感)
* gameplay.mp4: 60fps (滑らかなゲームプレイ映像)

これを単純に結合するとどうなるでしょうか。仮にfilm_look.mp4を最初に置いた場合、concatは全体のタイムベースを23.976fps基準で構築しようとします。その結果、後続のgameplay.mp4の60フレーム/秒の映像は、23.976フレーム/秒に合うようにフレームが間引かれ(ドロップされ)、カクカクとした不自然な動きになってしまいます。逆に60fpsを基準にすると、23.976fpsの映像のフレームが複製され、これもまた不自然な動きに見えることがあります。

解決策: fpsフィルターでフレームレートを統一する

最も一般的な解決策は、すべての動画を共通の目標フレームレート(例えば、Webで一般的な29.97fpsや30fps)に変換してから結合することです。

目標: すべての動画を30fpsに統一してから結合する。

bash
ffmpeg -i film_look.mp4 -i gameplay.mp4 \
-filter_complex \
"[0:v]fps=30[v0]; \
[1:v]fps=30[v1]; \
[v0][v1]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
output_30fps.mp4

コマンド解説:
* [0:v]fps=30[v0]: 1番目の動画(film_look.mp4)を30fpsに変換し、結果を[v0]とします。FFmpegはフレーム補間やドロップを適切に行い、自然な変換を試みます。
* [1:v]fps=30[v1]: 2番目の動画(gameplay.mp4)も同様に30fpsに変換し、結果を[v1]とします。
* [v0][v1]concat...: フレームレートが30fpsに統一された[v0][v1]を結合します。

これにより、動画全体でフレームレートが一定になり、再生時にカクつきが発生するのを防ぐことができます。どのフレームレートを目標にするかは、最終的な動画の用途(Web、Blu-rayなど)や、メインとなる素材の特性を考慮して決定します。

ピクセルフォーマット (Pixel Format) の違い

ピクセルフォーマットは、1ピクセルの色情報をどのようにデータとして保持するかを定義するものです。一般的にWeb動画や民生機器で最も広く使われているのはyuv420pです。しかし、高品質なソースや特定のソフトウェアからはyuv422pyuv444pといった、より色情報が豊富なフォーマットで出力されることがあります。

問題シナリオ:
* pro_source.mov: yuv422p (業務用カメラのソース)
* web_clip.mp4: yuv420p (一般的なWeb動画)

これもconcatは自動変換を試みますが、互換性の問題や意図しない色の変化を避けるため、最も汎用的なフォーマットに明示的に統一するのが安全です。

解決策: formatフィルターでピクセルフォーマットを統一する

目標: すべての動画を最も互換性の高いyuv420pに統一してから結合する。

bash
ffmpeg -i pro_source.mov -i web_clip.mp4 \
-filter_complex \
"[0:v]format=yuv420p[v0]; \
[1:v]format=yuv420p[v1]; \
[v0][v1]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
output_yuv420p.mp4

※思考: [1:v]は元々yuv420pなので、formatフィルタを適用する必要はない。コマンドを簡略化できる。
bash
ffmpeg -i pro_source.mov -i web_clip.mp4 \
-filter_complex \
"[0:v]format=yuv420p[v0]; \
[v0][1:v]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
output_yuv420p.mp4

この方が効率的だが、記事の説明としては両方に適用する方が「統一する」という意図が伝わりやすい。両方に適用しても既にそのフォーマットであれば何もしないので問題ない。可読性と教育的観点から、両方に適用する例で説明を進める。

コマンド解説:
* [0:v]format=yuv420p[v0]: 1番目の動画(pro_source.mov)のピクセルフォーマットをyuv420pに変換します。
* [1:v]format=yuv420p[v1]: 2番目の動画も同様にyuv420pに変換します(この場合、元々yuv420pなので実質的な変化はありませんが、明示的に指定することで確実性が増します)。
* [v0][v1]concat...: フォーマットが統一されたストリームを結合します。

解像度、フレームレート、ピクセルフォーマット、そしてSAR。これらをscale, pad, fps, format, setsarといったフィルターで事前に整えることで、concat filterはその真価を最大限に発揮し、安定した高品質な結合を実現できるのです。

第5章: オーディオストリームの取り扱い

ビデオだけでなく、オーディオもまた、サンプリングレート、チャンネル数、コーデックなどが異なる場合があります。concat filterはオーディオも結合できますが、ここでもビデオと同様の注意とテクニックが必要です。

concat filter のオーディオ処理

concat filterのa=1オプションは、オーディオストリームを1つに結合することを意味します。この際、ビデオと同様に最初の入力オーディオストリームの特性が基準となります。

問題シナリオ:
* interview.mp4: オーディオはステレオ (2ch)、サンプリングレート 48000Hz (48kHz)。
* bgm_clip.mp3: オーディオはモノラル (1ch)、サンプリングレート 44100Hz (44.1kHz)。

これをinterview.mp4を先頭にして結合すると、concatは出力オーディオを「ステレオ、48kHz」にしようとします。その結果、bgm_clip.mp3のオーディオは以下のように自動変換されます。
* チャンネル: モノラル(1ch)からステレオ(2ch)にアップミックスされます。多くの場合、同じ音声が左右両方のチャンネルに割り当てられます。
* サンプリングレート: 44.1kHzから48kHzにリサンプリングされます。

この自動変換は多くの場合でうまく機能しますが、より細かく制御したい場合や、予期せぬ問題を避けたい場合には、明示的なフィルター適用が有効です。

高度なオーディオ処理:aformatフィルター

aformatフィルターは、オーディオのフォーマットを精密に制御するための万能ツールです。サンプリングレート、チャンネルレイアウト(モノ、ステレオなど)、サンプルフォーマット(音の解像度)を一度に指定できます。

目標: すべてのオーディオを「ステレオ、48kHz」に統一してから結合する。

ここで、ビデオの処理とオーディオの処理をフィルタグラフ内で明確に分離して書くテクニックを紹介します。これは複雑な処理を行う際の可読性と保守性を大幅に向上させます。

bash
ffmpeg -i interview.mp4 -i bgm_clip.mp4 \
-filter_complex \
"[0:v]setsar=1[v0]; \
[1:v]setsar=1[v1]; \
[v0][v1]concat=n=2:v=1[outv]; \
[0:a]aformat=sample_rates=48000:channel_layouts=stereo[a0]; \
[1:a]aformat=sample_rates=48000:channel_layouts=stereo[a1]; \
[a0][a1]concat=n=2:v=0:a=1[outa]" \
-map "[outv]" -map "[outa]" \
-c:a aac -b:a 192k \
output_audio_unified.mp4

コマンド解説:
この-filter_complexの中身は、セミコロン;で区切られた複数の独立したフィルターチェーンで構成されています。

  1. ビデオ処理チェーン:

    • [0:v]setsar=1[v0];: 1番目のビデオのSARをリセットし[v0]と命名。
    • [1:v]setsar=1[v1];: 2番目のビデオのSARをリセットし[v1]と命名。
    • [v0][v1]concat=n=2:v=1[outv];: [v0][v1]をビデオのみ結合(a=0が省略されている形)し、最終的なビデオ出力を[outv]と命名。
  2. オーディオ処理チェーン:

    • [0:a]aformat=sample_rates=48000:channel_layouts=stereo[a0];: 1番目のオーディオ[0:a]を48kHzステレオにフォーマットし、[a0]と命名。
    • [1:a]aformat=sample_rates=48000:channel_layouts=stereo[a1];: 2番目のオーディオ[1:a]も同様に48kHzステレオにフォーマットし、[a1]と命名。
    • [a0][a1]concat=n=2:v=0:a=1[outa]: フォーマットが統一された[a0][a1]をオーディオのみ結合(v=0を明示)し、最終的なオーディオ出力を[outa]と命名。
  3. マッピング:

    • -map "[outv]": 最終的なビデオとして[outv]を指定。
    • -map "[outa]": 最終的なオーディオとして[outa]を指定。
  4. オーディオエンコード設定:

    • -c:a aac -b:a 192k: オーディオコーデックをAAC、ビットレートを192kbpsに指定。

このようにビデオとオーディオの処理を分離することで、それぞれの処理内容が明確になり、どんなに複雑なフィルタを組み合わせても混乱しにくくなります。これはconcat filterを使いこなす上で極めて重要なテクニックです。

第6章: 応用テクニックとトラブルシューティング

最後に、より実践的なシナリオへの対応と、よく遭遇するエラーの解決策を見ていきましょう。

応用1: 音声がない動画と音声がある動画の結合

問題シナリオ:
* silent_broll.mp4: ビデオのみで、オーディオストリームが存在しない。
* main_clip.mp4: ビデオとオーディオの両方が存在する。

この2つを結合しようとすると、[0:a]silent_broll.mp4のオーディオ)が存在しないため、FFmpegは「Stream specifier '0:a' matches no stream」のようなエラーを出して失敗します。

解決策: anullsrcフィルターで無音のオーディオを生成する

anullsrcは、指定した長さやフォーマットの無音のオーディオストリームを生成する特殊なフィルターです。これを使って、音声がない動画にダミーの無音トラックを割り当てます。

目標: silent_broll.mp4main_clip.mp4のオーディオ仕様に合わせた無音トラックを追加してから結合する。

bash
ffmpeg -i silent_broll.mp4 -i main_clip.mp4 \
-filter_complex \
"anullsrc=channel_layout=stereo:sample_rate=48000[a_dummy]; \
[0:v][1:v]concat=n=2:v=1[outv]; \
[a_dummy][1:a]concat=n=2:v=0:a=1[outa]" \
-map "[outv]" -map "[outa]" \
-shortest \
output_with_silent.mp4

※思考: main_clip.mp4のオーディオ仕様を事前にffprobeで調べておく必要がある。ここでは仮にステレオ/48kHzとする。

コマンド解説:
* anullsrc=channel_layout=stereo:sample_rate=48000[a_dummy]:
* main_clip.mp4のオーディオ仕様に合わせて、ステレオ/48kHzの無音オーディオストリームを生成します。
* この無音ストリームに[a_dummy]という名前を付けます。
* [0:v][1:v]concat...[outv]: ビデオは通常通り結合します。
* [a_dummy][1:a]concat...[outa]:
* concatの1番目のオーディオ入力として、生成した無音の[a_dummy]を使います。
* 2番目のオーディオ入力として、main_clip.mp4[1:a]を使います。
* これにより、最初の動画の部分は無音になり、2番目の動画の部分は元の音声が流れる、一つのオーディオストリーム[outa]が生成されます。
* -shortest: このオプションは非常に重要です。anullsrcは無限に無音を生成し続けるため、このオプションがないとFFmpegは処理を終了できません。-shortestは、入力ストリームの中で最も再生時間が短いものに合わせて全体の長さを決定するよう指示します。この場合、ビデオストリームの長さに合わせて処理が終了します。

応用2: 動画の間にトランジションを入れる

concatは単純なカット繋ぎ(瞬時に切り替わる)しかできません。フェードイン・フェードアウトのようなトランジション効果を入れたい場合は、xfadeフィルターなど、より高度なフィルターの出番となります。これはconcatの範囲を超える応用ですが、知っておくと表現の幅が広がります。xfadeは2つの動画間に様々な視覚効果を適用でき、concatと組み合わせて使うことも可能です。

よくあるエラーとその対処法

  • Stream specifier '...' matches no stream.

    • 原因: 指定したストリームが存在しない。例えば、音声のない動画に対して[0:a]を指定した、入力ファイルのインデックスを間違えた(-iが2つしかないのに[2:v]を使った)など。
    • 対処法: ffprobe inputfile.mp4コマンドを使って、各入力ファイルのストリーム構成(どのインデックスにビデオやオーディオがあるか)を事前に確認しましょう。音声がない場合はanullsrcを使います。
  • Cannot find a common pixel format for the link... / Inputs do not have the same sample rate.

    • 原因: concatの自動変換がうまく機能しない、互換性のないフォーマット(ピクセルフォーマットやサンプリングレート)の組み合わせ。
    • 対処法: 第4章、第5章で解説した通り、formatフィルターやaformatフィルターを使って、結合前にすべてのストリームを共通のフォーマットに明示的に変換してください。
  • Invalid argument

    • 原因: フィルターのパラメータ指定が間違っている。例えばconcat=n:3=が抜けている)、scale=1920x1080:であるべきところがxになっている)など、単純なタイプミスが多いです。
    • 対処法: コマンドを注意深く見直し、FFmpegの公式ドキュメントで各フィルターの正しい構文を確認してください。

結論

この記事では、FFmpegのconcat filterを軸に、仕様がバラバラな動画ファイルを華麗に結合するための包括的なテクニックを解説してきました。

私たちはまず、高速だが制約の多いconcat demuxerと、柔軟で高機能なconcat filterの違いを理解し、なぜ後者が私たちの目的にとって最適な選択肢であるかを確認しました。

次に、-filter_complexとストリーム指定子を用いたconcat filterの基本構文を学び、その動作原理の核心である「最初の入力が基準になる」というルールを把握しました。

そして、そのルールを乗り越えるために、
* scale, pad, setsarを駆使して、解像度やアスペクト比の不一致を吸収し、
* fps, formatを用いて、フレームレートやピクセルフォーマットを統一し、
* aformatでオーディオの仕様を揃える

といった、様々なフィルターとの連携テクニックを実践的なコマンド例と共に探求しました。さらに、音声のない動画の扱いや、ビデオとオーディオの処理を分離して記述する高度な構文など、より複雑なシナリオに対応する方法も学びました。

FFmpegは、一見すると難解な呪文の羅列に見えるかもしれません。しかし、その一つ一つの要素が持つ意味と、それらがどのように連携して動作するのかを理解すれば、それは強力で信頼性の高い映像処理ツールへと姿を変えます。ここで紹介したテクニックは、単なる動画結合に留まらず、あらゆる動画編集タスクに応用可能なFFmpegの基本的な考え方そのものです。

コマンドラインでの操作は、GUIのアプリケーションのような手軽さはありませんが、その引き換えに得られるのは、完全な自動化、再現性、そして無限のカスタマイズ性です。ぜひ、この記事を片手に、あなた自身の動画素材で様々な結合を試してみてください。試行錯誤を繰り返すうちに、FFmpegはあなたの最も頼りになる映像制作のパートナーとなることでしょう。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール