C# 動画プログラミングの 1 つです。

WPF の MediaElement や MediaPlayer で動画を再生する方法を紹介したページはいくつか見たのですが、通常再生のみが紹介されており、加工しながらの再生について言及されているページが見当たらなかったので、ここにまとめてみます。

目次


やりたいこと

overlay2動画再生時に、再生している動画の映像に、テキスト等を重ねます。

例として、映像の左上に灰色の長方形を描き、フレームレートや動画再生位置(秒数)といった文字を重ねてみます。

サンプルコード

GitHub にアップしてありますので、そちらからダウンロードしてください。

動作環境については GitHub リポジトリ内のヘルプファイルを参照して下さい。

実装の基本方針

WPF には、(動画に限らず)表示の更新を行うタイミング(つまりフレームごと)で発生するイベントがあり、それが CompositionTarget.Rendering イベントです。

このイベント内で再生中の動画をキャプチャした上で、テキスト等を書き込んでいく、というのが基本的な考え方です。

プログラムの大まかな動作としては、ドラッグ&ドロップされた動画を再生(もちろん音声も)しつつ、上記の方法で映像にテキスト等を重ねています。

プログラムの解説

MainWindow.xaml.cs の解説です。

ファイルをドロップされた際に発生するイベント Window_Drop() において、動画の再生 Play() と、重ね合わせの準備 PrepareOverlay() を行っています。

Play() は単純に、ドロップされたファイルを MediaPlayer で再生しているだけです。

今回の 1 つのポイントは PrepareOverlay() で、ここでフレーム描画イベントハンドラー CompositionTarget.Rendering の設定を行っています。

ここで設定されたイベントハンドラーは WPF での描画が必要なタイミングで常に呼びだされるため、仮に動画が再生されていなくても定期的に呼びだされます。呼びだされる頻度は環境によって異なるとのことで、私の環境では通常 60 fps 程度で呼びだされていますが、100 fps 程度に上がることもあります。あくまでも「WPF 描画のフレームレート」であって、動画のフレームレートではないことに留意してください。

イベントハンドラーとして設定された CompositionTargetRendering() の後半部分で合成を行っています。

DrawingVisual の DrawingContext に対して、動画、図形(背景の灰色四角形)、テキスト(フレームレートや再生位置)を書き込み、最後にそれらを表示用のビットマップ(mBmp)に書き込みます。

ImagemBmp は PrepareOverlay() で Image コントロールのソースに指定されているため、mBmp の内容がユーザーの目に見える形で表示されることになります。

WPF で動画を扱うメリット

WPF で動画を扱うメリットは、なんといっても手軽なことです。

加工しない再生だけなら MediaElement コントロール 1 つで簡単に動画を再生できますし、加工しても今回のサンプルコードのようにわずかなコード量で実現できます。

標準の C# 開発環境のみで実現できるため、追加のサードパーティーライブラリを用意したり、それをユーザーにインストールしてもらう(または一緒に配布する)という手間もありません。

GPULoadGPU ハードウェア支援が使える場合は、自動的に活用してくれているようです。

また、詳しくは未検証ですが、インストールされているコーデックライブラリ(K-Lite 等)も WPF 側で自動的に利用してくれているようで、扱える動画の形式も無限に広がっていきます。

WPF で動画を扱うデメリット

WPF で動画を扱うデメリットは、(動画の)フレームを厳密に扱えないのではないかということです(扱える方法をご存じの方は教えてください)。

まず、根本的な課題として、動画のフレームレートが取得できません。動画の縦横サイズ等はプロパティーで取得できるのですが、フレームレートを取得するプロパティーはありません。

また、WPF で動きのあるものを処理する際はアニメーションという仕組み(Animatable クラスないしは IAnimatable インターフェース)に則っており、動画(MediaElement / MediaPlayer)も例外ではありませんが、動きの管理が時間単位で行われています。

何らかの方法で動画のフレームレートを取得できたとしても、厳密な意味でのフレーム送りはできず、1 フレームは○○ミリ秒だから○○ミリ秒進める、というような形でのフレーム送りになるのではないかと思います(この辺りは未検証ですが)。30 fps だと 1 フレーム当たり 33.3333.... ミリ秒ですが、小数点の丸めによって微妙に進まない(または 2 フレーム進んでしまう)瞬間が発生したり、そもそも可変フレームレートだとどうしようもないのではないかと思います。

スクリーンショットの動画

このページ冒頭のスクリーンショットに映っている動画は、ダブルレンさんの「お願い☆EternalSummer!」という曲です。素敵な曲なので、是非聴いてみて下さい!


歌いたい時はニコカラバージョンでどうぞ。