旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【WPF】 スクリーンショットを撮り続けて動画に保存する

タイトルにあるようにスクリーンショットを撮り続けて動画に保存する方法のメモをします。
まずはスクリーンショットを撮る部分ですが、スクリーンショットをそのまま動画にすると容量がすごいことになるので、スクリーンショットを撮る⇒縮小するまでを作成します。

// Bitmapクラスを使うため参照の追加が必要
using System.Drawing;

private Bitmap GetScreenShot(int width, int height)
{
    var resizedBmp = new Bitmap(width, height);
    using (var bmp = new Bitmap((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight))
    using (Graphics g = Graphics.FromImage(bmp))
    using (Graphics resizedG = Graphics.FromImage(resizedBmp))
    {
        // スクリーンショットを撮る
        g.CopyFromScreen(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, 0), bmp.Size);

        // 動画サイズを減らすためリサイズする
        resizedG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
        resizedG.DrawImage(bmp, 0, 0, width, height);
    }

    return resizedBmp;
}


一定時間ごとに上記メソッドを呼び出す必要があるので、DispatcherTimerクラスを利用します。動画保存はAccord.Video.VFWのAVIWriterクラスを利用します。Accord.Video.VFWはNuGETを使ってインストールします。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;

using Accord.Video.VFW;
using System.Drawing;
using System.Windows.Threading;

namespace CSMovie
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly DispatcherTimer _timer = new DispatcherTimer();
        private readonly AVIWriter _aviWriter = new AVIWriter();
        // フレームレート
        private readonly int _frameRate = 15;
        // 動画の横サイズ
        private readonly int _videoWidth = 640;
        // 動画の縦サイズ
        private readonly int _videoHeight = 480;
        private readonly List<Task> _tasks = new List<Task>();

        public MainWindow()
        {
            InitializeComponent();

            InitializeDispatcherTimer();

            InitializeVideo();
        }

        private void InitializeDispatcherTimer()
        {
            // フレームレートに合わせて起動時間を設定する
            int interval_msec = 1000 / _frameRate;
            _timer.Interval = new TimeSpan(0, 0, 0, 0, interval_msec);
            _timer.Tick += dispatcherTimer_Tick;
        }

        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            // 時間がかかる処理なのでバックグラウンドで実行する
            _tasks.Add(
                Task.Run(() =>
                {
                    // スクリーンショットを撮る
                    using (Bitmap bmp = GetScreenShot(_videoWidth, _videoHeight))
                    {
                        // フレームに追加する
                        _aviWriter.AddFrame(bmp);
                    }
                }));
        }

        private void InitializeVideo()
        {
            _aviWriter.FrameRate = _frameRate;
            _aviWriter.Open("video.avi", _videoWidth, _videoHeight);
        }

        private void Window_ContentRendered(object sender, EventArgs e)
        {
            _timer.Start();
        }

        private Bitmap GetScreenShot(int width, int height)
        {
            var resizedBmp = new Bitmap(width, height);
            using (var bmp = new Bitmap((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight))
            using (Graphics g = Graphics.FromImage(bmp))
            using (Graphics resizedG = Graphics.FromImage(resizedBmp))
            {
                // スクリーンショットを撮る
                g.CopyFromScreen(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, 0), bmp.Size);

                // 動画サイズを減らすためリサイズする
                resizedG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
                resizedG.DrawImage(bmp, 0, 0, width, height);
            }

            return resizedBmp;
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            // タイマーを止める
            _timer.Stop();
            // 動作中のタスクがあれば待つ
            foreach (var task in _tasks)
            {
                task.Wait();
            }
            if (_aviWriter != null)
            {
                _aviWriter.Close();
                _aviWriter.Dispose();
            }
        }
    }
}


ちなみにスクリーンショットを撮る部分はここを参考にしました。

fieldlight.blog126.fc2.com