C#で動画を作ってみた話

#はじめに
C#の知識が.Net Framework 3.5あたりで止まっているので、ちょっとまずいなと思っている黒髭和熊です。
まぁ、作るのには困らないんですけどね・・・

さて、今回ですが、Youtubeで動画を公開しているのですが、その動画のオープニングを作りたいなぁと思っていました。
で、昔から使ってるキャラクタを動かしたいなと思ったんですが、簡単にできないかなー？と思ったんです。
面倒なので、描いた絵をプログラムで動かせば思い通りにできるんじゃないか？ということで調べて作りました。

動画はこれ。
ちなみに、作った動画を[Davinci Resolve](https://www.blackmagicdesign.com/jp/products/davinciresolve/)で音をつけたりフェードアウトさせています。
<iframe width="560" height="315" src="https://www.youtube.com/embed/yd971i1IGbg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

そんなお話です。

#使ったもの
- Visual Studio
- C#
- [AForge.NET](http://www.aforgenet.com/)

#概要
動画のオープニングだけじゃなくて、ほかの動画も作ることを考えています。
なので、アニメーションを行う部分は抽象化し、動画にする部分は共通の処理にしています。
ですが、そこはあまり重要ではないので触れません。(知りたい人がいれば書きます)
今回使用したAForgeをどう使っているかを紹介できればと思っています。

クラス図はこんな感じです。
Form1で管理して、描画は別のクラス、といった感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277984/bc4cfeb3-d19a-737c-2530-049e46b7620d.png)

実際の画面はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277984/60292b34-f3d2-2002-7148-4aff96797e6d.png)

さて、処理についてですが、作ったプログラムに沿った形で説明していきます。
AForgeの使用方法にあまり関係ないところがありますが、クラス図と比較しながら見ていただければと思います。

AbstructAnimationクラスを実装したChannelOpeningクラスをForm1に登録します。
ChannelOpeningクラスあオープニング動画の描画を行うクラスになります。

```csharp:Form1.cs
        private VideoFileWriter videoWriter = null;

        private Thread thread = null;
        private Bitmap bmp = null;

        private AbstractAnimation currentAnime = null;

    // ・・・省略・・・

        private void Form1_Load(object sender, EventArgs e)
        {
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            cmbAnime.Items.Add(new AnimeTest(this));
            cmbAnime.Items.Add(new IngressHyperion(this));
            cmbAnime.Items.Add(new ChannelOpening(this));

            cmbAnime.SelectedIndex = 0;
        }
```

実行はボタンのクリックで開始します。
このタイミングで、AForge.Video.FFMPEG.VideoFileWriterのインスタンスを生成します。

```csharp:Form1.cs
        private void btnExecute_Click(object sender, EventArgs e)
        {
    // ・・・省略・・・
               bmp = new Bitmap(AnimationConfig.bmpWidth, AnimationConfig.bmpHeight);

                // アニメーション初期化
                currentAnime.Initalize();

                if (AnimationConfig.isWriteMode == true)
                {
                    var text = txtLength.Text;
                    var movieLength = AnimationConfig.movieLength;
                    AnimationConfig.movieLength = movieLength;
                    AnimationConfig.writeFrameCnt = 0;
                    videoWriter = new VideoFileWriter();
                    videoWriter.Open(@"C:\data\test4.mp4", bmp.Width, bmp.Height, AnimationConfig.framerate, VideoCodec.MPEG4);
                }
```

VideoFileWriter.Open()メソッドで保存するファイル名や動画のサイズ、フォーマットを指定します。
ここで気を付けるべきはフォーマット。
AVIで保存すると結構大きなファイルになります。
フォーマットは[VideoCodec](http://www.aforgenet.com/framework/docs/html/975f81c7-c67f-baec-cd43-3daaaf8023b3.htm)列挙型で定義されています。
定義内容は公式ドキュメントの[VideoCodec Enumeration](http://www.aforgenet.com/framework/docs/html/975f81c7-c67f-baec-cd43-3daaaf8023b3.htm)を見てもらえばわかります。
私はVideoCodec.MPEG4を選択しています。
フレームレートはAnimationConfigクラスで60を指定しており、その値を利用しています。

ボタンを押したタイミングでスレッドを作成し、描画用メソッドdraw()を呼び出しています。
その中でChannelOpeningクラスの描画した内容をVideoFileWriterのWriteVideoFrameメソッドで書き込んでいます。

```csharp:Form1.cs

    // ・・・省略・・・

                thread = new Thread((ThreadStart)delegate ()
                {
                    long tm = 0;
                    long frm = 0;
                    while (thread != null)
                    {
                        tm = DateTime.Now.Ticks;
                        draw();

    // ・・・省略・・・

        private void draw()
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                currentAnime.OnDraw(g);

                if (AnimationConfig.isWriteMode == true && videoWriter != null)
                {
                    videoWriter.WriteVideoFrame(bmp);
                }
            }
        }
```

書き込みはすごく簡単で、[VideoFileWriter.WriteVideoFrame()](http://www.aforgenet.com/framework/docs/html/f778a996-56fb-7ce3-7639-2a46aa6d5f00.htm)メソッドに作成したビットマップを渡すだけです。
書き込みが終わったら、Close()メソッドで、VideoFileWriterを閉じればOKです。

```csharp:Form1.cs
    videoWriter.Close();
```


#終わりに

ざっくりですが、C#で動画を作ってみた話を紹介しました。
Magick.NETも試したのですが、私の実装が悪かったせいか、動画を作れないことがあったので断念。
AForge.NETはそんなこともなく割と簡単に使えたので採用しました。
音もやればできるみたいですが、まぁ、音はDavinci Resolve使ったほうが楽だったので^^;

~~実際のコードはたぶんGithubに公開すると思います。
いつになるかわかりませんがｗ~~
Githubにリポジトリ作りました。
[こちら](https://github.com/project-dev/CSharpAnimation)になります。

あ、最後に。
よかったら私のYoutubeの[チャンネル](https://www.youtube.com/channel/UCQyM1nHzm_XLFh2ok8TN9XQ)も見てみてください。
そのうちこの記事の内容も動画にしようと思っています。