FFmpegが実際にどう機能するのか(そしてなぜ最初のコマンドが失敗したのか)
FFmpegは誰もが最初に間違えてしまう単純な原則で動作します:入力からストリームを読み込み、それをフィルターを通して処理し、出力に書き込みます。混乱の原因は、1つのビデオファイルに複数のストリームが含まれていること—通常は1つのビデオストリーム、1つ以上のオーディオストリーム、時には字幕ストリームやメタデータストリームがあることから来ます。 `ffmpeg -i input.mp4 output.mp4`を実行すると、FFmpegはあなたが何を望んでいるのかについて多くの仮定をします。「最適な」ビデオストリーム、「最適な」オーディオストリームを選択し、デフォルトのエンコーダを通してそれらをコピーし、出力コンテナにマルチプレクスします。これは簡単な変換には問題ありませんが、制御が必要になると崩れるのです。 私の最初のコマンドが0バイトファイルを生成した理由は、互換性のないコーデックとコンテナの組み合わせを指定していたためです。私はVP9ビデオストリームをMP4コンテナに入れようとしていたのですが、これはサポートされていません。FFmpegはエンコーディングを開始し、出力を書き込めないことに気づいて諦めました。エラーメッセージは、私が読む方法を知らなかった200行の出力に埋もれていました。 私にとってすべてを変えたメンタルモデルはこちらです:FFmpegを3つの段階を持つパイプラインとして考えます。最初はデマルクシング—FFmpegは入力ファイルを開き、個々のストリームに分けます。次に、処理—各ストリームはコーデック(エンコーダ/デコーダ)とオプションのフィルターを通過します。3つ目がマルチプレクシング—処理されたストリームが出力コンテナにパッケージされます。 すべてのFFmpegコマンドはこのパターンに従います: ``` ffmpeg [グローバルオプション] [入力オプション] -i 入力 [出力オプション] 出力 ``` 順序は非常に重要です。`-i`の前のオプションは入力に適用され、`-i`の後のオプションは出力に適用されます。`-c:v libx264`を`-i`の前に置くと、FFmpegは入力をH.264としてデコードしようとし、おそらくそれは望んでいることではありません。`-i`の後に置けば、出力がH.264としてエンコードされます。 もう1つの重要な概念はストリーム指定子です。`-c:v`を書くと、「このコーデックをビデオストリームに適用する」と言っています。`-c:a`はオーディオストリームを対象にしています。`-c:s`は字幕を対象にしています。`-c:v:0`で最初のビデオストリームや、`-c:a:1`で2番目のオーディオストリームを指定することで、さらに具体的に指定できます。 この構造を理解した後、私は0バイトファイルを生成するのをやめました。私はFFmpegの出力を読み取り、各段階で何をしているのかを理解できました。デマルクシング、処理、マルチプレクシングのいずれに問題があるかを特定することで、問題をデバッグできました。私が50,000本のビデオをトランスコードした日(そして私が学んだこと)
3年前、私たちの会社が競合を買収しました。その買収の一環として、彼らの全ビデオライブラリ—50,000本のビデオが我々がサポートしていないフォーマットで提供されました。彼らは特定のプレーヤーを必要とするプロプライエタリコーデックを使用しており、私たちはすべてを標準のH.264に変換する必要がありました。 単純なアプローチは、単純なループを書くことでした:各ビデオに対して、基本設定でFFmpegを実行し、終了するのを待って次に進む。ビデオ1本あたり平均2分かかるとすると、69日間の連続処理が必要です。私たちには2週間しかありませんでした。 このプロジェクトは、私にFFmpegについての理解を前の3年間を合わせた以上に教えてくれました。私はハードウェアアクセラレーション、並列処理、実際に重要な数十のエンコーダ設定について学びました。どの品質指標が意味があり、どれがマーケティングのナンセンスであるかも学びました。最も重要なのは、「最良」のFFmpegコマンドは完全に制約次第であるということです。 私たちは最終的に、40台のマシンで同時に200本のビデオを処理する分散トランスコーディングシステムを構築しました。各マシンは5つのFFmpegインスタンスを実行し、スラッシングせずにCPU使用率を最大化するように慎重に調整されました。利用可能な場合はハードウェアアクセラレーションデコーディングを使用しましたが、品質差が私たちの使用ケースにとって重要であったためソフトウェアエンコーディングを使用しました。 私たちが落ち着いたコマンドは次のようなものでした: ```bash ffmpeg -hwaccel auto -i input.mov \ -c:v libx264 -preset medium -crf 23 \ -c:a aac -b:a 128k \ -movflags +faststart \ -max_muxing_queue_size 1024 \ output.mp4 ``` 各オプションが重要な理由を説明します。`-hwaccel auto`は、利用可能な場合にハードウェアデコーディングを使用するようにFFmpegに指示します—これにより、対応するGPUを持つマシンでデコード時間が60%削減されました。`-preset medium`は、エンコーディング速度と圧縮効率のバランスを取ります。すべてのプリセットをテストしましたが、`medium`は、半分の時間で`slower`の95%の品質を達成するスイートスポットでした。 `-crf 23`設定は、定数レートファクターを使用して品質を制御します。低い数値は高品質と大きなファイルを意味します。100本のビデオをサンプルとして使って18から28までのCRF値をテストし、我々のビデオチームにブラインド品質比較をしてもらいました。誰もCRF 23とCRF 20の区別をつけられませんでしたが、ファイルサイズは30%小さくなりました。 `-movflags +faststart`はmoovアトムをファイルの先頭に移動し、HTTPでのプログレッシブ再生を可能にします。このフラグがなければ、ブラウザは再生を開始する前にファイル全体をダウンロードする必要があります。この1つのオプションで、ユーザーエクスペリエンス指標が15%改善されました。 `-max_muxing_queue_size 1024`オプションは、私たちに3日間のデバッグを必要とした問題を解決しました。一部のソースビデオには可変フレームレートがあり、FFmpegの内部バッファがオーバーフローしました。デフォルトのキューサイズは8パケットで、VFRコンテンツには不十分です。1024に増やすことによって、「出力ストリームのためにバッファリングされたパケットが多すぎる」というエラーが解消され、我々の変換の5%が失敗することがなくなりました。 私たちは11日でプロジェクトを完了しました。分散システムは1日で4,545本のビデオを処理し、成功率は99.2%でした。失敗はすべて、破損したソースファイルまたはデコードできないコーデックを使用したものでした。私は今でもそのコマンドのバリエーションを使用しています—それは私たちの全ビデオ処理パイプラインの基盤です。コーデックとコンテナの互換性マトリックス
初心者にとってFFmpegで最も苛立たしい側面の一つは、どのコーデックがどのコンテナと互換性があるのかを理解することです。あなたは完璧なコマンドを作成するのに1時間を費やすことができますが、互換性のないコーデックをサポートしていないコンテナに入れようとして失敗するかもしれません。 こちらが私が常に参照する互換性マトリックスです:| コンテナ | ビデオコーデック | オーディオコーデック | 最適な用途 |
|---|---|---|---|
| MP4 | H.264, H.265, AV1 | AAC, MP3, Opus | ウェブ再生、モバイルデバイス、ユニバーサルな互換性 |
| WebM | VP8, VP9, AV1 | Vorbis, Opus | ウェブストリーミング、オープンソースプロジェクト、YouTube |
| MKV | すべて | すべて | アーカイブ、複数のオーディオトラック、字幕 |
| MOV | H.264, ProRes, DNxHD | AAC, PCM | プロフェッショナルな編集、Appleエコシステム |
| AVI | MPEG-4, H.264(制限付き) | MP3, PCM | レガシーシステム(新規プロジェクトでは避けるべき) |
| TS | H.264, H.265, MPEG-2 | AAC, MP3, AC-3 | 放送、HLSストリーミング |
誰も正しく説明しない品質設定
すべてのFFmpegチュートリアルは「良い品質」のために`-crf 23`を使用するか「5メガビット毎秒」のために`-b:v 5M`を使用するように言います。しかし、誰もこれらの設定が実際に何をするのか、またあなたのコンテンツに対して正しい値を選ぶ方法を説明しません。 私はさまざまなタイプのコンテンツに対して品質設定をテストするのに何百時間も費やしました。私が学んだことは、普遍的な「最良」の設定は存在しないということです。最適な品質パラメータは、あなたのコンテンツタイプ、ターゲットオーディエンス、配布方法によって異なります。「定数レートファクター(CRF)は、特定の品質レベルを指定し、その品質を達成するために必要なだけ多くのビットを使用するエンコーディングモードです。CRFの数値が低いほど、品質が高く、ファイルサイズが大きくなります。H.264の場合、範囲は0から51で、0はロスレス、51は最悪の品質です。デフォルトは23で、ほとんどのコンテンツでは『視覚的に透明』と見なされています—つまり、ほとんどの人がソースと区別できないということです。」CRFの問題は、可変ビットレート出力を生成することです。高動作のアクションシーンは10Mbpsを使用する一方で、静的なトーキングヘッドシーンは2Mbpsを使用するかもしれません。これはファイルサイズには効率的ですが、ストリーミングでは予測可能な帯域幅使用が必要なため問題を引き起こすことがあります。 ストリーミングの場合、一定ビットレート(CBR)または制約付きの可変ビットレート(VBR)が必要です。私がストリーミングに使用するコマンドは次の通りです: ```bash ffmpeg -i input.mp4 \ -c:v libx264 -preset medium \ -b:v 5M -maxrate 5M -bufsize 10M \ -c:a aac -b:a 128k \ output.mp4 ``` `-b:v 5M`はターゲットビットレートを5メガビット毎秒に設定します。`-maxrate 5M`はそのレートを超えないようにします。`-bufsize 10M`はデコーダバッファサイズをビットレートの2倍に設定します。これは標準の推奨事項です。これにより、バッファリングなしでスムーズにストリーミングされる出力が生成されます。 しかし、ほとんどの人が間違えるのは、ビットレート要件は解像度と動きに応じてスケールすることで、線形ではないということです。1080pビデオは720pビデオの2倍のビットレートを必要としません—約1.5倍です。4Kビデオは1080pの4倍のビットレートを必要としません—約2.5倍です。
「人間の視覚システムは対数的であり、線形ではありません。ビットレートを倍増させても、」