ビデオコンポジション — アセンブリ、オーディオ、エンコード、検証
FFmpegは配信レイヤーであり、デザインツールではありません。すでにデザインされたシーンを受け取り、クリーンなマスターファイルとプラットフォーム別エンコードを生成する必要があります。
references/FFMPEG-COOKBOOK.md でコピー可能なコマンドを、scripts/validate.sh で配信前チェックを確認してください。
ルールゼロ: 最初にプローブする。仮定したストリームプロパティに基づいてフィルターグラフを構築することは、出力が壊れる最も一般的な原因です。
ffprobe -v error -show_streams -show_format -of json input.mp4
1. 組み合わせる前に正規化する
互換性のない入力は、ジャダー、無音、ドリフト、ハローを引き起こします。マルチソースアセンブリの前に、すべての入力を以下のプロパティで統一します:
| プロパティ | ターゲット |
|---|---|
| 解像度 | プロジェクトマスター(例: 1920×1080) |
| ピクセルアスペクト比 | 1:1 |
| フレームレート | プロジェクトFPS、固定 |
| タイムベース | 一貫性 |
| ピクセルフォーマット | 配信は yuv420p、アルファは yuva444p10le/PNG |
| 色空間/転送特性 | 1つの標準に統一、タグ付け |
| オーディオ | 48 kHz、ステレオ、一貫性のあるコーデック |
ffmpeg -i in.mp4 -vf "scale=1920:1080:flags=lanczos,fps=30,setsar=1" \
-c:v libx264 -crf 14 -preset fast -pix_fmt yuv420p \
-c:a aac -ar 48000 -ac 2 -b:a 256k normalized.mp4
中間ファイルは高品質(CRF 12–16 または ProRes)で保持します。配信バージョンは一度だけエンコードしてください。
2. 連結
同じコーデック、サイズ、fps、ピクセルフォーマット、オーディオレイアウト → ストリームコピー、高速でロスレス:
printf "file 'shot_01.mp4'\nfile 'shot_02.mp4'\n" > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp4
それ以外 → フィルターグラフ:
ffmpeg -i a.mp4 -i b.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30,setsar=1[v0]; \
[1:v]scale=1920:1080,fps=30,setsar=1[v1]; \
[v0][0:a][v1][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" -c:v libx264 -crf 16 -pix_fmt yuv420p -c:a aac final.mp4
一致しない入力をストリームコピー連結しないでください。「成功」して再生が壊れた出力になります。
3. オーバーレイ、アルファ、ピクチャーインピクチャー
# 背景の上に透過要素
ffmpeg -i bg.mp4 -i logo.mov -filter_complex \
"[0:v][1:v]overlay=W-w-48:48:format=auto" -c:v libx264 -crf 16 out.mp4
# ピクチャーインピクチャー、スケーリングと角丸
ffmpeg -i main.mp4 -i insert.mp4 -filter_complex \
"[1:v]scale=640:-2[pip];[0:v][pip]overlay=W-w-40:H-h-40" out.mp4
アルファのルール:
- H.264/MP4 はアルファを保持できません。PNGシーケンス、ProRes 4444、またはWebM/VP9(アルファ対応)を使用してください。
- 各段階の後、コントラストのある背景でアルファを確認してください。
- 暗いエッジでのプレマルチプライドとストレートアルファのハローに注意。ソースレンダラーの規約に合わせてください。
4. オーディオ
# フェードイン/アウトとボイスダッキング付きのBGM
ffmpeg -i video.mp4 -i music.mp3 -i voice.wav -filter_complex \
"[1:a]volume=0.35,afade=t=in:st=0:d=1,afade=t=out:st=28:d=2[bed]; \
[2:a]volume=1.0[vo]; \
[bed][vo]sidechaincompress=threshold=0.08:ratio=6:attack=8:release=250[duck]; \
[duck][vo]amix=inputs=2:duration=first:dropout_transition=0[a]" \
-map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out.mp4
# ラウドネスを測定し、正規化
ffmpeg -i in.mp4 -af loudnorm=I=-14:TP=-1:LRA=11:print_format=json -f null -
ffmpeg -i in.mp4 -af loudnorm=I=-14:TP=-1:LRA=11 -c:v copy out.mp4
ターゲットは配信先によって異なります(多くのウェブプラットフォームは -14 LUFS、放送は -23/-24)。測定してから選択してください。すでにターゲットに達しているファイルを正規化しないでください。
5. 字幕
# 焼き込み(ハードサブ)
ffmpeg -i in.mp4 -vf "subtitles=captions.srt:force_style='FontName=Inter,FontSize=22,PrimaryColour=&H00FFFFFF,OutlineColour=&H80000000,BorderStyle=3,Outline=1,Shadow=0'" \
-c:v libx264 -crf 18 -c:a copy out.mp4
# ソフトサブ(移動可能なトラック)
ffmpeg -i in.mp4 -i captions.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng out.mp4
RTLを確認: シェーピング対応フォント、正しい Alignment、レンダリングフレームの検査。キャプションを安全領域内に、プラットフォームUIの上に配置してください。
6. プラットフォームエンコード
# 横長 YouTube、1080p30
ffmpeg -i master.mov -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p \
-profile:v high -level 4.1 -movflags +faststart \
-c:a aac -b:a 192k -ar 48000 out_1080p.mp4
# 縦型ソーシャル、1080x1920
ffmpeg -i master.mov -vf "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" \
-c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p -movflags +faststart \
-c:a aac -b:a 192k out_vertical.mp4
# さらなるコンポジション用の高品質中間ファイル
ffmpeg -i master.mov -c:v prores_ks -profile:v 3 -pix_fmt yuv422p10le -c:a pcm_s24le master.mov
エンコーダが存在することを確認: ffmpeg -encoders | grep -E "libx264|prores|nvenc"。ハードウェアエンコーダは高速ですが、ビットレートあたりの品質が低いことが多いため、出荷前に比較してください。
7. フレーム、コンタクトシート、GIF
# 特定の時刻の静止画
ffmpeg -ss 00:00:03.5 -i in.mp4 -frames:v 1 frame.png
# コンタクトシート
ffmpeg -i in.mp4 -vf "fps=1,scale=320:-1,tile=4x4" -frames:v 1 sheet.jpg
# 高品質GIF(palettegen/paletteuse)
ffmpeg -i in.mp4 -vf "fps=15,scale=640:-1:flags=lanczos,palettegen" palette.png
ffmpeg -i in.mp4 -i palette.png -lavfi "fps=15,scale=640:-1:flags=lanczos[x];[x][1:v]paletteuse" out.gif
8. 検証(スキップしない)
ffprobe -v error \
-show_entries format=duration,size,bit_rate,format_name \
-show_entries stream=index,codec_type,codec_name,width,height,r_frame_rate,avg_frame_rate,pix_fmt,channels,sample_rate,nb_frames \
-of json final.mp4
自動チェック:
# 黒フレームセグメント(意図しない黒フレーム)
ffmpeg -i final.mp4 -vf "blackdetect=d=0.1:pix_th=0.05" -an -f null - 2>&1 | grep blackdetect || true
# フリーズセグメント(静止が長すぎる)
ffmpeg -i final.mp4 -vf "freezedetect=n=0.003:d=2" -an -f null - 2>&1 | grep freeze || true
# オーディオの有無とピークレベル
ffmpeg -i final.mp4 -af volumedetect -f null - 2>&1 | grep -E "mean_volume|max_volume"
その後、最初のフレーム、最後のフレーム、各トランジション、テキストの多いフレームを確認してください。数値はファイルが適切に形成されていることを証明しますが、見た目が正しいことを証明するわけではありません。
9. フレーム精度の落とし穴
- VFRソース(スクリーン録画、スマートフォン)はCFRタイムラインに対してドリフトします。
fps=30で正規化し、再生時間を確認してください。 - 1フレームの誤差: 30fpsでNフレームは正確にN/30秒です。300フレームのレンダリングは10.0秒です。再生時間が9.97秒の場合、フレームがドロップされています。
- ビデオより短いオーディオ → 最後に無音が入ります。意図的にパディングするかトリミングしてください。
- 回転したスマートフォン動画には表示行列が含まれています。
-noautorotateは本当に必要な場合のみ使用してください。 - エンコード後の色ずれ:
color_primaries/transferタグを確認。ブラウザプレビューは信頼できる色基準ではありません。 - 繰り返しエンコードは品質を劣化させます。高品質のマスターを保持し、そこからエンコードしてください。
10. 配信パッケージ
output/
final_1080p.mp4 # プライマリ
final_vertical.mp4 # プラットフォームバリアント(必要な場合)
final_master.mov # ProRes/H.264 CRF 14、将来のエンコードのために保持
thumbnail.png # 1280x720 または 1080x1920
contact-sheet.jpg
captions.srt
project-report.md # ツールバージョン、設定、実行したチェック
ffprobeで実際のプロパティを報告し、推測しないでください。