Media Foundation で動画に画像・文字を合成する

· · Media Foundation, C++, Windows開発, GDI+, Direct2D, DirectWrite, H.264

まず結論

MP4 の各フレームに画像や文字を焼き込む基本形は以下の通りです。

  1. Source Reader でデコード → 非圧縮フレームを取り出す
  2. 描画 API で合成 → GDI+ や Direct2D で画像・文字を載せる
  3. 必要なら色変換 → RGB32 → NV12 など
  4. 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

実装の流れ

  1. ScopedMfScopedGdiplus で MF と GDI+ を初期化
  2. ConfigureSourceReader で入力動画の情報を取得し、RGB32 で受け取る設定
  3. CreateSinkWriter で出力ファイルを作成(H.264/NV12)
  4. ループで ReadSampleCopySampleToTopDownBgraDrawOverlayBgraToNv12WriteSample
  5. 最後に Finalize で完了

読むときの重要ポイント

stride と上下向きを先に吸収する

動画フレームは stride が width×4 と一致しないことや、上下向きが逆の場合がある。コードでは top-down の BGRA バッファに正規化してから描画している。

ReadSample の flags と sample の両方を見る

ReadSampleS_OK でも sample == nullptr になることがある(STREAMTICK、ENDOFSTREAM など)。HRESULTflagsinputSample の3つを揃えて見る必要がある。

timestamp と duration は入力を引き継ぐ

固定 fps 前提で毎回計算するより、入力サンプルの timestamp/duration をできるだけ引き継ぐほうが崩れにくい。

本番で伸ばすなら

  1. 音声 remux を足す: 映像だけ再エンコード、音声はそのまま
  2. Video Processor MFT を使う: 色空間変換、サイズ変更、インターレース解除をまとめて扱える
  3. Direct2D/DirectWrite に置き換える: 高解像度・長尺向け
  4. D3D11 surface ベースに進む: GPU パスへ寄せたい場合
  5. 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 のアルファ未定義などの落とし穴と、安定動作させるための実装ポイントが分かります。

記事を読む

共有メモリの安全な使い方

共有メモリを実務で扱うときの典型的な落とし穴を整理し、同期、可視性、寿命、ABI、権限、クラッシュ復旧まで踏まえた事故率の低い設計の出発点を、Windows と POSIX の両方の API を交えながら具体的に示します。

記事を読む

関連トピック

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

このテーマがつながるサービス

この記事は次のサービスページにつながります。近い入口からご覧ください。

ブログ一覧に戻る