おはよう君需要なし

求不得苦な日々

C#でSKnet社のHDMIキャプチャ「MonsterX U3.0R」を使ってみた

 ゲーム配信マンには馴染み深い(?)MonsterXはUSB3.0で1920x1080(60FPS)のムービーがキャプチャできるボードです。 今回ひょんなことからC#HDMIキャプチャしたデータを使用したいみたいな要望があったのでこちらの商品を試してみました。

プログラムを書いてみる

 ちょっと調べてみるとこれってDirectShowでキャプチャできるみたいなので(それを調べてワザワザ購入した)、OpenCVでVideoCapture使えば余裕そう。というわけでザックリ書いてみました。ButtonとPictureBoxだけをつけたFormを用意した超単純なものです。

VideoCapture m_cap;
Timer m_timer;
Bitmap m_bitmap;
Mat m_imagemat;

public Form1()
{
    InitializeComponent();
    m_cap = new VideoCapture();
    m_timer = new Timer();
    m_cap.Open(CaptureDevice.DShow, 1);
    m_imagemat = new Mat();
}
        
private void button1_Click(object sender, EventArgs e)
{
    if (!m_cap.IsOpened()) return;

    m_timer.Interval = (int)(1000.0 / 60.0);
    m_timer.Tick += m_timer_Tick;
    m_timer.Start();
}

void m_timer_Tick(object sender, EventArgs e)
{
    try
    {
        m_cap.Read(m_imagemat);
        if (m_bitmap == null)
        {
            m_bitmap = m_imagemat.ToBitmap();
        }
        else
        {
            m_imagemat.ToBitmap(m_bitmap);
        }
        this.pictureBox1.Image = m_bitmap;
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.Message);
    }
}

 このプログラム、ちょっとDirectShowのデバイスIDを決めうちで作ってしまってるのが良くないですね。今回はたまたま

m_cap.Open(CaptureDevice.DShow, 1);

で開くことができましたが、ぼくの環境では

m_cap.Open(CaptureDevice.DShow, 0);

ではWebカメラがキャプチャデバイスとして開きました。

 あ、あとFPS

m_timer.Interval = (int)(1000.0 / 60.0);

としてしまってるのも問題かもしれませんね。なぜかm_cap.FPSとしても0が返ってきてしまったので八方塞ですが。

 今回ちょっと苦労した点としては、BitmapやMatをm_timer_Tickのたびにnewしていたのでは どうもガベージコレクションがうまく作動しないっぽく、メモリアロケーションがらみの例外を吐いてしまうという問題があったことです。これについては

m_imagemat = new Mat();
m_bitmap = m_imagemat.ToBitmap();

をしまくらないこと(インスタンスが増えすぎてしまう???)で解決することができました。 解像度の低いWebカメラではループのたびにnewしてても問題なかったのでおそらく1フレームあたりの容量とフレームレートが問題になるのかもしれませんね。

最後に

 今回はOpenCVを手軽にサクッっと使いたかったのでOpenCVSharpというライブラリを使用させていただきました。 これ、ちょっとOpenCV使いたいなぁって時に便利で、しかもNuGetから使用できるのでC#の手軽さと相まって 超絶便利なライブラリだと思ってます。しかもすぐにUIに反映できるExtensionまで準備されているので感謝感謝です。

github.com