Media Foundation で動画に画像・文字を合成する
まず結論
MP4 の各フレームに画像や文字を焼き込む基本形は以下の通りです。
- Source Reader でデコード → 非圧縮フレームを取り出す
- 描画 API で合成 → GDI+ や Direct2D で画像・文字を載せる
- 必要なら色変換 → RGB32 → NV12 など
- Sink Writer で再エンコード → 新しい MP4 として出力
画像や文字を描くのは Media Foundation の仕事ではありません。 GDI+ や Direct2D/DirectWrite などの描画 API を使います。
なぜややこしいのか
「動画に文字を入れる」には実際には4つの要素が混ざっています。
| 要素 | 内容 |
|---|---|
| コンテナとコーデック | mp4 はコンテナで、中身は H.264 などの圧縮データ |
| デコード/エンコード | 圧縮データのままだと描画できないので、非圧縮に戻す必要がある |
| 描画 | 文字や画像の合成は GDI+ や Direct2D の役割 |
| 色空間とピクセル形式 | 描画しやすい形式とエンコーダーが好む形式は違う(RGB32 vs NV12) |
処理イメージ
input.mp4 → Source Reader → 非圧縮フレーム(RGB32) → GDI+で描画 → BGRA→NV12変換 → Sink Writer → output.mp4
パイプラインの分担
入力: Source Reader
MF_SOURCE_READER_ENABLE_VIDEO_PROCESSINGを有効にすると、YUV→RGB32 変換とインターレース解除を自動で行う- 描画しやすいように RGB32 で受け取るのが初手では扱いやすい
描画: GDI+ または Direct2D
- まず動かすなら GDI+: 導入が軽く、1ファイル完結しやすい
- 速度重視なら Direct2D/DirectWrite: 長尺・高解像度向け
色変換: RGB32 → NV12
H.264 エンコーダーは NV12 などの YUV 系を前提とする。Video Processor MFT を使うか、自前で変換する。
出力: Sink Writer
- 出力ストリーム型: ファイルに書きたい形式(例:
MFVideoFormat_H264) - 入力ストリーム型: アプリが渡す形式(例:
MFVideoFormat_NV12)
音声
実務では映像だけ再エンコードし、音声は compressed のまま remux する構成が使いやすい。
サンプルコードのポイント
この記事には Visual Studio 2022 の C++ コンソールアプリにそのまま貼れる 1 ファイル完結サンプル が掲載されています。
OverlayMp4.exe input.mp4 overlay.png output.mp4
コードの特徴
- 前提: Windows 10/11、x64 ビルド、プリコンパイル済みヘッダー不使用
- 入力動画の幅と高さは偶数(NV12 が 4:2:0 のため)
- 出力は映像のみの MP4
- 重ねる文字列は
kOverlayTextで固定(デフォルトはHelloWorld)
実装の流れ
ScopedMfとScopedGdiplusで MF と GDI+ を初期化ConfigureSourceReaderで入力動画の情報を取得し、RGB32 で受け取る設定CreateSinkWriterで出力ファイルを作成(H.264/NV12)- ループで
ReadSample→CopySampleToTopDownBgra→DrawOverlay→BgraToNv12→WriteSample - 最後に
Finalizeで完了
読むときの重要ポイント
stride と上下向きを先に吸収する
動画フレームは stride が width×4 と一致しないことや、上下向きが逆の場合がある。コードでは top-down の BGRA バッファに正規化してから描画している。
ReadSample の flags と sample の両方を見る
ReadSample は S_OK でも sample == nullptr になることがある(STREAMTICK、ENDOFSTREAM など)。HRESULT、flags、inputSample の3つを揃えて見る必要がある。
timestamp と duration は入力を引き継ぐ
固定 fps 前提で毎回計算するより、入力サンプルの timestamp/duration をできるだけ引き継ぐほうが崩れにくい。
本番で伸ばすなら
- 音声 remux を足す: 映像だけ再エンコード、音声はそのまま
- Video Processor MFT を使う: 色空間変換、サイズ変更、インターレース解除をまとめて扱える
- Direct2D/DirectWrite に置き換える: 高解像度・長尺向け
- D3D11 surface ベースに進む: GPU パスへ寄せたい場合
- Custom MFT として切り出す: 複数アプリで再利用したい場合
まとめ
Media Foundation で動画フレームに画像や文字を焼き込むときは、取り出す・描く・変換する・書き戻す の4段階に分けて考えると整理しやすい。最初は Source Reader → RGB32 → GDI+ → NV12 → Sink Writer の構成で動かし、必要に応じて段階的に強化していくのが実務的。
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Media Foundation でYUV→RGB変換
Media Foundation のデコード後に得られる NV12 や YUY2 を RGB へ変換する2通りの方法を整理し、Source Reader の自動変換と自前変換の使い分け、色空間と stride の落とし穴まで実装目線で押さえます。
Media Foundation でMP4から静止画を切り出す
Media Foundation の Source Reader で MP4 から指定時刻に最も近いフレームを取り出し、PNG として保存する手順を解説します。シーク誤差や stride、RGB32 のアルファ未定義などの落とし穴と、安定動作させるための実装ポイントが分かります。
Media Foundation 入門【COMベースのWindowsメディアAPI】
Media Foundation の全体像を、Source Reader / Sink Writer / MFT / Media Session といった部品の役割と、COM 由来の HRESULT・GUID・apartment がどこに顔を出すかから整理します。最初に押さ...
共有メモリの安全な使い方
共有メモリを実務で扱うときの典型的な落とし穴を整理し、同期、可視性、寿命、ABI、権限、クラッシュ復旧まで踏まえた事故率の低い設計の出発点を、Windows と POSIX の両方の API を交えながら具体的に示します。
C#をNative AOTでDLL化しC/C++から呼び出す方法
C# のクラスライブラリを Native AOT でネイティブ DLL として発行し、UnmanagedCallersOnly で C/C++ から呼び出す構成を、設計指針・最小実装・はまりどころまでまとめて整理した実務向け解説記事です。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。