読者です 読者をやめる 読者になる 読者になる

しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

WPF を使って動画のサムネイルを生成してみた

かなり久しぶりに WPF を使って動画からサムネイルを作ってみるコードを書いてみました。

最初は MediaElement を作って、そのまま RenderTargetBitmap にレンダリングすれば良いかなと思ったんですが、それだと MediaElement 自体の描画が行われないみたいなので、DrawingVisual を使ってみたら上手くいきました。

とりあえずコードを一通り載せておきます。

int maxWidth = 640;

// キャプチャする動画を読み込んでおく
var player = new MediaPlayer
{
    ScrubbingEnabled = true
};

// 動画を開いてすぐに一時停止する
player.Open(new Uri(@"D:\video.mp4", UriKind.Absolute));
player.Pause();

// 指定位置へシーク
player.Position = TimeSpan.FromSeconds(32);

// 読み込みが完了するまで待機
while (player.DownloadProgress < 1.0 && player.NaturalVideoWidth == 0)
{
    Thread.Sleep(100);
}

// リサイズ後のサイズを計算
var ratio = (double)maxWidth / player.NaturalVideoWidth;

int width = (int)(player.NaturalVideoWidth * ratio);
int height = (int)(player.NaturalVideoHeight * ratio);

// 描画用の Visual を用意
var visual = new DrawingVisual();

using (var context = visual.RenderOpen())
{
    context.DrawVideo(player, new System.Windows.Rect(0, 0, width, height));
}

// レンダリングするビットマップを用意
var bitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);

// ビットマップに Visual をレンダリング
bitmap.Render(visual);

// PNG として保存
var encoder = new PngBitmapEncoder();

encoder.Frames.Add(BitmapFrame.Create(bitmap));

using (var stream = File.OpenWrite(@"D:\sample.png"))
{
    encoder.Save(stream);
}

注意点は ScrubbingEnabled を true にしておかないと、Position プロパティを弄ってシークさせた時にサムネイルが更新されないので上手く動きません。テストとして今回はクラウディアさんの動画を使いました。

クラウド ガール 技術解説マンガ | MSDN

f:id:shiba-yan:20140622015357p:plain

ちゃんとサムネイルを切り出すことが出来ました。

あと、Azure Web サイトの WebJobs として使いたくて、その前にコンソールアプリケーションを作ってテストしたのですが、残念ながら動きませんでした。デコーダーとかの関係があるのかもしれません。*1

Windows Azureメディア・サービスを利用したアプリケーション開発 - Build Insider

ちなみに Azure Media Services を使うと簡単にサムネイル作れるみたいですね。

*1:ffmpeg とか使ってコマンドラインベースでやるしかないかも